#sql-server #tsql
#sql-server #tsql
Вопрос:
Используя T-SQL, как я могу выбрать n строк неключевого, неиндексного столбца и избежать дублирования результатов?
Пример таблицы:
ID_ | state | customer | memo
------------------------------------------
1 | abc | 123 | memo text xyz
2 | abc | 123 | memo text abc
3 | abc | 456 | memo text def
4 | abc | 456 | memo text rew
5 | abc | 789 | memo text yte
6 | def | 123 | memo text hrd
7 | def | 432 | memo text dfg
Я хочу выбрать, скажем, 2 заметки для состояния «abc», но возвращаемые заметки не должны быть для одного и того же клиента.
memo
----
memo text xyz
memo text def
PS: единственное доступное условие выбора — это состояние (например: где state = ‘abc’)
Мне удалось сделать это очень неэффективным способом
SELECT top 2 MAX(memo)
FROM table
WHERE state = 'abc'
GROUP BY customer
Это отлично работает для небольшого размера выборки, но рабочая таблица содержит более 1 миллиарда строк.
Комментарии:
1. i hv doubt…do вам нужен результат для клиента 789 или нет…
2. В примере, который я упомянул, ответ отрицательный, поскольку N в этом случае равно 2. Если бы это было 3, то да, результат включал бы значение 789.
Ответ №1:
Вы можете попробовать использовать следующий запрос в вашем фактическом размере базы данных. Не уверен в производительности в таблице базы данных с миллиардом строк. Таким образом, вы можете выполнить тест самостоятельно.
SELECT memo
FROM (SELECT memo,
ROW_NUMBER() OVER (PARTITION BY customer ORDER BY (SELECT 0)) AS RN
FROM table1 WHERE state = 'abc') T
WHERE RN = 1
Вы можете проверить СКРИПКУ SQL
РЕДАКТИРОВАТЬ: добавление некластеризованного индекса для состояния и клиента, включая memo, значительно повысит производительность.
CREATE NONCLUSTERED INDEX [custom_index] ON table
(
[state] ASC,
[customer] ASC
)
INCLUDE ( [memo]) WITH (SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF) ON [DATA]
Комментарии:
1. Спасибо. Мне пришлось изменить ваш запрос, как показано ниже, и добавить некластеризованный индекс для состояния и клиента. ВЫБЕРИТЕ верхнюю заметку 2 ИЗ (ВЫБЕРИТЕ customer, state, memo, ROW_NUMBER() ПОВЕРХ (РАЗДЕЛЕНИЕ ПО ЗАКАЗУ клиента ПО (ВЫБЕРИТЕ 0)) КАК RN ИЗ table1, ГДЕ state = ‘abc’) T ГДЕ RN = 1
2. Будет ждать альтернативных предложений, прежде чем пометить это как ответ.
3. Да, определенно, я тоже хочу знать, есть ли лучшее решение 🙂 @Jayachandran . Также вы можете исключить customer и state из инструкции select подзапроса.
4. Да, только что заметил, что 🙂
5. PS: Проголосую за ответ, как только у меня будет требуемая репутация.
Ответ №2:
Способ получить это n различных значений для состояния / клиента — получить идентификатор для каждой группы
SELECT MIN(ID_) ID
FROM Table1
GROUP BY State, customer
( MIN
может быть заменено на MAX
, это просто способ получить одно из значений)
затем JOIN
добавьте это в таблицу, добавив другое условие
WITH getID AS (
SELECT MIN(ID_) ID
FROM Table1
GROUP BY State, customer
)
SELECT TOP 2
t.ID_, t.State, t.Customer, t.memo
FROM table1 t
INNER JOIN getID g ON t.ID_ = g.ID
WHERE t.state = 'abc'
если ваша версия SQLServer не поддерживает WITH
CTE
, может стать подзапросом
SELECT TOP 2
t.ID_, t.State, t.Customer, t.memo
FROM table1 t
INNER JOIN (SELECT MIN(ID_) ID
FROM Table1
GROUP BY State, customer
) g ON t.ID_ = g.ID
WHERE t.state = 'abc'
Другой способ — использовать CROSS APPLY
для получения ID
SELECT TOP 2
t.ID_, t.State, t.Customer, t.memo
FROM table1 t
CROSS APPLY (SELECT TOP 1
ID_
FROM table1 t1
WHERE t1.State = t.State AND t1.Customer = t.Customer) c
WHERE t.state = 'abc'
AND c.ID_ = t.ID_;
Комментарии:
1. Спасибо за это предложение. Рад сообщить, что этот метод также работает и дает ожидаемый результат. Однако, когда я выполнил оба предложения @TechGirl и ваше предложение (как CTE, так и подзапрос) параллельно, относительное разделение затрат составило 40: 60. Поэтому я склонен использовать метод номера строки.
2. :-). Я не имел в виду это как оскорбление. Я просто сообщал о своем результате для других, которые могли бы найти это полезным, и им не придется заново изобретать колесо. Как упоминалось в вопросе, моя самая большая проблема — производительность. Как я уже сказал, ваше решение работает отлично.
3. Относительная стоимость — это то, что я проверил на лету. Я все еще изучаю варианты. Одна вещь, которую я заметил, заключалась в том, что в вашем запросе было соединение с результатом поиска по индексу (memo) и сканирования индекса (для идентификатора, состояния и клиента), где, как у @TechGirl, был поиск по индексу (для customer и memo) и фильтр. Возможно, есть способ избежать проверки индекса, используя покрывающий индекс. Но тогда это занимает много места:(.
4. Работает, но ухудшает производительность. Сканирование индекса исключается и заменяется поиском по индексу, однако оно извлекает 2 миллиона записей по сравнению с всего лишь 25 сканированием индекса :-).
5. PS: Проголосую за ответ, как только у меня будет требуемая репутация.