Select top с использованием SQL Server возвращает другой вывод, чем select * - PullRequest
0 голосов
/ 03 октября 2019

Я пытался получить select top n данные из базы данных, основанные на алфавитном и числовом формате. Вывод должен быть отсортирован по алфавиту в первую очередь, а затем по номеру.

Когда я пытаюсь получить все данные (select *), я получаю правильный вывод:

select nocust, share 
from TB_STOCK
where share = ’BBCA’ 
  and concat(share, nocust) < ‘ZZZZZZZZ’
order by 
    case when nocust like ‘[a-z]%’ then 0 else 1 end


nocust | share
-------+--------
a522   | BBCA
b454   | BBCA
k007   | BBCA
p430   | BBCA
q797   | BBCA
s441   | BBCA
s892   | BBCA
u648   | BBCA
v107   | BBCA
4211   | BBCA
6469   | BBCA
6751   | BBCA

Но когда я пытаюсь select top n (например: топ 5), яполучить результат, отличный от ожидаемого (не как select * from table):

select top 5 nocust, share 
from TB_STOCK
where share = ’BBCA’ 
  and concat(share, nocust) < ‘ZZZZZZZZ’
order by 
    case when nocust like ‘[a-z]%’ then 0 else 1 end

nocust | share
-------+--------
k007   | BBCA
b454   | BBCA
a522   | BBCA
p430   | BBCA
q797   | BBCA

Я ожидаю, что ошибка находится где-то между concat и order by, может кто-нибудь сказать мне, как получить правильный топ-5, например:

nocust | share
-------+--------
a522   | BBCA
b454   | BBCA
k007   | BBCA
p430   | BBCA
q797   | BBCA

Ответы [ 3 ]

1 голос
/ 03 октября 2019

Ваш ORDER BY не является стабильным видом;он сортирует данные по одной из двух категорий, но недостаточно детально определяет, как элементы должны быть отсортированы в этой категории. Это означает, что в форме TOP 5 sqlserver может свободно выбирать стратегию доступа к данным, что означает, что он может легко остановиться после того, как найдет 5 строк, данные которых таковы, что case when возвращает 0

Предположим, у вас есть этот выводот SELECT * ... ORDER BY Category

Category, Thing
Animal, Cat
Animal, Dog
Animal, Goat
Vegetable, Potato
Vegetable, Turnip
Vegetable, Swede

Существует абсолютно никаких гарантий , что если вы сделаете SELECT TOP 2 * ... ORDER BY category, вы получите "Cat, Dog" в этом порядке. Вы можете разумно получить «Козел, Собака» сегодня и «Кошка, Козел» завтра, когда SQL-сервер перетасовал свои индексы после добавления новых данных. Единственная вещь, которую вы можете гарантировать с помощью топ-2 по категориям, заключается в том, что, если в БД есть как минимум два животных, и нет новой категории, которая в алфавитном порядке раньше, чем «животное», вы получите два животных

Так ли это, потому что оптимизация TOP N означает, что sqlserver может остановиться рано, если у него есть N строк, соответствующих критериям;ему не нужно обращаться и сортировать миллион строк, если он уже нашел 5 строк, которые имеют категорию, которая будет первой в сортировке. Давайте представим, что он может знать различные значения и количество этих значений в столбце как часть своей статистики, он может сортировать эти различные значения, чтобы узнать, какие из них будут первыми, а затем пойти и найти любые 5 случайных строк, которые имеют значение, которое будетсначала отсортируйте и верните их. По сути, сервер sql может подумать: «Я знаю, что у меня есть 3« животных », а животные стоят перед всем остальным, а пользователь хочет 2. Я просто начну читать строки и остановлюсь после того, как получу 2 животных», а не «Я прочитаю». каждую вещь, отсортируйте все миллионы по категориям, затем возьмите первые 2 строки "

Это может быть намного быстрее, чем сортировка миллиона строк с последующим извлечением первых X

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

Добавьте больше столбцов в ваш заказ, чтобы каждая строказанимает гарантированное место в общем порядке, тогда сортировка будет стабильной, и TOP N будет возвращать одни и те же строки каждый раз. Чтобы сделать сортировку стабильной, коллекция столбцов, по которой вы сортируете, должна иметь уникальную комбинацию значений. Вы можете отсортировать по 20 столбцам, но если есть строки, в которых все 30 из этих столбцов имеют одинаковые значения (а дифференцирование происходит только по 21-му значению, по которому вы не упорядочиваете), то порядок сортировки не гарантируется

1 голос
/ 03 октября 2019

У вас очень странный ORDER BY - он только гарантирует, что записи с буквой в начале упорядочены до тех, которые имеют номер в начале - но вы НЕ фактически упорядочиваете поценит себя. Никаких конкретных ORDER BY означает: нет никакой гарантии относительно того, как будут упорядочены строки - как вы видите здесь.

Вам необходимо адаптировать ORDER BY к:

 ORDER BY
     CASE WHEN nocust LIKE '[a-z]%' THEN 1 ELSE 0 END,
     nocust

СЕЙЧАС вы на самом деле упорядочиваете по nocust - и теперь, я уверен, выходные данные будут идентичны

0 голосов
/ 03 октября 2019

Я пытаюсь ответить на это с другой точки зрения.

Сначала должно быть ясно, что Optimizer make the best possible plan quickly.

Optimizer select index or do not select index in most cost effective manner.

Я использую Adventure 2016 database и Production.Product имеет 504 строк.

select [Name],ProductNumber from Production.Product
order by [Name]

Сортирует строки, как и ожидалось.

select top 5 [Name],ProductNumber from Production.Product
order by [Name]

Сортирует строки, как и ожидалось.

ЕслиЯ использую оператор case в Order

select [Name],ProductNumber from Production.Product
order by case when [name] like '[a]%' then 1 else -1 end

. Сортировка записи производится по назначению. Все 504 строки обрабатываются.

Если я использую меньше, чем равно 20% of total rows в Top, как

select Top 5 [Name],ProductNumber from Production.Product
order by case when [name] like '[a]%' then 1 else -1 end



Then it pick first n records and display n record quickly.
Sorting was not as expected.

Если я использую больше 20% of total rows в Top, как

select Top (101) [Name],ProductNumber from Production.Product
order by case when [name] like '[a]%' then 1 else -1 end

Он будет обрабатывать все 504 rows и сортировать их соответственно.

Результат сортировки соответствует ожидаемому.

Во всех вышеперечисленных случаях Clustered Index Scan (Product id) выполнено. В этом примере [Name]and ProductNumber два разных non clustered index.

Но это не было выбрано.

Вы можете сделать это,

;With CTE as(

select  nocust, share ,
case when nocust like ‘[a-z]%’ then 0 else 1 end SortCol
from TB_STOCK
where share = ’BBCA’ 
  and concat(share, nocust) < ‘ZZZZZZZZ’


)

select top 5 * from CTE
order by SortCol
...