2010-02-24 5 views
4

J'ai un tableau à deux colonnes avec une clé primaire (int) et une valeur unique (nvarchar (255))moyen le plus rapide pour retourner une valeur de clé primaire SQL Server 2005

Lorsque j'insère une valeur à cette table, Je peux utiliser Scope_identity() pour retourner la clé primaire pour la valeur que je viens d'insérer. Toutefois, si la valeur existe déjà, je dois effectuer une sélection supplémentaire pour retourner la clé primaire pour une opération de suivi (insertion de cette clé primaire dans une seconde table)

Je pense qu'il doit y avoir une meilleure façon de Pour ce faire, j'ai envisagé d'utiliser des index couverts, mais la table ne comporte que deux colonnes. La plupart de ce que j'ai lu sur les index couverts suggère qu'ils aident uniquement lorsque la table est significativement plus grande que l'index.

Y at-il un moyen plus rapide de faire cela? Un indice couvert serait-il plus rapide même s'il est de la même taille que le tableau?

+0

Que voulez-vous dire « Si la valeur existe déjà » ... voulez-vous dire que vous essayez d'abord de trouver un dossier avec le même " Colonne2 'valeur? –

Répondre

1

Construction d'un indice ne vous gagnez n'importe quoi puisque vous avez déjà créé votre colonne de valeur comme unique (qui construit un index en arrière-plan). En effet, une analyse de table complète n'est pas différente d'une analyse d'index dans votre scénario. Je suppose que vous voulez avoir une sorte de comportement insert-if-not-already-existsts. Il n'y a aucun moyen pour se déplacer d'une seconde sélectionnez

if not exists (select ID from where name = @...) 
    insert into ... 
    select SCOPE_IDENTITY() 
else 
    (select ID from where name = @...) 

Si la valeur arrive à exister, la requête ont généralement été mis en cache, donc il ne devrait pas être frappé de performance pour la deuxième sélection d'identification.

+0

En outre, cela fait 2 sélections, et donc doubler la surcharge si l'enregistrement existe. –

+0

Bien que vous n'obtiendrez pas un "SCAN de table complet" mais un "index SEEK non-cluster". Tout à fait une différence. – erikkallen

+0

@erik, je ne suis certes pas trop forte sur SQL Server, pourriez-vous élaborer un peu plus? –

0

Créer un index unique pour la deuxième entrée, puis:

if not exists (select null from ...) 
    insert into ... 
else 
    select x from ... 

Vous ne pouvez pas vous éloigner de l'indice, et il est pas vraiment beaucoup en tête - SQL Server prend en charge les colonnes d'index JUSQU'A 900- octets, et ne discrimine pas. Les besoins de votre modèle sont plus importants que les problèmes de performances perçus, symboliser une chaîne (ce que vous faites) est une méthode courante pour réduire la taille de la base de données, et cela signifie indirectement (et généralement) de meilleures performances.

- modifier -

Pour apaiser fléole:

declare @x int = select x from ... 

if (@x is not null) 
    return x 
else 
    ... 
+0

Cela exécute 2 instructions select si l'enregistrement existe. Vous n'avez pas besoin de faire ça. –

+0

Ce sont des balançoires et des ronds-points, et seront probablement optimisés. –

0
[Update statment here] 
IF (@@ROWCOUNT = 0) 
BEGIN 
    [Insert statment here] 
    SELECT Scope_Identity() 
END 
ELSE 
BEGIN 
    [SELECT id statment here] 
END 

Je ne sais pas sur les performances, mais il n'a pas grand frais généraux

+0

Je n'ai pas besoin de mettre à jour la ligne si elle existe déjà ... – roryok

0

Comme cela a déjà été mentionné, cela ne devrait pas être une opération lente, surtout si vous indexez les deux colonnes. Cependant, si vous êtes déterminé à réduire le coût de cette opération, je ne vois aucune raison pour laquelle vous ne pouvez pas supprimer complètement la table et utiliser directement la valeur unique plutôt que de la rechercher dans cette table. Un mappage 1-1 comme celui-ci est (théoriquement) redondant. Je dis théoriquement parce qu'il peut y avoir des implications de performance à l'utilisation d'un nvarchar au lieu d'un int.

+0

Il y a de nombreuses raisons de ne pas utiliser le champ varchar (255) comme clé ... principalement les clés primaires des autres tables. –

+0

Bien évidemment, cela pourrait finir comme un remodelage majeur. Mais si quelqu'un cherche à optimiser un index, alors le remodelage des données doit absolument être pris en compte. –

0

Je posterai cette réponse car tout le monde semble dire que vous devez interroger la table deux fois dans le cas où l'enregistrement existe ... ce n'est pas vrai.

Étape 1) Créer un index unique sur l'autre colonne:

Je recommande ce que l'indice:

- Nous avons inclus la colonne « ID » de sorte que SQL pas à regarder loin une fois la clause "WHERE" est terminée.

CREATE INDEX MyLilIndex ON dbo.MyTable (Column2) INCLUDE (ID) 

Étape 2)

DECLARE @TheID INT 
SELECT @TheID = ID from MyTable WHERE Column2 = 'blah blah' 

IF (@TheID IS NOT NULL) 
BEGIN 
    -- See, you don't have to query the table twice! 
    SELECT @TheID AS TheIDYouWanted 
END 
ELSE 
    INSERT... 
    SELECT SCOPE_IDENTITY() AS TheIDYouWanted 
+0

Le cœur de sa question n'est pas sur la syntaxe, mais principalement sur l'index. –

+0

Je ne parle pas d'un changement de syntaxe ... Je lui dis de ne pas interroger deux fois la table (quel que soit l'index). J'ai seulement posté cette réponse parce que d'autres suggèrent qu'il a frappé la table deux fois. Aussi, j'ai maintenant inclus quel indice il devrait avoir exactement. –

0

Vous pouvez utiliser OUTPUT clause pour renvoyer la valeur dans la même déclaration. Voici un exemple.

LDD:

CREATE TABLE ##t (
    id int PRIMARY KEY IDENTITY(1,1), 
    val varchar(255) NOT NULL 
) 
GO 

-- no need for INCLUDE as PK column is always included in the index 
CREATE UNIQUE INDEX AK_t_val ON ##t (val) 

DML:

DECLARE @id int, @val varchar(255) 

SET @val = 'test' -- or whatever you need here 

SELECT @id = id FROM ##t WHERE val = @val 

IF (@id IS NULL) 
BEGIN 
    DECLARE @new TABLE (id int) 

    INSERT INTO ##t (val) 
    OUTPUT inserted.id INTO @new -- put new ID into table variable immediately 
    VALUES (@val) 

    SELECT @id = id FROM @new 
END 

PRINT @id 
Questions connexes