【问题标题】:How universal is the LIMIT statement in SQL?SQL 中的 LIMIT 语句有多普遍?
【发布时间】:2009-10-06 23:15:44
【问题描述】:

我正在推广一个 Django DB 复制应用程序,它使用以下语句:

SELECT %s FROM %s LIMIT 1

获取 1 行并使用 Python DBAPI 来描述字段,它适用于 ORACLE 和 MySQL,但是 LIMIT 语句的跨平台程度如何?

【问题讨论】:

  • 在哪个版本的 Oracle 中可以使用?
  • ORACLE 9i @ AlphaServer,我的错,刚试了一下,不行:(

标签: sql database rdbms


【解决方案1】:

LIMIT 已经在各种开源数据库中变得非常流行,但不幸的是,OFFSET 分页一直是它们中标准化程度最低的 SQL 功能,直到 @987654321 才被标准化@。

在此之前,jOOQ user manual page on the LIMIT clause 展示了如何在每种 SQL 方言中形成各种等效语句:

-- MySQL, H2, HSQLDB, Postgres, and SQLite
SELECT * FROM BOOK LIMIT 1 OFFSET 2

-- CUBRID supports a MySQL variant of the LIMIT .. OFFSET clause
SELECT * FROM BOOK LIMIT 2, 1

-- Derby, SQL Server 2012, Oracle 12c, SQL:2008 standard
-- Some need a mandatory ORDER BY clause prior to OFFSET
SELECT * FROM BOOK [ ORDER BY ... ] OFFSET 2 ROWS FETCH NEXT 1 ROWS ONLY

-- Ingres
SELECT * FROM BOOK OFFSET 2 FETCH FIRST 1 ROWS ONLY

-- Firebird
SELECT * FROM BOOK ROWS 2 TO 3

-- Sybase SQL Anywhere
SELECT TOP 1 ROWS START AT 3 * FROM BOOK

-- DB2 (without OFFSET)
SELECT * FROM BOOK FETCH FIRST 1 ROWS ONLY

-- Sybase ASE, SQL Server 2008 (without OFFSET)
SELECT TOP 1 * FROM BOOK

现在,这些都非常简单,对吧?当您必须模仿它们时,令人讨厌的部分来了:

-- DB2 (with OFFSET), SQL Server 2008 (with OFFSET), 
SELECT * FROM (
  SELECT BOOK.*, 
    ROW_NUMBER() OVER (ORDER BY ID ASC) AS RN
  FROM BOOK
) AS X
WHERE RN > 2
AND RN <= 3

-- DB2 (with OFFSET), SQL Server 2008 (with OFFSET)
-- When the original query uses DISTINCT!
SELECT * FROM (
  SELECT DISTINCT BOOK.ID, BOOK.TITLE 
    DENSE_RANK() OVER (ORDER BY ID ASC, TITLE ASC) AS RN
  FROM BOOK
) AS X
WHERE RN > 2
AND RN <= 3

-- Oracle 11g and less
SELECT * 
FROM (
  SELECT b.*, ROWNUM RN 
  FROM (
    SELECT *
    FROM BOOK
    ORDER BY ID ASC
  ) b
  WHERE ROWNUM <= 3
) 
WHERE RN > 2

Read about the ROW_NUMBER() vs. DENSE_RANK() rationale here

选择你的毒药;-)

【讨论】:

  • 在 SQL Server 中,使用 OFFSET 和 FETCH NEXT 需要使用 ORDER BY 子句。
  • @BoltBait:你说得对,我一直忘记这一点。谢谢!
  • 所以这些语句都跳过两个元素并返回一个,对吧?标题为“无偏移”的陈述除外。
  • 我又看了一遍,在我看来,下块跳过了 1 并返回了 2。如果不是这种情况,我会喜欢一个解释。例如最后一个例子应该在中间选择返回三个元素,然后在外部选择返回最后两个,对吧?
  • @JensSchauder:你是对的,敏锐的眼光。将立即修复。
【解决方案2】:

LIMIT 远非通用——在主要的 RDBMS 之外,它几乎仅限于 MySQL 和 PostgreSQL。 Here 详细分析了这在许多其他实现中是如何完成的,包括 MSSQL、Oracle 和 DB2,以及在 ANSI SQL 中。

【讨论】:

    【解决方案3】:

    http://en.wikipedia.org/wiki/Select_(SQL)#Limiting_result_rows 列出了 select 命令的所有主要变体。

    我认为最好的方法是在 SELECT 语句之前使用 SET ROWCOUNT 命令。

    所以,对你来说:

    SET ROWCOUNT 1
    SELECT %s FROM %s
    

    【讨论】:

      【解决方案4】:

      它根本不是通用的。实际上,我很惊讶它在 Oracle 中为您工作;它以前不存在。通常 Oracle 用户会选择ROWNUM

      每个数据库都有自己的语法来按行号限制结果。还有两种方法是ANSI标准的SQL:

      1. FETCH FIRST。源自 DB/2,仅在 SQL:2008 中成为标准,因此对 DBMS 的支持很少。不能使用偏移量。

      2. 窗口函数SELECT ..., ROW_NUMBER() OVER (ORDER BY some_ordering) AS rn WHERE rn BETWEEN n AND m ... ORDER BY some_ordering。这来自 SQL:2003,在较新的 DBMS 中有一些(不完整,有时很慢)支持。它可以在行号上使用偏移量或任何其他比较函数,但缺点是丑得可怕。

      Here's a good overview 如果您想要跨 DBMS 分页支持,您将不得不处理的乏味。

      【讨论】:

      • +1 是一个非常好的跨平台表达 LIMIT 方式的链接。
      【解决方案5】:

      LIMIT 不是 1992 年标准的 ANSI SQL 标准的一部分;我手头没有任何后期标准的副本。在最好的情况下,供应商对标准的遵守情况非常模糊。值得一提的是,“LIMIT”被列为保留字(这意味着即使在实现中它不是关键字的情况下,它也不能合法地用作标识符)。

      【讨论】:

        【解决方案6】:

        它不适用于 MSSQL(它使用 SELECT TOP 10 * FROM Blah 代替)。这削减了数据库市场的很大一部分。我不确定其他人。

        此外,您的 DB API 可能会为您翻译它,尽管可能性很小。

        【讨论】:

          【解决方案7】:

          由于在其中一个答案中提到 LIMIT 和 OFFSET 或多或少仅限于 MySQL 和 PostgreSQL,我想指出 SAP HANA 也支持 LIMIT 和 OFFSET 子句。但在 SAP HANA 数据库中不允许使用 OFFSET without LIMIT。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2019-01-08
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2023-04-08
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多