2015-03-14 1 views
1

Я играл с этим некоторое время, и, хотя есть некоторые подходы «грубой силы», которые технически работают, я чувствую, что мне не хватает что-то более элегантное (и эффективное).SQL Server: как эффективно вернуть объединенный результирующий набор из двух немного разных запросов

У меня есть таблица, содержащая историю событий. Для каждого события есть «источник» и «пункт назначения» (это то, что я фильтрую). Мне нужно вернуться в верхние n строк, упорядоченных по дате (сначала последней), сначала для определенного адресата из определенного источника, а затем для одного и того же адресата для любого источника, отличного от того, который использовался в первом запросе. Например, если первый запрос (соответствующий как получателю, так и источнику) возвращает n строк, мне не нужны строки из второго запроса (соответствующие одному и тому же адресу и соответствующему любому другим источникам), но если первый запрос возвращает меньше чем n строк, мне нужно, чтобы остальное было заполнено вторым запросом (предполагая, что есть такие строки).

Использование UNION ALL не работает, поскольку отдельные запросы не могут быть отсортированы отдельно, поэтому я получаю набор результатов, содержащий верхний n «любого» источника для данного адресата (если бы я мог включить ORDER BY в каждом отдельном запросе, это, вероятно, будет разумное решение):

SELECT TOP (100) * FROM 
(
SELECT TOP (100) Destin, Source, OtherData, Timestamp 
FROM History 
WHERE Destin = @Destin 
AND Source = @source 

UNION ALL 

SELECT TOP (100) Destin, Source, OtherData, Timestamp 
FROM History 
WHERE Destin = @Destin 
AND Source <> @source 

ORDER BY Timestamp DESC 
) AS SubQuery 
ORDER BY TimeStamp DESC 

Я попытался КТР, как хорошо, но ничего лучшего на этом фронте не найдено. Любые идеи об альтернативных подходах?

PS - TOP (100) в подзапросах - попытка повысить производительность, но я не могу сказать, будет ли SQL Server автоматически останавливать запуск первого (или второго) подзапроса, когда он удовлетворяет TOP (100) во внешнем запросе (что было бы идеальным).

+0

Ваша сортировка по метке. Означает ли это, что результаты приходят сначала с первой половины профсоюза? – shawnt00

+0

@ shawnt00: Да; показанный запрос не совсем работает, поскольку ограничение UNION ALL не позволяет сортировать отдельные запросы; цель состоит в том, чтобы получить первые 100 строк, самые последние до самых старых, «предпочитая» те, которые соответствуют как месту назначения, так и источнику, но затем «завершают» набор результатов теми, которые соответствуют только месту назначения. – eric

+0

Ну, проблема сортировки обрабатывается добавлением дополнительного столбца к результатам, например '1 as src' и' 2 as src'. Затем вы можете сортировать по 'src'. Это не относится к соображениям производительности, хотя это может быть связано с запуском запросов отдельно внутри proc и, при необходимости, объединением результатов. – shawnt00

ответ

2

В приведенном ниже примере используется выражение CASE для обеспечения требуемой последовательности. Кластеризованный (или охватывающий) индекс в destin поможет оптимизировать этот запрос. Это будет выполнять один поиск.

SELECT TOP(100) Destin 
     , Source 
     , OtherData 
     , Timestamp 
     , CASE WHEN Source = @source THEN 1 
      ELSE 2 
     END AS seq 
FROM History 
WHERE Destin = @Destin 
ORDER BY seq 
     , Timestamp DESC; 

РЕДАКТИРОВАТЬ:

Ниже еще один метод, который может работать лучше, если типичный случай значительно больше, чем 100 строк для одной и той же Destin. Кластеризованный/охватывающий индекс в столбцах ORDER BY, например, предложенный Aaron, поможет здесь. Это не так элегантно, как метод выражения CASE, и требует 2 оператора поиска, но это вариант, если производительность важнее, чем ремонтопригодность.

WITH same_source 
      AS (SELECT Destin 
         , Source 
         , OtherData 
         , Timestamp 
         , 1 AS seq 
         , ROW_NUMBER() OVER (ORDER BY Destin, Source, Timestamp) AS row_num 
       FROM  dbo.History 
       WHERE Destin = @Destin 
         AND Source = @Source 
      ) , 
     different_source 
      AS (SELECT Destin 
         , Source 
         , OtherData 
         , Timestamp 
         , 2 AS seq 
         , ROW_NUMBER() OVER (ORDER BY Destin, Source, Timestamp) AS row_num 
       FROM  dbo.History 
       WHERE Destin = @Destin 
         AND Source <> @Source 
      ) 
    SELECT TOP (100) 
      Destin 
      , Source 
      , OtherData 
      , Timestamp 
    FROM (SELECT Destin 
         , Source 
         , OtherData 
         , Timestamp 
         , seq 
       FROM  same_source 
       WHERE  row_num <= 100 
       UNION ALL 
       SELECT Destin 
         , Source 
         , OtherData 
         , Timestamp 
         , seq 
       FROM  different_source 
       WHERE  row_num <= 100 
      ) AS test 
    ORDER BY seq 
      , Destin 
      , Source 
      , Timestamp; 
+0

Да, это довольно пятно. Добавление TOP (n) к этому выглядит так, как будто это сделает трюк. Destin уже является кластеризованным индексом. Я проведу несколько тестов и посмотрю на план выполнения, чтобы убедиться, что он достаточно эффективен (моя проблема в том, что эта таблица содержит миллионы записей). Благодаря! – eric

+0

Хммм. Он выполняет поиск в кластеризованном индексе, что хорошо, но до сих пор существует довольно критический недостаток производительности. Предположим, что есть 10K строк с конкретным назначением, а 150 из них также имеют тот источник, о котором мы заботимся. Если TOP (n) равно 100, запрос будет намного более эффективным, если бы он искал как destin, так и источник (источник - это второй столбец в кластерном индексе). As-is, результат один и тот же, но SQL необходимо выполнить поиск по большинству/всем 10K-записям, которые соответствуют destin. Вот почему я первоначально подходил к этому с двумя отдельными запросами. – eric

+0

@ user640466 Итак, создайте индексный ключ 'Destin, Source' и убедитесь, что он включает' OtherData' и 'Timestamp' (<- плохое имя для столбца кстати). Тогда порядок, сделанный вами, просто будет помещать более важный источник в первую очередь, а если их больше 100, остальные не будут рассмотрены. –

Смежные вопросы