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

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

т.е.

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

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

Ответ 1

Вопрос помечен как 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 могут иметь кластерный индекс или структурированы как heaps.

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

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

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

Оба этих логических ограничения физически реализуются с уникальным индексом. Если явно не указано иначе, 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                                 |
+-------------------------------------+-------------------------------------+

Последнее требует немного объяснения. В определении переменной таблицы в начале этого ответа не уникальный некластеризованный индекс на Name моделируется индексом уникальный на 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, так как это добавляет "Уникализатор" ко всем строкам. Не только те, которые этого требуют.

Ответ 2

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

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

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

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

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

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

Ответ 3

Если переменная 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);