SQL Server, используя UNION ALL для нескольких таблиц, а затем реализацию подкачки

Мне также нужна помощь по поисковому вызову и использованию UNION ALL для нескольких таблиц:

Как реализовать оптимизированный пейджинг при объединении нескольких таблиц с помощью UNION ALL и возвращении только определенного количества строк...


declare @startRow int
declare @PageCount int

set @startRow = 0
set @PageCount = 20

set rowcount @PageCount

select Row_Number() OVER(Order by col1) as RowNumber, col1, col2
from
(
    select col1, col2 from table1 where datetimeCol between (@dateFrom and @dateTo)
    union all
    select col1, col2 from table2 where datetimeCol between (@dateFrom and @dateTo)
    union all
    select col1, col2 from table3 where datetimeCol between (@dateFrom and @dateTo)
    union all
    select col1, col2 from table4 where datetimeCol between (@dateFrom and @dateTo)
    union all
    select col1, col2 from table5 where datetimeCol between (@dateFrom and @dateTo)
) as tmpTable
where RowNumber > @startRow

Таблица 3, 4 и 5 имеют огромное количество строк (миллионы строк), где таблицы 1 и 2 могут содержать только несколько тысяч строк.

Если startRow является "0", я ожидаю только данные от строки 1 до 20 (из таблицы 1). Я получаю правильный результат, но с большой накладной на оставшейся таблице, в то время как sql-сервер пытается получить все данные и фильтровать его....

чем дольше интервал @dateFrom и @dateTo делает мой запрос значительно медленнее при попытке получить только несколько строк из общего набора результатов

Пожалуйста, помогите, как я могу реализовать простой, но лучший подход с аналогичной логикой.: (

Ответ 1

Рассмотрим использование предложения OFFSET FETCH (работает с MSSQL 2012):

declare @startRow int
declare @PageCount int

set @startRow = 0
set @PageCount = 20


select col1, col2
from
(
    select col1, col2 from table1 where datetimeCol between (@dateFrom and @dateTo)
    union all
    select col1, col2 from table2 where datetimeCol between (@dateFrom and @dateTo)
    union all
    select col1, col2 from table3 where datetimeCol between (@dateFrom and @dateTo)
    union all
    select col1, col2 from table4 where datetimeCol between (@dateFrom and @dateTo)
    union all
    select col1, col2 from table5 where datetimeCol between (@dateFrom and @dateTo)
) as tmpTable
order by col1
offset @startRow rows
fetch next @PageCount rows only

Я также хочу упомянуть здесь, почему этот запрос всегда занимает время O (n * log (n)).
Для выполнения этого запроса база данных должна:

  • объединение нескольких списков в один список - время O (n) для каждой таблицы, где n - общее количество строк в ваших таблицах;
  • список сортировки по col1 - принимает O (n * log (n)), где n - общее количество строк
  • перемещайте список в отсортированном порядке, пропустите строки @startRow, возьмите следующие строки @PageCount.

Как вы можете видеть, вам нужно объединить и отсортировать все данные, чтобы получить ожидаемый результат (номер 3).

Если производительность этого запроса по-прежнему оставляет желать лучшего, и вы хотите увеличить его, попробуйте:

  • создать кластерный индекс на основе col1 во всех таблицах
  • создать индекс некластеров на основе col1 во всех таблицах и ** включить все остальные столбцы, которые вы хотите вывести в списке выбора **.

Ответ 2

Вместо использования классического подкачки OFFSET (обратите внимание, что SQL Server 2012 теперь поддерживает его), я думаю, что ваш конкретный вариант использования может сильно выиграть от метода, который часто называют "методом поиска", как описано в этот пост в блоге здесь. Тогда ваш запрос будет выглядеть следующим образом.

select top 20 col1, col2
from
(
    select col1, col2 from t1 where datetimeCol between (@dateFrom and @dateTo)
    union all
    select col1, col2 from t2 where datetimeCol between (@dateFrom and @dateTo)
    union all
    select col1, col2 from t3 where datetimeCol between (@dateFrom and @dateTo)
    union all
    select col1, col2 from t4 where datetimeCol between (@dateFrom and @dateTo)
    union all
    select col1, col2 from t5 where datetimeCol between (@dateFrom and @dateTo)
) as tmpTable
where (col1 > @lastValueForCol1)
   or (col1 = @lastValueForCol1 and col2 > @lastValueForCol2)
order by col1, col2

Значения @lastValueForCol1 и @lastValueForCol2 являются соответствующими значениями последней записи с предыдущей страницы. Это позволяет вам получать "следующую" страницу. Если направление ORDER BY DESC, просто используйте <. Если (col1, col2) не является глобально уникальным на вашем tmpTable, вам может потребоваться добавить еще один столбец в запрос и в предложение WHERE и ORDER BY, чтобы избежать потери записей между страницами.

С помощью вышеописанного метода вы не можете сразу перейти на страницу 3, не предварительно извлек предыдущие 40 записей. Но часто вы не хотите так далеко прыгать. Вместо этого вы получаете гораздо более быстрый запрос, который мог бы получать данные в постоянное время, в зависимости от вашей индексации. Кроме того, ваши страницы остаются "стабильными", независимо от того, изменяются ли базовые данные (например, на странице 1, когда вы находитесь на странице 4).

Обратите внимание, что "метод поиска" также называется подкачкой набора ключей.

Индексация

В то время как поисковый вызов с помощью метода поиска всегда быстрее, чем при использовании OFFSET, вы должны убедиться, что (col1, col2) индексируется в каждой из ваших таблиц!

Ответ 3

Поскольку таблицы упорядочены в наборе результатов для поискового вызова (Union ALL не сортирует), нет причин выбирать из всех 5 таблиц. Вы должны изменить код на:

  • Запрос из таблицы 1. Посмотрите, есть ли у вас достаточно записей.
  • Если не запрос из таблицы 2 и т.д.

Управление счетчиком смещений в соответствии с количеством запросов, запрошенных каждый раз. Таким образом, вы запрашиваете только нужные вам таблицы.

Вы даже можете оптимизировать, выбрав количество записей в таблице в соответствии с фильтром, чтобы узнать, нужно ли запрашивать у него какие-либо данные. Поэтому, если вам нужны записи 30-50, а таблица1 содержит только 20 совпадающих записей, вы можете вообще пропустить ее.

Ответ 4

select col1, col2 from table1 where datetimeCol between (@dateFrom and @dateTo)
union all
select col1, col2 from table2 where datetimeCol between (@dateFrom and @dateTo)
union all
select col1, col2 from table3 where datetimeCol between (@dateFrom and @dateTo)
union all
select col1, col2 from table4 where datetimeCol between (@dateFrom and @dateTo)
union all
select col1, col2 from table5 where datetimeCol between (@dateFrom and @dateTo)

существенно эффективнее обычной таблицы, если есть указатель на ключ сортировки, который вы используете для подкачки. Обычно это приводит к тому, что план запросов, в котором все таблицы объединяются, объединяется. Объединение конкатенации - это потоковая операция. Эта стоимость пропорциональна количеству строк, а не количеству строк в таблицах.

Пейджинг по номеру строки в SQL Server всегда работает, перечисляя строки, начинающиеся до конца, до тех пор, пока не будет достигнуто требуемое окно. Независимо от того, рисуются ли строки из таблицы или из нескольких объединенных таблиц, это не имеет принципиального значения.

Таким образом, хороший шанс сделать это быстро - создать индекс покрытия с ключом col1. К сожалению, невозможно одновременно индексировать значение between (@dateFrom and @dateTo). Поэтому вам нужно попробовать обе стратегии индексирования и выбрать, что лучше всего работает.

Ответ 5

Возможно, проблема с дизайном базы данных, так как у вас есть 5 похожих таблиц. Но помимо этого вы можете материализовать запрос UNION ALL в постоянную таблицу или temp # -table с соответствующими индексами на ней и, наконец, разбивать на материализованные данные с помощью предложения ROW_NUMBER().