Насколько универсален оператор LIMIT в SQL? - PullRequest
15 голосов
/ 07 октября 2009

Я нахожусь в процессе обобщения приложения репликации БД Django, и оно использует оператор:

SELECT %s FROM %s LIMIT 1

для извлечения 1 строки и использования DBAPI Python для описания полей, он отлично работает с ORACLE и MySQL, но как кроссплатформенный оператор LIMIT?

Ответы [ 7 ]

23 голосов
/ 05 июня 2014

LIMIT стал довольно популярным среди различных баз данных с открытым исходным кодом, но, к сожалению, дело в том, что разбиение на страницы OFFSET было одной из наименее стандартизированных функций SQL из всех, поскольку оно было стандартизировано еще в SQL: 2008

.

До этого на странице руководства пользователя jOOQ в предложении LIMIT показано, как различные эквивалентные операторы могут быть сформированы на каждом диалекте 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

Прочтите о ROW_NUMBER() против DENSE_RANK() обоснование здесь

Выбери свой яд; -)

12 голосов
/ 07 октября 2009

LIMIT очень далек от универсальности - из основных СУБД он в значительной степени ограничен MySQL и PostgreSQL. Здесь - подробный анализ того, как это делается во многих других реализациях, включая MSSQL, Oracle и DB2, а также в ANSI SQL.

10 голосов
/ 07 октября 2009

http://en.wikipedia.org/wiki/Select_(SQL)#Limiting_result_rows перечисляет все основные варианты команды выбора.

Я считаю, что лучший способ сделать это - использовать команду SET ROWCOUNT перед оператором SELECT.

Итак, для вас:

SET ROWCOUNT 1
SELECT %s FROM %s
7 голосов
/ 07 октября 2009

Это совсем не универсально. На самом деле я удивлен, что это работает для вас в Oracle; это раньше не присутствовало. Обычно пользователи Oracle выбирают ROWNUM.

Каждая база данных имеет собственный синтаксис для ограничения результатов по номеру строки. Есть также два метода, которые являются стандартом ANSI SQL:

  1. FETCH FIRST. Происходит из DB / 2 и сделан стандартным только в SQL: 2008, поэтому поддержка СУБД очень мала. Невозможно использовать смещение.

  2. Функция управления окнами SELECT ..., ROW_NUMBER() OVER (ORDER BY some_ordering) AS rn WHERE rn BETWEEN n AND m ... ORDER BY some_ordering. Это из SQL: 2003 и имеет некоторую (неоднозначную, иногда медленную) поддержку в более новых СУБД. Он может использовать смещение или любую другую функцию сравнения для номера строки, но его недостаток в том, что он ужасно уродлив.

Вот хороший обзор трудоемкости, с которой вам придется столкнуться, если вы хотите поддержку разбиения на страницы между СУБД.

2 голосов
/ 07 октября 2009

LIMIT не является частью стандарта ANSI SQL с стандарта 1992 года; У меня нет копии более позднего стандарта. В лучшие времена соответствие поставщиков стандарту довольно расплывчато. Что бы ни стоило, «LIMIT» указан как зарезервированное слово (то есть юридически его нельзя использовать в качестве идентификатора даже в тех случаях, когда это не ключевое слово в реализации).

2 голосов
/ 07 октября 2009

Он не работает на MSSQL (который использует SELECT TOP 10 * FROM Blah). Это отрезает значительную часть рынка БД. Я не уверен в других.

Кроме того, возможно, хотя и очень маловероятно, что ваш API БД переведет его для вас.

1 голос
/ 04 мая 2018

Поскольку в одном из ответов было упомянуто, что LIMIT и OFFSET более или менее ограничены MySQL и PostgreSQL, я подумал указать, что SAP HANA также поддерживает предложения LIMIT и OFFSET. Но OFFSET без LIMIT не разрешен в базе данных SAP HANA.

...