选择每第 n 行,不要拉整个表

dot*_*hen 6 mysql

鉴于关于允许在 dba.SE上使用基本 SQL 问题的 Meta 讨论,我提出了一个我现在遇到的问题,对此 Stack Overflow 上的答案是不充分和幼稚的。我希望有比 SO 上提出的问题更好的解决方案(因为我目前在应用程序中面临这个问题),而 dba.SE 似乎是找到更好答案的理想场所。

这是 Stack Overflow 上的原始问题:如何从 mysql 中选择每个第 n 行?

这是公认的答案:

SELECT * 
FROM ( 
    SELECT 
        @row := @row +1 AS rownum, [column name] 
    FROM ( 
        SELECT @row :=0) r, [table name] 
    ) ranked 
WHERE rownum % [n] = 1 
Run Code Online (Sandbox Code Playgroud)

接受的答案的关键问题是它需要将整个表拉到一个临时表中。因此,我已经在这个问题的标题中解决了这个问题。

还要考虑该表可能已删除行,因此仅对WHERE MOD主键进行测试的替代查询也不是一个好的解决方案。Id est,不能相信主键是连续的。

有没有更好的方法来表达查询,该查询将返回每第二、第十或任意第 n 行,它不需要将整个表拉入内存但也考虑已删除的行?

每个第 n 行都可以定义为:

n =  2: Rows 0, 2, 4, 6, 8, ...
n = 10: Rows 0, 10, 20, 30, ...
n = 42: Rows 0, 42, 84, 126, ...
Run Code Online (Sandbox Code Playgroud)

我的目标数据库是 MySQL 5.5,运行在一个常见的 Debian 派生 Linux 发行版上。

编辑:回应托马斯的回答:

建议的解决方案没有产生预期的结果,见下文:

mysql> SELECT 
    ->     @i:=@i+1 AS iterator 
    ->     , t.name
    -> FROM 
    ->     events AS t,
    ->     (SELECT @i:=0) AS dummy
    -> WHERE @i % 10 = 0
    -> ORDER BY name ASC;
+----------+-------+
| iterator | name |
+----------+-------+
|        1 |     0 |
+----------+-------+
1 row in set (0.29 sec)

mysql> select count(*) from events;
+----------+
| count(*) |
+----------+
|   892507 |
+----------+
1 row in set (0.17 sec)
Run Code Online (Sandbox Code Playgroud)

Gor*_*son 5

对于测试数据在 events

id  txtcol
--  ------
 1  event0
 2  event1
 4  event2
 5  event3
 6  event4
 8  event5
 9  event6
Run Code Online (Sandbox Code Playgroud)

按升序检索主键值

SELECT id FROM events ORDER BY id
Run Code Online (Sandbox Code Playgroud)

将其包装在查询中以分配从零开始的排名

set @row:=-1;
SELECT @row:=@row+1 AS rownum, id 
FROM
    (
        SELECT id FROM events ORDER BY id
    ) AS sorted
Run Code Online (Sandbox Code Playgroud)

将其包装在查询中以选择第一行和此后的每三行

set @row:=-1;
SELECT id
FROM
    (
        SELECT @row:=@row+1 AS rownum, id 
        FROM
            (
                SELECT id FROM events ORDER BY id
            ) AS sorted
    ) as ranked
WHERE rownum % 3 = 0
Run Code Online (Sandbox Code Playgroud)

最后,将其包装在查询中以检索其他列

set @row:=-1;
SELECT events.*
FROM
    events
    INNER JOIN
    (
        SELECT id
        FROM
            (
                SELECT @row:=@row+1 AS rownum, id 
                FROM
                    (
                        SELECT id FROM events ORDER BY id
                    ) AS sorted
            ) as ranked
        WHERE rownum % 3 = 0
    ) AS subset
        ON subset.id = events.id
Run Code Online (Sandbox Code Playgroud)

回来

id  txtcol
--  ------
 1  event0
 5  event3
 9  event6
Run Code Online (Sandbox Code Playgroud)