结果集中行自动编号的纯 SQL 技术

Posted

技术标签:

【中文标题】结果集中行自动编号的纯 SQL 技术【英文标题】:Pure-SQL Technique for Auto-Numbering Rows in Result Set 【发布时间】:2008-10-14 18:20:51 【问题描述】:

我正在寻找一种对结果集(不是表格)中的行进行顺序编号的方法。本质上,我从如下查询开始:

SELECT id, name FROM people WHERE name = 'Spiewak'

ids 显然不是一个真实的序列(例如1, 2, 3, 4)。我需要的是结果集中包含这些自动编号的另一列。如果必须,我愿意使用 SQL 函数,但我宁愿在不使用 ANSI 规范的扩展的情况下这样做。

平台是 mysql,但如果可能,该技术应该是跨平台的(因此希望避免非标准扩展)。

【问题讨论】:

【参考方案1】:

要获得有意义的行号,您需要对结果进行排序。然后你可以这样做:

SELECT id, name
    , (SELECT COUNT(*) FROM people p2 WHERE name='Spiewak' AND p2.id <= p1.id) AS RowNumber
FROM people p1
WHERE name = 'Spiewak'
ORDER BY id

注意,子查询的 WHERE 子句需要匹配 WHERE 子句或主查询的主键主查询的 ORDER BY。

SQL Server 有 ROW_NUMBER() OVER 结构来简化这一点,但我不知道 MySQL 是否有什么特殊的解决方法。


由于我在这里的帖子被接受为答案,我还想指出 Dan Goldstein 的回复,这在方法上非常相似,但使用 JOIN 而不是子查询,并且通常会表现更好

【讨论】:

这很聪明,但性能一定很糟糕。并不是说有更好的答案,只是对它的 O(n^2) 感到不寒而栗。 哇!这很聪明。我什至不知道您可以使用子查询以这种方式定义 res 字段。 同意性能可能会更好,但如果你仅限于 ansi,那就是你必须做的。有时人们会预先计算这些结果以加快速度。 哦,如果您的 id 列(在这种情况下)排列在聚集索引上,它可能不会那么不好。至少,SQL Server 2000 足够智能,可以缓存和构建以前的结果。 这个解决方案是 ANSI 并且可以执行 - 但是 - 这是一个潜在的维护噩梦问题,订购必须严格并且可能与标准更改不同步。【参考方案2】:

AFAIK,没有“标准”方式。

MS SQL Server 有 row_number(),而 MySQL 没有。

在 MySQL 中最简单的方法是

SELECT a.*, @num := @num + 1 b from test a, (SELECT @num := 0) d;

来源:http://www.xaprb.com/blog/2006/12/02/how-to-number-rows-in-mysql/中的cmets

【讨论】:

【参考方案3】:

一个非常低效但 ANSI SQL 的想法是计算具有较小 id 匹配相同条件的行数。我还没有测试过这个 SQL,它可能不会工作,但类似:

SELECT id, name, sub.lcount
FROM people outer
JOIN (SELECT id, COUNT(id) lcount FROM people WHERE name = 'Spiewak' AND id < outer.id GROUP BY id) sub on outer.id = sub.id
WHERE name = 'Spiewak'

【讨论】:

和我发的基本一样,但是在大多数情况下JOIN可能会比子查询快。 哦,您仍然需要在外部查询中明确排序,否则返回的结果可能与查询的排序不同。【参考方案4】:

据我所知,没有 ANSI 标准的方法可以做到这一点。

在 SQL Server 中有一个可以使用的 ROW_NUMBER() 函数,而在 Oracle 中,有一个 ROWNUM 伪列。

在 MySQL 中,有this technique

【讨论】:

为此,他可能更喜欢 ROW_NUMBER() 而不是 RANK(),但由于他使用的是 MySQL/Ansi,所以这是一个有争议的问题。【参考方案5】:
SELECT @i:=@i+1 AS iterator, t.*
FROM tablename t,(SELECT @i:=0) foo

【讨论】:

【参考方案6】:

在 oracle 中,我知道您想要做的唯一数据库是对数据进行子选择

select rownum, id , blah, blah
from  (
select id, name FROM people WHERE name = 'Spiewak'
)

基本概念是 rownum 将根据内部选择返回的结果集进行评估。

我希望这可以为您指明一个可以使用的解决方案。

【讨论】:

【参考方案7】:

这个页面应该给你一个标准的 SQL 方法:

https://www.sqlteam.com/articles/returning-a-row-number-in-a-query

希望这会有所帮助。

【讨论】:

【参考方案8】:

我知道这是一个旧线程,但我刚刚在寻找这个答案。我在 MySQL 中尝试了 Dan Goldstein 的查询,但它并没有像写的那样工作,因为 'outer' 是一个保留字。然后,我注意到它仍然在使用子查询。

所以,我想出了一个使用 JOIN 的版本,但没有子查询:

  SELECT SUM(IF(p1.id > p2.id, 0, 1)) AS `row`, p2.id, p2.name
  FROM people p1 JOIN people p2 ON p1.name = p2.name
  WHERE p1.name = 'Spiewak'
  GROUP BY p2.id

这在 MySQL 5.1 中对我有用。对于 MySQL,GROUP BY p2.id 似乎就足够了。可以将明确的 ORDER BY p2.id 添加到查询的末尾,但无论哪种方式,我都得到了相同的结果。

【讨论】:

【参考方案9】:

最近对问题的回答,但我相信窗口函数现在是 ANSI SQL 的标准,或者至少是大多数当前 SQL 风格的标准。因此,请使用 ROW_NUMBER() 函数。

SELECT
    p.ROW_NUMBER() over (order by id) as 'row_id',
    p.id as 'id',
    p.name as 'name'
FROM
    people p
WHERE
    p.name = 'Spiewak'

【讨论】:

确实,窗口函数是在 2003 年引入 ANSI SQL 的。MySQL 从 v8.0(2018 年发布)开始支持窗口函数。 SQLite 自 v3.25.0 起(也来自 2018 年)。 “从 2009 年到 2017 年的近八年时间里,PostgreSQL 是唯一支持 SQL 窗口函数的主要免费开源产品。仅仅一年后,到 2018 年 9 月,所有开源竞争对手都赶上了……甚至有些超越。 " -- 来源:modern-sql.com/blog/2019-02/postgresql-11

以上是关于结果集中行自动编号的纯 SQL 技术的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server游标

JDBC的结果集

SQLServer之创建Transact-SQL游标

MS SQL语句declare游标的求助

SqlServerSqlServer编程语言T-SQL的游标使用

合并查询结果集UNION(去重), UNION ALL(不去重),INTERSECT(交集),MINUS(差集,第一个结果集减去第二个结果集,第一个结果集中不在第二个结果集中的记录行)