8

我有一个经典的分页系统LIMIT startrecord, endrecord,我想弄清楚 X 记录位于哪个页码中。

我现在唯一的想法是递归地寻找所有记录以找出它。但我正在寻找一种更“经济”的方法!

有任何想法吗 ?

4

5 回答 5

5

您可以使用子查询创建一个包含结果及其位置的表,然后查询您正在查看的特定条目:

SET @rank=0; 
SELECT rank, record 
FROM (
    SELECT 
        @rank:=@rank+1 AS rank, 
        record 
    FROM table
) as subquery 
WHERE record = x;

返回的表将显示记录及其在原始查询中出现的排名。您可以将排名除以每页的结果数......或者将其构建到查询中。希望这可以帮助。

于 2013-10-29T11:10:08.123 回答
4

计算出您要查找的记录之前的记录数。这要求您为您的查询假设一个自然的顺序。

SELECT COUNT(id) AS c
FROM tbl
WHERE sort_field < ((SELECT sort_field FROM tbl WHERE id = 18))
OR (sort_field = ((SELECT sort_field FROM tbl WHERE id = 18)) AND id < 18);

然后只需检索c并计算ceilling(c/page_size)。这将为您提供记录所在的页码。唯一要记住的重要事项是,您需要按照与限制查询相同的顺序对记录进行排序。

为了描述查询的作用,它计算 id 为 18 的记录之前的记录数。唯一棘手的部分是记录与sort_fieldMySQL 将使用主键的值相同的记录,在我们的例子中是id. 这就是为什么我们OR在我们的条件下发挥作用。在我的回答中,我假设您正在对原始查询(其中包含 limit 语句)进行升序排序,但如果您是降序排序,那么您需要将所有更改<>.

于 2014-01-19T00:55:27.043 回答
3

在您的查询中使用类似的内容作为子选择的s一部分

SELECT s.row, s.RECORD, YOUR_OTHER_FIELDS...
FROM (SELECT @row := 0) cnt
  JOIN (SELECT @row := @row + 1 row, RECORD, ...YOUR QUERY WITH ORDER BY ...) s
WHERE s.RECORD = <desired record number>

并将行除以分页中的页面大小。具体但荒谬的例子:

SELECT p.row, p.id
FROM (SELECT @row := 0) cnt
  JOIN (SELECT @row := @row + 1 row, id FROM products ORDER BY id desc) p
WHERE p.id = 485166

正如预期的那样,值row随您在子选择中使用的顺序而变化。

它将变量初始化折叠到查询中,因此这只是一个语句。它也不依赖于行的自然顺序或分布 - 只要它们返回的顺序对于您指定的任何 ORDER (或省略)保持相同。

于 2014-01-22T12:31:48.983 回答
0

如果这是您经常使用的东西,我认为创建存储过程或函数是个好主意。我们可以在内部使用光标,遍历结果并获取所需项目的位置。我认为这会更快,它不必迭代所有记录,并且不需要子查询(对于所有这些,我会说它更经济),您可以使用 order、join 和任何您需要的东西。

DELIMITER $$


CREATE FUNCTION position ( looking_for INT ) 
 RETURNS INT
READS SQL DATA
BEGIN
  -- First we declare all the variables we will need
  DECLARE id   INT;
  DECLARE pos   INT;
  SET pos=0;
  -- flag which will be set to true, when cursor reaches end of table
  DECLARE exit_loop BOOLEAN;         

  -- Declare the sql for the cursor
  DECLARE pos_cursor CURSOR FOR
    SELECT id  
    FROM your_table
    --you can use where, join, group by, order and whatever you need 
 --end of query

  -- Let mysql set exit_loop to true, if there are no more rows to iterate
  DECLARE CONTINUE HANDLER FOR NOT FOUND SET exit_loop = TRUE;

  -- open the cursor
  OPEN example_cursor;

  -- marks the beginning of the loop
  example_loop: LOOP

    -- read the id from next row into the variable id
    FETCH  pos_cursor INTO  id;
    -- increment the pos var
    SET pos=pos+1;

    -- check if we found the desired item, 
    -- if it has been set we close the cursor and exit 
    -- the loop
    IF id=looking_for THEN
        CLOSE example_cursor;
        LEAVE example_loop;
    END IF;


    -- check if the exit_loop flag has been set by mysql, 
    -- if it has been set we close the cursor and exit 
    -- the loop
    IF exit_loop THEN
        CLOSE example_cursor;
        LEAVE example_loop;
    END IF;

  END LOOP example_loop;
 RETURN pos;
END $$

DELIMITER ;

您只需创建一次函数,使用它,您只需要使用以下 sql:

CALL position(ID_OF_THE_ITEM_YOU_ARE_LOOKING_FOR);

它返回项目的位置,在返回的行集的位置 [0][0] 中。

当然,您可以创建一个比较名称或任何其他字段甚至多个字段的函数,而不是 id。

如果查询总是不同的,那么你不能使用函数,但你仍然可以使用游标(语法相同)。您可以在 PHP 中构建游标,让其pos成为系统变量(使用 @pos),并且在任何情况下只需添加查询的特定 sql(和之间的 DECLARE pos_cursor CURSOR FOR部分--end of query

于 2014-01-24T01:16:42.633 回答
-1

你不能真正创造一种“经济”的方式。您必须从数据库中获取完整的记录列表,因为无法从 MySQL 中知道记录的位置。

根据您的排序、数据更改的频率,您可以为记录分配其在列中的位置:将列位置添加到您正在查询的表中。这可能并非在所有情况下都可行。

于 2014-01-21T18:32:28.877 回答