Почему мой оператор SQL с OFFSET / FETCH не работает? - PullRequest
0 голосов
/ 04 мая 2018

Я использую SQL Server, и в таблице Purchase у меня около 50 записей. Я пытаюсь получить первые 20 (0-20), затем 20-40 и, наконец, последние 10.

У меня есть команда SQL:

SELECT * FROM Purchase WHERE dataOK = 'OK' ORDER BY PurchaseDate DESC OFFSET 0 ROWS FETCH NEXT 20 ROWS ONLY

SELECT * FROM Purchase WHERE dataOK = 'OK' ORDER BY PurchaseDate DESC OFFSET 19 ROWS FETCH NEXT 20 ROWS ONLY

SELECT * FROM Purchase WHERE dataOK = 'OK' ORDER BY PurchaseDate DESC OFFSET 39 ROWS FETCH NEXT 20 ROWS ONLY

Я не знаю почему, но первые две команды возвращают одинаковые записи. Почему он не показывает два разных набора результатов?

EDIT: Хорошо, я понимаю, что XX в FETCH XX - это количество строк, которые должны быть возвращены. Но мой SQL-сервер возвращает точно такой же результат для указанных выше первых двух команд.

Ответы [ 4 ]

0 голосов
/ 04 мая 2018

@ Папараццо понял это, но, возможно, я могу немного расширить их ответ.

Предложение ORDER BY PurchaseDate не будет детерминированным , если любые две записи в вашей таблице имеют одинаковый PurchaseDate, потому что порядок будет по-прежнему выполняться независимо от того, какая из этих двух записей SQL Server выберет вернись первым. В вашем случае, поскольку все ваших записей имеют одинаковые PurchaseDate, SQL Server может вернуть эти записи в в любом порядке , и предложение будет выполнено. Другими словами, ваше предложение ORDER BY не имеет смысла, за исключением того, что оно требуется для использования OFFSET...FETCH.

Поскольку вы не можете точно знать, какой порядок будет производиться вашим предложением ORDER BY, вы также не можете знать, куда конкретный OFFSET поместит вас в этот порядок, что означает, что вы не можете знать, какие записи будут возвращены любой из трех ваших запросов. То, что SQL Server возвращает один и тот же набор результатов для каждого из ваших первых двух запросов, является допустимым поведением, но даже это не гарантируется каждый раз; Вы могли бы также легко получить другие результаты.

Если вам нужно гарантировать, что такие запросы будут возвращать непересекающиеся наборы, то вам нужно написать свое предложение ORDER BY так, чтобы никакие две записи не имели одинаковую комбинацию значений для полей, по которым вы упорядочиваете. В вашем случае вы говорите, что у вас есть уникальный идентификатор для каждой строки, поэтому вы можете заказать сначала по PurchaseDate, а затем по этому идентификатору, и ваши запросы будут работать для вас. Вот пример:

-- Create sample data: 50 records with unique RowNumber values and the same PurchaseDate value.
declare @Purchase table (RowNumber int, PurchaseDate date);
with Numbers as (select top 50 RowNumber = row_number() over (order by (select null)) from sys.objects)
insert @Purchase
select
    RowNumber,
    PurchaseDate = /*dateadd(day, RowNumber,*/ convert(date, getdate())/*)*/
from
    Numbers;

-- These result sets are nondeterministic because all the PurchaseDate values are the same, which
-- means that the records may be returned in any order and the ORDER BY will be satisfied.
select * from @Purchase order by PurchaseDate offset 0 rows fetch next 20 rows only;
select * from @Purchase order by PurchaseDate offset 20 rows fetch next 20 rows only;
select * from @Purchase order by PurchaseDate offset 40 rows fetch next 20 rows only;

-- Adding the unique RowNumber to my ORDER BY clause makes the results deterministic and therefore
-- guarantees that the following result sets will be rows 1-20, 21-40, and 41-50, respectively.
select * from @Purchase order by PurchaseDate, RowNumber offset 0 rows fetch next 20 rows only;
select * from @Purchase order by PurchaseDate, RowNumber offset 20 rows fetch next 20 rows only;
select * from @Purchase order by PurchaseDate, RowNumber offset 40 rows fetch next 20 rows only;

Обратите внимание, что если бы я раскомментировал закомментированный раздел строки, который присваивает значения PurchaseDate, которые дали бы каждой строке уникальный PurchaseDate, то мне не нужно было бы включать RowNumber в мой ORDER BY, чтобы получить непересекающиеся множества, потому что одного PurchaseDate было бы достаточно, чтобы гарантировать уникальное упорядочение.

0 голосов
/ 04 мая 2018

я бы переписал как

SELECT * 
FROM Purchase 
WHERE dataOK = 'OK' 
ORDER BY PurchaseDate DESC OFFSET 0 ROWS FETCH NEXT 20 ROWS ONLY
GO
SELECT * 
FROM Purchase 
WHERE dataOK = 'OK' 
ORDER BY PurchaseDate DESC OFFSET 19 ROWS FETCH NEXT 21 ROWS ONLY
SELECT * 
FROM Purchase 
WHERE dataOK = 'OK' 
ORDER BY PurchaseDate DESC OFFSET 40 ROWS FETCH NEXT 10 ROWS ONLY
0 голосов
/ 04 мая 2018

Если PurchaseDate не является уникальным, то заказ в этой группе не гарантированно повторяется.
Добавьте идентификатор в сортировку, чтобы получить повторяемую сортировку.

SELECT * FROM Purchase WHERE dataOK = 'OK' 
ORDER BY PurchaseDate DESC, ID 
OFFSET  0 ROWS FETCH NEXT 20 ROWS ONLY

SELECT * FROM Purchase WHERE dataOK = 'OK' 
ORDER BY PurchaseDate DESC, ID 
OFFSET 19 ROWS FETCH NEXT 20 ROWS ONLY

SELECT * FROM Purchase WHERE dataOK = 'OK' 
ORDER BY PurchaseDate DESC, ID 
OFFSET 39 ROWS FETCH NEXT 20 ROWS ONLY
0 голосов
/ 04 мая 2018

Количество возвращаемых строк определяется частью команды FETCH. Это количество строк . Это не позиция смещения.

Итак, если вам нужно 20 строк, то у вас всегда будет FETCH NEXT 20 ROWS ONLY.

Первые два не должны возвращать одинаковые результаты. , , хотя некоторые результаты могут выглядеть одинаково, если вы посмотрите только на PurchaseDate.

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