Créer un index sur une variable de table

192

Pouvez-vous créer un index sur une variable de table dans SQL Server 2000?

c'est à dire

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

Puis-je créer un index sur Name?

GordyII
la source
3
La création des deux types de tables temporaires a un coût; et si vous avez tellement de données que vous avez besoin d'un index, il est peut-être temps d'utiliser une vraie table; que vous avez configuré pour garantir la sécurité des transactions; filtrez par spid ou identifiant d'utilisateur, puis effacez-le à la fin. Les tables réelles et les tables temporaires ont toutes deux des hauts et des bas, mais si les performances sont un problème; essayez-le aussi avec une vraie table.
u07ch
Une table temporaire «EST» une vraie table, elle disparaît lorsque vous avez terminé. La vraie différence (autre que cela disparaîtra automatiquement) est que c'est dans TempDB. C'est en fait énorme en ce qui concerne les index et les contraintes, car vous pourriez vous retrouver avec des conflits de noms, non seulement avec d'autres exécutions de votre code, mais avec du code s'exécutant dans d'autres bases de données de votre instance.
bielawski
@bielawski c'est une variable de table et non une table temporaire. Les variables de table n'autorisent pas les contraintes explicitement nommées, les noms générés par le système sont garantis d'être uniques. Ils autorisent les index nommés à partir de 2014, mais ce n'est pas un problème car les index doivent uniquement être nommés de manière unique dans un objet et non entre des objets.
Martin Smith
Mon point était double. 1) À part l'utilisation d'une variable pour éviter l'intrication des transactions, il n'y a pas de différence importante entre une table temporaire et une variable de table. Dans V-2000 cependant, il n'y a pas de syntaxe pour ajouter des contraintes, des index ... à une variable. 2) Étant donné que l'on peut utiliser une table temporaire à la place, les appendices de table nommés comme les index vont entrer en conflit avec des copies exécutées simultanément du même SP si un nom statique est utilisé! Le mécanisme ci-dessous a été développé explicitement parce que j'ai retracé les échecs du SP à des index nommés en conflit dans ces circonstances exactes. Ils DOIVENT être uniques.
bielawski
1
@bielawski - Aucun nom d' index n'a besoin d'être unique entre les objets - seuls les noms de contraintes le font. c'est trivial à tester. Il suffit d'exécuterCREATE TABLE #T1(X INT); CREATE TABLE #T2(X INT); CREATE INDEX IX ON #T1(X); CREATE INDEX IX ON #T2(X);
Martin Smith

Réponses:

364

La question est étiquetée SQL Server 2000, mais pour le bénéfice des personnes qui développent sur la dernière version, je vais d'abord y répondre.

SQL Server 2014

En plus des méthodes d'ajout d'index basés sur des contraintes décrites ci-dessous, SQL Server 2014 permet également de spécifier des index non uniques directement avec la syntaxe en ligne sur les déclarations de variables de table.

Un exemple de syntaxe pour cela est ci-dessous.

/*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*/
);

Les index filtrés et les index avec des colonnes incluses ne peuvent actuellement pas être déclarés avec cette syntaxe, mais SQL Server 2016 assouplit cela un peu plus. Depuis CTP 3.1, il est désormais possible de déclarer des index filtrés pour les variables de table. Par RTM, il se peut que les colonnes incluses soient également autorisées, mais la position actuelle est qu'elles "ne parviendront probablement pas à entrer dans SQL16 en raison de contraintes de ressources"

/*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

Puis-je créer un index sur Name?

Réponse courte: Oui.

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

Une réponse plus détaillée est ci-dessous.

Les tables traditionnelles dans SQL Server peuvent avoir un index clusterisé ou être structurées sous forme de tas .

Les index en cluster peuvent être déclarés comme uniques pour interdire les valeurs de clé en double ou être par défaut non uniques. S'il n'est pas unique, SQL Server ajoute silencieusement un identificateur à toutes les clés en double pour les rendre uniques.

Les index non clusterisés peuvent également être explicitement déclarés comme uniques. Sinon, pour le cas non unique, SQL Server ajoute le localisateur de lignes (clé d'index cluster ou RID pour un tas) à toutes les clés d'index (pas seulement les doublons), cela garantit à nouveau qu'elles sont uniques.

Dans SQL Server 2000-2012, les index sur les variables de table ne peuvent être créés implicitement qu'en créant une contrainte UNIQUEou PRIMARY KEY. La différence entre ces types de contraintes réside dans le fait que la clé primaire doit se trouver sur des colonnes non nullables. Les colonnes participant à une contrainte unique peuvent être nulles. (bien que l'implémentation par SQL Server de contraintes uniques en présence de NULLs ne soit pas conforme à celle spécifiée dans la norme SQL). De plus, une table ne peut avoir qu'une seule clé primaire mais plusieurs contraintes uniques.

Ces deux contraintes logiques sont physiquement implémentées avec un index unique. Si ce n'est pas spécifié explicitement, le PRIMARY KEYdeviendra l'index clusterisé et les contraintes uniques non groupées mais ce comportement peut être remplacé en spécifiant CLUSTEREDou NONCLUSTEREDexplicitement avec la déclaration de contrainte (Exemple de syntaxe)

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

En raison de ce qui précède, les index suivants peuvent être créés implicitement sur des variables de table dans 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                                 |
+-------------------------------------+-------------------------------------+

Le dernier demande un peu d'explication. Dans la définition de variable de table au début de cette réponse, l' index non unique non clusterisé Nameest simulé par un index unique activé Name,Id(rappelez-vous que SQL Server ajouterait de toute façon la clé d'index clusterisé à la clé NCI non unique).

Un index cluster non unique peut également être obtenu en ajoutant manuellement une IDENTITYcolonne pour agir comme un unique.

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

Mais ce n'est pas une simulation précise de la façon dont un index cluster non unique serait normalement implémenté dans SQL Server car cela ajoute le "Uniqueifier" à toutes les lignes. Pas seulement ceux qui en ont besoin.

Martin Smith
la source
1
Remarque: la solution 2000-2012 ne fonctionne que si la colonne de texte <= 900 octets. c'est à dire. varchar (900), nvarchar (450)
Andre Figueiredo
1
@AndreFigueiredo oui, c'est la taille maximale d'une clé d'index sur les tables permanentes également dans ces versions.
Martin Smith
1
Je voulais juste noter que la réponse SQL 2014 fonctionne bien dans Azure. Merci Martin!
Jaxidian
13

Il faut comprendre que du point de vue des performances, il n'y a aucune différence entre les tables @temp et les tables #temp qui favorisent les variables. Ils résident au même endroit (tempdb) et sont implémentés de la même manière. Toutes les différences apparaissent dans des fonctionnalités supplémentaires. Voir cet article incroyablement complet: /dba/16385/whats-the-difference-between-a-temp-table-and-table-variable-in-sql-server/16386#16386

Bien qu'il existe des cas où une table temporaire ne peut pas être utilisée, comme dans des fonctions de table ou scalaires, dans la plupart des autres cas antérieurs à la v2016 (où même des index filtrés peuvent être ajoutés à une variable de table), vous pouvez simplement utiliser une table #temp.

L'inconvénient d'utiliser des index nommés (ou des contraintes) dans tempdb est que les noms peuvent alors entrer en conflit. Pas seulement théoriquement avec d'autres procédures, mais souvent assez facilement avec d'autres instances de la procédure elle-même qui essaieraient de mettre le même index sur sa copie de la table #temp.

Pour éviter les conflits de noms, quelque chose comme ça fonctionne généralement:

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

Cela garantit que le nom est toujours unique, même entre les exécutions simultanées de la même procédure.

Bielawski
la source
Il manque une parenthèse après varchar (40), ajoutez que
Tejasvi Hegde
1
Il n'y a pas de problème avec les index nommés - les index doivent uniquement être nommés de manière unique dans une table. Le problème vient des contraintes nommées et la meilleure solution n'est généralement pas de les nommer dans des tables temporaires - les contraintes nommées empêchent la mise en cache des objets de table temporaire.
Martin Smith
1
Cela ne doit être vrai que pour certaines versions (si c'est vrai pour n'importe quelle version). J'ai dû trouver cette solution de contournement spécifiquement parce que j'avais tracé les échecs de sp au conflit d'index nommés lors d'exécutions simultanées.
bielawski
@bielawski Utilisez-vous 2016? Je suis très curieux de savoir si les index nommés sur les tables temporaires sont un risque pour les environnements concurrents.
Elaskanator
1
@Elaskanator oui, ils le sont. Nous avons trouvé des conflits dans les tables de métadonnées SQL en cas de chargement, la suppression du nom de l'index a résolu le problème. C'était SQL 2016.
Dan Def
0

Si la variable de table a des données volumineuses, alors au lieu de la variable de table (@table), créez une table temporaire (#table) .table variable ne permet pas de créer un index après l'insertion.

 CREATE TABLE #Table(C1 int,       
  C2 NVarchar(100) , C3 varchar(100)
  UNIQUE CLUSTERED (c1) 
 ); 
  1. Créer une table avec un index clusterisé unique

  2. Insérer des données dans la table Temp "#Table"

  3. Créez des index non clusterisés.

     CREATE NONCLUSTERED INDEX IX1  ON #Table (C2,C3);
Boopathi.Indotnet
la source
créer un index après une instruction d'insertion pour éviter un tri inutile
Boopathi.Indotnet
Notez que ce n'est pas possible si la variable de table est dans une fonction
Geoff