2009-05-07 8 views
0

J'ai une table. Le tableau doit stocker des valeurs de nombre d'un endroit, donc d'abord j'avais juste les deux colonnes sans la colonne incrémenter, donnant les éléments suivants:TSQL Insert Conundrum

RefID | TypeID 
    1 | 1 
    1 | 3 
    1 | 6 
    2 | 3 
    3 | 5 
    3 | 6 

Lorsque la première colonne est la référence pour un emplacement et le second est les valeurs réelles.

Le problème était de savoir comment choisir la valeur à donner à la première colonne. L'idée que j'ai est d'ajouter un champ d'auto-incrémentée pour générer ces valeurs, donc je devrais les données suivantes à la place:

ID | RefID | TypeID 
1 | 1 | 1 
2 | 1 | 3 
3 | 1 | 6 
4 | 4 | 3 
5 | 5 | 5 
6 | 5 | 6 

Ainsi, la colonne NuméroAuto (colonne 1) agit comme une graine pour la colonne de référence. J'ai donc deux problèmes: copier la valeur de la colonne d'identité dans la colonne de référence et renvoyer la valeur de référence à l'application afin qu'elle puisse être utilisée s'il y a plus d'une valeur pour l'emplacement.

je suis venu avec cette procédure stockée:

CREATE PROCEDURE [dbo].[AddCaseType] 
(
    @TypeID INTEGER, 
    @CaseID INTEGER = NULL OUT 
) 
AS 
BEGIN 

    INSERT INTO 
     dbo.CaseTypeList(RefID, TypeID) 
    VALUES 
     (ISNULL(@CaseID,SCOPE_IDENTITY()), @TypeID) 

    Set @CaseID = SCOPE_IDENTITY() 
END 
GO 

vérifie isNull pour NULL sur CaseID et si elle est nulle à utiliser la valeur SCOPE_IDENTITY(). Howevert SCOPE_IDENTITY est évalué avant l'insertion et retourne ainsi la dernière identité générée et non la nouvelle.

Je ne peux pas utiliser une valeur unique d'une table liée car j'ai besoin d'un suivi complet si un utilisateur édite les valeurs. Donc je sais ce que je veux faire, mais je n'ai pas les connaissances ou l'expérience pour le faire. Donc pour encapsuler: Comment puis-je générer une valeur unique pour le premier élément d'un ensemble et renvoyer cette valeur afin que je puisse le réutiliser pour le reste de cet ensemble?

Aussi, vous pourriez répondre s'il existe un autre moyen, plus simple. S'il vous plaît rappelez-vous - je suis un débutant en matière de SQL, donc je dois comprendre pourquoi quelque chose se passe parce que je pourrais avoir besoin de le changer à l'avenir. Après avoir pris un café, j'ai remarqué une erreur dans mon code - un résultat de la modification à trois colonnes, donc [ID] devient RefID dans la première partie de l'instruction INSERT. Cela invalide également une partie du code et modifie donc en partie la nature de mon problème. Désolé pour la confusion que cela peut causer.

+0

@Graham: Pouvez-vous ajouter des en-têtes de colonne sur vos tables d'échantillons? –

+0

@Michael Haren. Terminé. J'ai aussi remarqué un bug dans mon code qui invalide une partie de mon problème. –

+0

@Graham: J'ai mis à jour ma réponse pour capturer, je l'espère, vos changements. Note sur @@ Identity ou Scope_Identity(): il récupère une valeur qui a été * insérée le plus récemment * dans la portée désirée - pas la valeur * next *. C'est pourquoi dans ma réponse, je génère moi-même le numéro dans une transaction. –

Répondre

5

Lors de l'insertion dans une table avec une colonne identity, vous ne spécifiez rien pour la colonne identity:

DECLARE @ERR INT 

INSERT INTO CaseTypeList (TypeID, CaseID) --Column1 is auto-number, skip it 
VALUES (@TypeID, @CaseID) 

-- capture error var and last inserted identity value 
SELECT @ERR = @@ERROR, @ID = SCOPE_IDENTITY() 

-- IF @ERR <> 0 handle error, otherwise return 

Vous n'avez pas besoin insertion d'identité ou quelque chose comme ça, sauf si vous voulez contrôler explicitement la nombre. Dans ce cas, vous ne le définissez pas sur identity.

Aussi, j'ai un peu deviné sur vos colonnes.


Edit: OK, donc il semble que vous n'avez pas réellement besoin la colonne d'identité du tout. Au lieu de cela, vous devez générer le numéro suivant dans une séquence si aucune n'est fournie.Dans ce cas, vous pouvez laisser la colonne d'identité là-dedans - il ne fera pas mal quoi que ce soit, mais pourrait aider plus tard si vous voulez une seule clé unique:

CREATE PROCEDURE [dbo].[AddCaseType] 
(
    @RefID  INTEGER = NULL OUT, 
    @TypeID INTEGER 
) 
AS 

BEGIN TRANSACTION 

    IF @RefID IS NULL BEGIN 
    SELECT @RefID = MAX(RefID)+1 FROM CaseTypeList 
    END 

    IF @@ERROR <> 0 BEGIN ROLLBACK; RAISERROR('Could not get ID', 16, 1) END 


    INSERT INTO 
    CaseTypeList(RefID, TypeID) 
    VALUES 
    (@RefID, @TypeID) 

    IF @@ERROR <> 0 BEGIN ROLLBACK; RAISERROR('Could not insert', 16, 1) END 

COMMIT TRANSACTION 

Note: vous devriez probablement avoir une clé unique Constraint sur RefID et TypeID s'il ne peut pas y avoir de doublons.

+0

Ce que mon code essayait de faire était d'utiliser la valeur d'identité pour la valeur refID si une valeur refID n'était pas spécifiée. Malheureusement, j'avais changé la table mais pas mis à jour mon code correctement et cela me causait aussi des erreurs. Le point principal reste vrai: comment puis-je générer une valeur unique pour le premier élément d'un ensemble et renvoyer cette valeur afin que je puisse le réutiliser pour le reste de cet ensemble? –

+0

Si vous voulez réellement la valeur d'identité, remplacez le dernier contrôle d'erreur par ceci: "SELECT @ERR = @@ ERROR, @ID = SCOPE_IDENTITY()". Si vous voulez le RefID nouveau ou existant comme vous l'avez dit, ne faites rien car il est déjà capturé avec le "SELECT @RefID ..." –

+0

Léger problème avec votre code: Que se passe-t-il lorsque CaseTypeList ne contient aucune valeur? Le SELECT renvoie NULL et provoque une erreur car RefID ne peut pas être nul. –