Эмулируйте предложение MySQL LIMIT в Microsoft SQL Server 2000 - PullRequest
39 голосов
/ 19 октября 2008

Когда я работал над компонентом базы данных Zend Framework , мы пытались абстрагировать функциональность предложения LIMIT, поддерживаемого MySQL, PostgreSQL и SQLite. То есть создание запроса может быть сделано следующим образом:

$select = $db->select();
$select->from('mytable');
$select->order('somecolumn');
$select->limit(10, 20);

Когда база данных поддерживает LIMIT, это создает запрос SQL, подобный следующему:

SELECT * FROM mytable ORDER BY somecolumn LIMIT 10, 20

Это было более сложно для брендов баз данных, которые не поддерживают LIMIT (этот пункт, кстати, не является частью стандартного языка SQL). Если вы можете генерировать номера строк, сделайте весь запрос производной таблицей, а во внешнем запросе используйте BETWEEN. Это было решением для Oracle и IBM DB2. Microsoft SQL Server 2005 имеет аналогичную функцию номера строки, поэтому можно написать запрос следующим образом:

SELECT z2.*
FROM (
    SELECT ROW_NUMBER OVER(ORDER BY id) AS zend_db_rownum, z1.*
    FROM ( ...original SQL query... ) z1
) z2
WHERE z2.zend_db_rownum BETWEEN @offset+1 AND @offset+@count;

Однако Microsoft SQL Server 2000 не имеет функции ROW_NUMBER().

Итак, мой вопрос: вы можете придумать способ эмулировать функциональность LIMIT в Microsoft SQL Server 2000, используя только SQL? Без использования курсоров или T-SQL или хранимой процедуры. Он должен поддерживать оба аргумента для LIMIT, как количество, так и смещение. Решения с использованием временной таблицы также неприемлемы.

Изменить:

Наиболее распространенное решение для MS SQL Server 2000 похоже на приведенное ниже, например, для получения строк с 50 по 75:

SELECT TOP 25 *
FROM ( 
  SELECT TOP 75 *
  FROM   table 
  ORDER BY BY field ASC
) a 
ORDER BY field DESC;

Однако это не работает, если общий набор результатов, скажем, 60 строк. Внутренний запрос возвращает 60 строк, потому что он находится в верхних 75. Затем внешний запрос возвращает строки 35-60, что не помещается в желаемую "страницу" из 50-75. По сути, это решение работает, если вам не нужна последняя «страница» набора результатов, которая не кратна размеру страницы.

Edit:

Другое решение работает лучше, но только если вы можете предположить, что результирующий набор содержит уникальный столбец:

SELECT TOP n *
FROM tablename
WHERE key NOT IN (
    SELECT TOP x key
    FROM tablename
    ORDER BY key
);

Вывод:

Похоже, что нет универсального решения для эмуляции LIMIT в MS SQL Server 2000. Хорошее решение существует, если вы можете использовать функцию ROW_NUMBER() в MS SQL Server 2005.

Ответы [ 4 ]

5 голосов
/ 23 июня 2009

Вот еще одно решение, которое работает только в Sql Server 2005 и новее, поскольку оно использует оператор "исключение". Но я делюсь этим в любом случае. Если вы хотите получить записи 50 - 75, напишите:

select * from (
    SELECT top 75 COL1, COL2
    FROM MYTABLE order by COL3
) as foo
except
select * from (
    SELECT top 50 COL1, COL2
    FROM MYTABLE order by COL3
) as bar
5 голосов
/ 06 апреля 2009
SELECT TOP n *
FROM tablename
WHERE key NOT IN (
    SELECT TOP x key
    FROM tablename
    ORDER BY key
    DESC
);
4 голосов
/ 13 мая 2009

Когда вам нужен только LIMIT, ms sql имеет эквивалентное ключевое слово TOP, так что все понятно. Когда вам нужно LIMIT с OFFSET, вы можете попробовать некоторые хаки, как описано выше, но все они добавляют некоторые накладные расходы, то есть для заказа в одну сторону, а затем в другую, или дорогую операцию NOT IN. Я думаю, что все эти каскады не нужны. Самым чистым решением, на мой взгляд, было бы просто использовать TOP без смещения на стороне SQL, а затем искать требуемую начальную запись с помощью соответствующего метода клиента, такого как mssql_data_seek в php. Хотя это решение не является чисто SQL, я думаю, что оно лучшее, потому что оно не добавляет никаких накладных расходов (пропущенные записи не будут передаваться по сети при поиске мимо них, если вас это беспокоит) ).

0 голосов
/ 22 июня 2011

Я бы попробовал реализовать это в моем ORM, поскольку там все довольно просто. Если он действительно должен быть в SQL Server, то я бы посмотрел код, сгенерированный linq to sql для следующего оператора linq to sql, и пошел бы оттуда. Инженер MSFT, который реализовывал этот код, был частью команды SQL на протяжении многих лет и знал, что он делает.

var result = myDataContext.mytable.Skip (pageIndex * pageSize) .Take (pageSize)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...