Можно ли добавить индекс в временную таблицу? И какая разница между create #t и declare @t

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

Просмотр вывода можно упростить следующим образом:

PID (int), Kind (int), Date (date), D1,D2..DN

где поля PID и Date и Kind не уникальны (может быть более одной строки с одинаковой комбинацией pid, kind, date), но это те, которые будут использоваться в объединении, как это

left join ComplexView mkcs on mkcs.PID=q4.PersonID and mkcs.Date=q4.date and mkcs.Kind=1
left join ComplexView mkcl on mkcl.PID=q4.PersonID and mkcl.Date=q4.date and mkcl.Kind=2
left join ComplexView mkco on mkco.PID=q4.PersonID and mkco.Date=q4.date and mkco.Kind=3

Теперь, если я просто делаю это так, выполнение запроса занимает значительное время, потому что сложный вид запускается три раза, я предполагаю, и из его огромного количества строк используются только некоторые из них (например, из 40000 используется только 2000)

То, что я сделал, - declare @temptable, и вставить в @temptable select * из ComplexView, где Date... - одно время для каждого запроса. Я выбираю только строки, которые я собираюсь использовать из своего ComplexView, а затем я присоединяюсь к этому @temptable.

Это значительно сократило время выполнения.

Тем не менее, я заметил, что если я создам таблицу в своей базе данных и добавлю кластерный индекс в PID, Kind, Date (non-unique clustered) и возьму данные из этой таблицы, а затем сделаю delete * из этой таблицы и вставка в эту таблицу из сложного вида занимает несколько секунд (3 или 4), а затем, используя эту таблицу в моем запросе (левое присоединение к ней три раза), сократите время запроса до половины, от 1 минуты до 30 секунд!

Итак, мой вопрос, в первую очередь, - возможно ли создать индексы для объявленных @temptables. И затем - я видел, как люди говорили о синтаксисе "create #temptable". Может, это то, что мне нужно? Где я могу прочитать о том, какая разница между declare @temptable и create #temptable? Что я должен использовать для запроса, такого как мой? (этот запрос относится к отчету служб MS Reporting Services, если это имеет значение).

Ответ 1

Это не полный ответ, но #table создаст временную таблицу, которую вам нужно удалить или она будет сохраняться в вашей базе данных. @table - это переменная таблицы, которая не будет сохраняться дольше, чем ваш script.

Кроме того, я думаю, что это сообщение ответит на другую часть вашего вопроса.

Создание индекса для переменной таблицы

Ответ 2

#tablename - физическая таблица, хранящаяся в tempdb, что сервер будет автоматически отбрасываться, когда соединение, которое его создало, закрыто, @tablename - это таблица, хранящаяся в памяти и проживающая в течение всего срока службы партии/процедуры который создал его, как локальную переменную.

Вы можете добавить индекс (не PK) в таблицу #temp.

create table #blah (fld int)
create nonclustered index idx on #blah (fld)

Ответ 4

Синтаксис @tableName является табличной переменной. Они довольно ограничены. Синтаксис описан в документации для DECLARE @local_variable. Вы можете отчасти иметь индексы для табличных переменных, но только косвенно, указав ограничения PRIMARY KEY и UNIQUE для столбцов. Итак, если ваши данные в столбцах, по которым вам нужен индекс, оказываются уникальными, вы можете сделать это. Смотрите этот ответ. Этого может быть "достаточно" для многих вариантов использования, но только для небольшого количества строк. Если у вас нет индексов для вашей табличной переменной, оптимизатор обычно будет обрабатывать переменные таблицы так, как если бы они содержали одну строку (независимо от того, сколько строк на самом деле), что может привести к ужасным планам запросов, если у вас есть сотни или тысячи строк в них. вместо этого.

Синтаксис #tableName представляет собой локальную временную таблицу. Вы можете создать их, используя синтаксис SELECT…INTO #tableName или CREATE TABLE #tableName. Область этих таблиц немного сложнее, чем область переменных. Если у вас есть CREATE TABLE #tableName в хранимой процедуре, все ссылки на #tableName в этой хранимой процедуре будут ссылаться на эту таблицу. Если вы просто ссылаетесь на #tableName в хранимой процедуре (не создавая ее), она заглянет в область действия вызывающего. Таким образом, вы можете создать #tableName в одной процедуре, вызвать другую процедуру и в этой другой процедуре прочитать/обновить #tableName. Однако, как только процедура, которая создала #tableName, будет выполнена до конца, эта таблица автоматически не будет ссылаться и очищаться SQL Server. Таким образом, нет смысла очищать эти таблицы вручную, если только у вас нет процедуры, которая предназначена для циклического выполнения/выполнения в течение неопределенного или длительного периода времени.

Вы можете определить сложные индексы для временных таблиц, как если бы они были постоянными таблицами, по большей части. Так что если вам нужно индексировать столбцы, но иметь повторяющиеся значения, которые не позволяют использовать UNIQUE, то это путь. Вам даже не нужно беспокоиться о конфликтах имен в индексах. Если вы выполняете что-то вроде CREATE INDEX my_index ON #tableName(MyColumn) в нескольких сеансах, каждый из которых создал свою собственную таблицу с именем #tableName, SQL Server выполнит некоторую магию, чтобы повторное использование идентификатора глобального вида my_index не взорваться.

Кроме того, временные таблицы будут автоматически строить статистику и т.д., Как обычные таблицы. Оптимизатор запросов распознает, что временные таблицы могут содержать более 1 строки, что само по себе может привести к значительному повышению производительности по сравнению с переменными таблицы. Конечно, это также небольшое количество накладных расходов. Хотя эти издержки, вероятно, того стоят и не заметны, если время выполнения ваших запросов превышает одну секунду.