可以有一个与数据库无关的 SQL 查询来获取前 N 行吗?

Posted

技术标签:

【中文标题】可以有一个与数据库无关的 SQL 查询来获取前 N 行吗?【英文标题】:Can there be a database-agnostic SQL query to fetch top N rows? 【发布时间】:2010-10-23 22:05:56 【问题描述】:

我们希望能够使用 SQL 查询选择前 N 行。目标数据库可以是 Oracle 或 mysql。有没有一种优雅的方法? (不用说,我们在这里处理已排序的数据。)

【问题讨论】:

【参考方案1】:

要从该表中获得前 5 名得分手:

CREATE TABLE people
             (id      int, 
              name    string, 
              score   int)

试试这个 SQL:

SELECT id, 
       name, 
       score
FROM   people  p
WHERE  (SELECT COUNT(*) 
        FROM   people p2
        WHERE  p2.score  > p.score 
       ) <=4 

我相信这应该适用于大多数地方。

【讨论】:

【参考方案2】:

没有。语法不同。

但是,您可以创建视图:

/* Oracle */

CREATE VIEW v_table
AS
SELECT  *
FROM    (
        SELECT  *
        FROM    table
        ORDER BY
                column
        )
WHERE   rownum <= n

/* MySQL */

CREATE VIEW v_table
AS
SELECT  *
FROM    table
ORDER BY
        column
LIMIT   n

【讨论】:

受限于强制为您的视图排序(至少在 SQL 服务器上使用 TOP n 子句时)。 其他 DBMS 支持该概念但使用其他符号。 另外,一些 DBMS 不允许在视图中使用 ORDER BY 子句。 感谢您的回答。但是这个问题的主要动机是避免编写单独的查询。查看@kishore 建议的链接以及这些答案,似乎没有这样的方法。 您编写单独的查询一次,只是为了创建一个视图。要查询这些值,您只需发出 SELECT * FROM v_table 这两个数据库都相同。【参考方案3】:

我认为即使只是在 mysql 和 mssql 之间也是不可能的。我做了一个模拟这种行为的选项:

    创建具有自动递增 int 列的视图;说“PagingHelperID” 编写查询如:SELECT columns FROM viewname WHERE PagingHelperID BETWEEN startindex AND stopindex

这将使订购变得困难,您需要为每个要检索数据的订单提供不同的视图。

您也可以在查询时根据数据库动态“重写”您的 sql,并为重写器定义您自己的方法,但我认为没有任何“好的”方法可以做到这一点。

【讨论】:

对。我想避免任何类型的数据库检测。【参考方案4】:

如果表上有唯一键是...

Select * From Table O
Where (Select Count(*) From Table I
       Where [UniqueKeyValue] < O.UniqueKeyValue)  < N

如果您希望“Top”定义基于其他逻辑而不是唯一键...

编辑:如果定义“Top”含义的“排序”基于非唯一列或列集,那么您仍然可以使用它,但您不能保证您将能够准确地取出 N 条记录...

  Select * From Table O
  Where (Select Count(*) From Table I
         Where nonUniqueCol < O.nonUniqueCol) < 10

如果记录 8、9、10、11 和 12 在 [nonUniqueCol] 中都具有相同的值,那么查询将要么只生成 7 条记录,(带有 '

注意:由于这涉及关联的子查询,因此对于非常大的表,性能可能是一个问题...

【讨论】:

如果您的唯一键是 GUID 或其他一些非顺序数据怎么办? 唯一键只允许您识别记录...如果定义“顶部”含义的“排序”是基于其他一些逻辑,那么子查询将被写入“计数”基于该逻辑的记录...唯一的问题是,如果 N 值出现在有多个实例的特定值上......(如果记录 8、9、10 和 11 都具有相同的值,您无法获得前 10 名)【参考方案5】:

从 MySQL 8 开始,您可以使用 ROW_NUMBER() 过滤以统一、符合标准的语法获取 LIMIT (MySQL) 或 FETCH (Oracle) 的语义:

SELECT t.a, t.b, t.c, t.o
FROM (
  SELECT a, b, c, o, ROW_NUMBER() OVER (ORDER BY o)
  FROM x
) t
WHERE rn <= :limit
ORDER BY o

但这可能比使用供应商特定的语法效率低,所以如果你有一些抽象LIMITFETCH 的方法(例如使用像jOOQ 或Hibernate 这样的ORM,甚至一些模板语言),应该是首选。

【讨论】:

【参考方案6】:

仔细研究后,最大的问题是 MySQL 不符合 ISO SQL:2003 标准。如果是这样,您将拥有这些方便的窗口函数:

SELECT * from
(   SELECT
    RANK() OVER (ORDER BY <blah>) AS ranking,
    <rest of columns here>,
    FROM <table>
)
WHERE ranking <= <N>

唉,MySQL(和其他模仿它的行为,例如 SQLite)不这样做,因此整个限制问题。

查看来自***的这个 sn-p (http://en.wikipedia.org/wiki/Window_function_(SQL)#Limiting_result_rows)

【讨论】:

以上是关于可以有一个与数据库无关的 SQL 查询来获取前 N 行吗?的主要内容,如果未能解决你的问题,请参考以下文章

在哪里放置与 Eloquent 无关的 SQL 查询

SQL查询获取同一字段前/后n行的值_lag/lead

Jooq 与 SQL Server 如何选择前 N 个结果

SQL中怎么设置字段为10位数字。

SQL查询以确定是不是有任何与曲目无关的艺术家[关闭]

SQL - 使用第三个查询来比较前两个的结果