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

192

Можете ли вы создать индекс для табличной переменной в SQL Server 2000?

т.е.

DECLARE @TEMPTABLE TABLE (
     [ID] [int] NOT NULL PRIMARY KEY
    ,[Name] [nvarchar] (255) COLLATE DATABASE_DEFAULT NULL 
)

Могу ли я создать индекс на Name?

GordyII
источник
3
Существует необходимость в создании обоих типов временных таблиц; и если у вас так много данных, что вам нужен индекс, возможно, пришло время взглянуть на реальную таблицу; что вы настроены на безопасность транзакций; Отфильтруйте по spid или идентификатору пользователя, а затем очистите его в конце. Реальные таблицы v временные таблицы имеют свои взлеты и падения, но если производительность является проблемой; попробуйте это с реальным столом тоже.
u07ch
Временная таблица - это настоящая таблица, она просто исчезнет, ​​когда вы закончите. Реальная разница (кроме того, что она исчезнет автоматически) заключается в том, что она находится в TempDB. Это на самом деле огромно, когда дело доходит до индексов и ограничений, потому что вы можете столкнуться с конфликтами имен, не только с другими выполнениями вашего кода, но и с кодом, выполняющимся в других базах данных в вашем экземпляре.
Белавский
@bielawski это переменная таблицы, а не временная таблица. Табличные переменные не допускают явно названных ограничений, сгенерированные системой имена гарантированно будут уникальными. Они разрешают именованные индексы с 2014 года, но это не проблема, поскольку индексы должны иметь уникальное имя только внутри объекта, а не между объектами.
Мартин Смит
Моя точка зрения была в 2 раза. 1) Кроме использования переменной, чтобы избежать запутывания транзакции, нет никакой существенной разницы между временной таблицей и табличной переменной. Однако в V-2000 нет синтаксиса для добавления ограничений, индексов ... в переменную. 2) Учитывая , можно использовать временную таблицу вместо именованных таблиц придатков как индексы БУДУТ столкновение с одновременным исполняющими копиями одного и тем же SP , если используются имя статического! Приведенный ниже механизм был разработан в явном виде, потому что я отслеживал сбои SP по именованным индексам, конфликтующим в этих точных обстоятельствах. Они ДОЛЖНЫ быть уникальными.
Белавский
1
@bielawski - никакие имена индексов не должны быть уникальными между объектами - только имена ограничений. это тривиально, чтобы проверить. Просто казниCREATE TABLE #T1(X INT); CREATE TABLE #T2(X INT); CREATE INDEX IX ON #T1(X); CREATE INDEX IX ON #T2(X);
Мартин Смит

Ответы:

364

Вопрос помечен SQL Server 2000, но в интересах людей, разрабатывающих последнюю версию, я сначала расскажу об этом.

SQL Server 2014

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

Пример синтаксиса для этого ниже.

/*SQL Server 2014+ compatible inline index syntax*/
DECLARE @T TABLE (
C1 INT INDEX IX1 CLUSTERED, /*Single column indexes can be declared next to the column*/
C2 INT INDEX IX2 NONCLUSTERED,
       INDEX IX3 NONCLUSTERED(C1,C2) /*Example composite index*/
);

Отфильтрованные индексы и индексы с включенными столбцами в настоящее время не могут быть объявлены с этим синтаксисом, однако SQL Server 2016 смягчает это немного дальше. Из CTP 3.1 теперь можно объявлять отфильтрованные индексы для табличных переменных. В RTM может быть так, что включенные столбцы также разрешены, но текущая позиция такова, что они "скорее всего не попадут в SQL16 из-за ограничений ресурсов"

/*SQL Server 2016 allows filtered indexes*/
DECLARE @T TABLE
(
c1 INT NULL INDEX ix UNIQUE WHERE c1 IS NOT NULL /*Unique ignoring nulls*/
)

SQL Server 2000 - 2012

Могу ли я создать индекс по имени?

Краткий ответ: да.

DECLARE @TEMPTABLE TABLE (
  [ID]   [INT] NOT NULL PRIMARY KEY,
  [Name] [NVARCHAR] (255) COLLATE DATABASE_DEFAULT NULL,
  UNIQUE NONCLUSTERED ([Name], [ID]) 
  ) 

Более подробный ответ ниже.

Традиционные таблицы в SQL Server могут иметь кластеризованный индекс или иметь структуру кучи .

Кластерные индексы могут быть объявлены как уникальные, чтобы запретить повторяющиеся значения ключа, или по умолчанию не уникальные. Если не уникален, то SQL Server автоматически добавляет уникализатор к любым дублирующимся ключам, чтобы сделать их уникальными.

Некластеризованные индексы также могут быть явно объявлены как уникальные. В противном случае для неуникального случая SQL Server добавляет указатель строки (ключ кластеризованного индекса или RID для кучи) ко всем ключам индекса (а не только к дубликатам), что снова гарантирует их уникальность.

В SQL Server 2000 - 2012 индексы для табличных переменных можно неявно создавать только путем создания ограничения UNIQUEили PRIMARY KEY. Разница между этими типами ограничений заключается в том, что первичный ключ должен находиться в столбцах, которые не могут содержать значения NULL. Столбцы, участвующие в уникальном ограничении, могут иметь значение NULL. (хотя реализация SQL Server уникальных ограничений при наличии NULLs не соответствует той, которая указана в стандарте SQL). Также таблица может иметь только один первичный ключ, но несколько уникальных ограничений.

Оба эти логических ограничения физически реализованы с уникальным индексом. Если явно не указано иное PRIMARY KEY, кластерный индекс станет уникальным, а уникальные ограничения не будут кластеризованы, но это поведение можно переопределить, указав CLUSTEREDили NONCLUSTEREDявно указав объявление ограничения (пример синтаксиса)

DECLARE @T TABLE
(
A INT NULL UNIQUE CLUSTERED,
B INT NOT NULL PRIMARY KEY NONCLUSTERED
)

В результате вышеизложенного могут быть неявно созданы следующие индексы для табличных переменных в SQL Server 2000 - 2012.

+-------------------------------------+-------------------------------------+
|             Index Type              | Can be created on a table variable? |
+-------------------------------------+-------------------------------------+
| Unique Clustered Index              | Yes                                 |
| Nonunique Clustered Index           |                                     |
| Unique NCI on a heap                | Yes                                 |
| Non Unique NCI on a heap            |                                     |
| Unique NCI on a clustered index     | Yes                                 |
| Non Unique NCI on a clustered index | Yes                                 |
+-------------------------------------+-------------------------------------+

Последний требует небольшого объяснения. В определении табличной переменной в начале этого ответа неуникальный некластеризованный индекс on Nameмоделируется уникальным индексом on Name,Id(напомним, что SQL Server в любом случае автоматически добавит ключ кластеризованного индекса в неуникальный ключ NCI).

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

DECLARE @T TABLE
(
A INT NULL,
B INT NULL,
C INT NULL,
Uniqueifier INT NOT NULL IDENTITY(1,1),
UNIQUE CLUSTERED (A,Uniqueifier)
)

Но это не точное моделирование того, как неуникальный кластерный индекс обычно будет реализован в SQL Server, так как это добавляет «уникализатор» ко всем строкам. Не только те, которые этого требуют.

Мартин Смит
источник
1
Примечание: решение 2000-2012 работает, только если текстовый столбец <= 900 байтов. то есть. varchar (900), nvarchar (450)
Андре Фигейредо
1
@AndreFigueiredo Да, это максимальный размер ключа индекса для постоянных таблиц в этих версиях.
Мартин Смит
1
Я просто хотел отметить, что ответ на SQL 2014 хорошо работает в Azure. Спасибо Мартин!
Джаксидиан
13

Следует понимать, что с точки зрения производительности нет различий между таблицами @temp и таблицами #temp, которые предпочитают переменные. Они находятся в одном месте (tempdb) и реализованы одинаково. Все отличия появляются в дополнительных функциях. Посмотрите эту удивительно полную рецензию: /dba/16385/whats-the-difference-between-a-temp-table-and-table-variable-in-sql-server/16386#16386

Хотя в некоторых случаях временная таблица не может использоваться, например, в табличных или скалярных функциях, в большинстве других случаев до v2016 (когда к табличной переменной можно добавить даже отфильтрованные индексы) вы можете просто использовать таблицу #temp.

Недостатком использования именованных индексов (или ограничений) в базе данных tempdb является то, что имена могут затем конфликтовать. Не только теоретически с другими процедурами, но часто довольно легко с другими экземплярами самой процедуры, которая будет пытаться поместить тот же индекс в свою копию таблицы #temp.

Чтобы избежать конфликтов имен, обычно работает что-то вроде этого:

declare @cmd varchar(500)='CREATE NONCLUSTERED INDEX [ix_temp'+cast(newid() as varchar(40))+'] ON #temp (NonUniqueIndexNeeded);';
exec (@cmd);

Это гарантирует, что имя всегда уникально даже при одновременном выполнении одной и той же процедуры.

Bielawski
источник
У него есть одна пропущенная скобка после varchar (40), добавьте, что
Tejasvi Hegde
1
С именованными индексами проблем нет - индексы должны иметь уникальные имена в таблице. Проблема заключается в именованных ограничениях, и лучшее решение, как правило, состоит в том, чтобы не называть их во временных таблицах - именованные ограничения предотвращают кэширование объектов временной таблицы.
Мартин Смит
1
Это должно быть верно только для определенных версий (если это верно для любой версии). Мне пришлось придумать этот обходной путь именно потому, что я обнаружил сбои sp в конфликте именованных индексов во время одновременного выполнения.
Белавский
@bielawski Ты используешь 2016 год? Мне очень любопытно, являются ли именованные индексы во временных таблицах риском для параллельных сред.
Эласканатор
1
@Elaskanator да, они есть. Мы обнаружили конфликт в таблицах метаданных SQL, когда под нагрузкой удаление имени индекса решило проблему. Это был SQL 2016.
Dan Def
0

Если переменная Table содержит большие данные, то вместо табличной переменной (@table) создать временную таблицу (#table) .table переменная не позволяет создавать индекс после вставки.

 CREATE TABLE #Table(C1 int,       
  C2 NVarchar(100) , C3 varchar(100)
  UNIQUE CLUSTERED (c1) 
 ); 
  1. Создать таблицу с уникальным кластерным индексом

  2. Вставить данные в таблицу Temp "#Table"

  3. Создать некластеризованные индексы.

     CREATE NONCLUSTERED INDEX IX1  ON #Table (C2,C3);
Boopathi.Indotnet
источник
создать индекс после оператора вставки, чтобы избежать ненужной сортировки
Boopathi.Indotnet
Обратите внимание, что это невозможно, если переменная таблицы находится в функции
Geoff