我有一个经典的分页系统LIMIT startrecord, endrecord
,我想弄清楚 X 记录位于哪个页码中。
我现在唯一的想法是递归地寻找所有记录以找出它。但我正在寻找一种更“经济”的方法!
有任何想法吗 ?
我有一个经典的分页系统LIMIT startrecord, endrecord
,我想弄清楚 X 记录位于哪个页码中。
我现在唯一的想法是递归地寻找所有记录以找出它。但我正在寻找一种更“经济”的方法!
有任何想法吗 ?
您可以使用子查询创建一个包含结果及其位置的表,然后查询您正在查看的特定条目:
SET @rank=0;
SELECT rank, record
FROM (
SELECT
@rank:=@rank+1 AS rank,
record
FROM table
) as subquery
WHERE record = x;
返回的表将显示记录及其在原始查询中出现的排名。您可以将排名除以每页的结果数......或者将其构建到查询中。希望这可以帮助。
计算出您要查找的记录之前的记录数。这要求您为您的查询假设一个自然的顺序。
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_field
MySQL 将使用主键的值相同的记录,在我们的例子中是id
. 这就是为什么我们OR
在我们的条件下发挥作用。在我的回答中,我假设您正在对原始查询(其中包含 limit 语句)进行升序排序,但如果您是降序排序,那么您需要将所有更改<
为>
.
在您的查询中使用类似的内容作为子选择的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 (或省略)保持相同。
如果这是您经常使用的东西,我认为创建存储过程或函数是个好主意。我们可以在内部使用光标,遍历结果并获取所需项目的位置。我认为这会更快,它不必迭代所有记录,并且不需要子查询(对于所有这些,我会说它更经济),您可以使用 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
)
你不能真正创造一种“经济”的方式。您必须从数据库中获取完整的记录列表,因为无法从 MySQL 中知道记录的位置。
根据您的排序、数据更改的频率,您可以为记录分配其在列中的位置:将列位置添加到您正在查询的表中。这可能并非在所有情况下都可行。