IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Développement SQL Server Discussion :

Trigger incrémente plusieurs champs


Sujet :

Développement SQL Server

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé
    Inscrit en
    Novembre 2006
    Messages
    249
    Détails du profil
    Informations forums :
    Inscription : Novembre 2006
    Messages : 249
    Par défaut Trigger incrémente plusieurs champs
    Bonjour,
    Soit la table suivante:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    CREATE TABLE [dbo].[ENTREE](
    	[IDEntree] [bigint] IDENTITY(1,1) NOT NULL,
    	[RefEntree] [nchar](18) NOT NULL,
    	[DateEntree] [date] NOT NULL,
    	[ObjetEntree] [ntext] NOT NULL,
    	[RefFournisseur] [int] NOT NULL,
     CONSTRAINT [PK_ENTREE] PRIMARY KEY CLUSTERED 
    (
    	[IDEntree] ASC
    )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
    ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
    .
    Cette table me permet de garder en mémoire l'entête d'une entrée en stock d'articles.
    La colonne [RefEntree] doit se présenter suivant le format:
    "AAAA-MM-CompteurMois-CompteurAnnee"
    AAAA: Répresentera l'année en cours (Ex.: 2011)
    MM: Représentera mois (Ex.: 01, 10, 12, .etc) de l'année en cours
    CompteurMois: Représentera le nombre d'entrée faite dans le mois MM de l'annee AAAA
    CompteurAnnee: Représentera le nombre d'entrée faite dans l'année AAAA.

    CompteurMois & CompteurAnnee doit pouvoir s'incrémenter lors d'un nouveau ajout dans la table suivant le mois MM & l'année AAAA.
    CompteurMois est un entier à 4 caractères CompteurAnnee à 5 caractères

    Je reste à l'écoute pour des propositions, suggestions de triggers permettrant de prendre cela en compte.
    NB: [RefEntree] est unique dans la table et:
    AAAA et MM provient de la date que nous aurons à saisir dans la colonne: [DateEntree]

  2. #2
    Membre éclairé Avatar de J0r_x
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2006
    Messages
    804
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Marne (Champagne Ardenne)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Administration - Collectivité locale

    Informations forums :
    Inscription : Mai 2006
    Messages : 804
    Par défaut
    Pourquoi ne pas avoir un champ compteurmois et compteurannée dans cette table ?

  3. #3
    Modérateur

    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Janvier 2005
    Messages
    5 826
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Janvier 2005
    Messages : 5 826
    Par défaut
    Bonjour,

    Il y a 3 erreurs :

    - toute valeur stockée dans une colonne doit être atomique (première forme normale), or ce n'est pas votre cas
    - vous stockez des valeurs numériques dans une colonne dont le type permet de stocker des chaînes de caractère, ce qui n'a aucun sens
    - vos stockez des valeurs qui ne contiennent pas de caractères non latins dans une colonne dont le type (nchar) est conçu pour cela.
    L'embêtant c'est qu'au lieu de consommer un octet par caractère en char (qui utilise ASCII) vous en utilisez deux en nchar (qui utilise Unicode).

    Résoudre la première erreur élimine les deux autres.
    En fait, c'est une erreur de modélisation : il vaudrait mieux que vous ayez une table qui gère votre séquence, et référencer celle-ci par une contrainte d'intégrité dans la table ENTREE.
    En faisant cela, vous évitez le trigger, qui rallonge la durée de la transaction, donc diminue la concurrence d'accès à la table ENTREE.
    D'autre part la valeur du compteur par an n'a pas d'utilité (à priori), puisque c'est la somme des compteurs des mois d'une année particulière.

    Voici donc pour la table :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    CREATE TABLE dbo.COMPTEUR_ENTREE
    (
    	COMPTEUR_ENTREE int identity NOT NULL CONSTRAINT PK_COMPTEUR_ENTREE PRIMARY KEY NONCLUSTERED
    	, ANNEE smallint NOT NULL CONSTRAINT CHK_COMPTEUR_ENTREE__ANNEE CHECK (ANNEE >= 0)
    	, MOIS tinyint NOT NULL CONSTRAINT CHK_COMPTEUR_ENTREE__MOIS CHECK (MOIS BETWEEN 1 AND 12)
    	, COMPTEUR int NOT NULL CONSTRAINT DF_COMPTEUR_ENTREE__COMPTEUR DEFAULT (0)
    	, CONSTRAINT UQ_COMPTEUR_ENTREE__ANNEE__COMPTEUR UNIQUE CLUSTERED (ANNEE, MOIS)
    )
    GO
    S'il est possible que votre compteur par mois dépasse 2.147.483.647, alors changez la colonne COMPTEUR en bigint

    Et la procédure stockée :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    CREATE PROCEDURE dbo.usp_COMPTEUR_ENTREE_get
    	@COMPTEUR int OUTPUT
    	, @COMPTEUR_ENTREE int OUTPUT
    AS
    BEGIN
    	SET NOCOUNT ON
    	SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
     
    	DECLARE @ANNEE smallint
    		, @MOIS tinyint
     
    	SELECT	@COMPTEUR_ENTREE = COMPTEUR_ENTREE
    		, @ANNEE = ANNEE
    		, @MOIS = MOIS
    	FROM	dbo.COMPTEUR_ENTREE
    	WHERE	ANNEE = DATEPART(year, GETDATE())
    	AND	MOIS = DATEPART(month, GETDATE())
     
    	-- Si le mois et/ou l'année n'existent pas, on les ajoute
    	IF
    	(
    		@ANNEE IS NULL
    		OR @MOIS IS NULL
    	)
    	BEGIN
    		INSERT	 INTO dbo.COMPTEUR_ENTREE
    		(
    			ANNEE
    			, MOIS
    			, COMPTEUR
    		)
    		SELECT	DATEPART(year, GETDATE())
    			, DATEPART(month, GETDATE())
    			, 1
     
    		SELECT	@COMPTEUR = 1
    			, @COMPTEUR_ENTREE = SCOPE_IDENTITY()
    	END
    	ELSE
    	BEGIN
    		-- Si cela existe, on ne fait qu'incrémenter le compteur
    		UPDATE	dbo.COMPTEUR_ENTREE
    		SET	@COMPTEUR = COMPTEUR = COMPTEUR + 1
    		WHERE	ANNEE = @ANNEE
    		AND	MOIS = @MOIS
    	END
    END
    GO
    Un exemple d'utilisation :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    DECLARE @c int
    	, @ce int
     
    EXEC dbo.usp_COMPTEUR_ENTREE_get
    	@COMPTEUR = @c OUTPUT
    	, @COMPTEUR_ENTREE = @ce OUTPUT
    Avant chaque modification de la table ENTREE, vous appelez la procédure stockée : elle vous la valeur pour la contrainte d'intégrité (la colonne RefEntree), et la valeur du compteur.

    Au final, il vous faut changer le type de la colonne RefEntree en int :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    ALTER TABLE dbo.ENTREE
    ALTER COLUMN RefEntree int
    Faire l'éventuel UPDATE pour mettre cette colonne à la bonne valeur de la table dbo.COMPTEUR_ENTREE.COMPTEUR_ENTREE.

    Ensuite mettre la colonne non-NULLable et mettre la référence d'intégrité :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    ALTER TABLE dbo.ENTREE
    ALTER COLUMN RefEntree int NOT NULL
    CONSTRAINT FK_ENTREE_RefEntree FOREIGN KEY REFERENCES dbo.COMPTEUR_ENTREE
    @++

  4. #4
    Membre Expert Avatar de iberserk
    Homme Profil pro
    Architecte de base de données
    Inscrit en
    Novembre 2004
    Messages
    1 795
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Architecte de base de données
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2004
    Messages : 1 795
    Par défaut
    toute valeur stockée dans une colonne doit être atomique (première forme normale), or ce n'est pas votre cas
    Pas sur que fsmrel serait d'accord sur la forme :-), la valeur est atomique puisque ce n'est pas une séquence (sont type nchar en est la preuve).

    Mais ça n’empêche pas que je suis 100% d'accord avec toi sur la dangerosité de ces pratiques fourre tout.

  5. #5
    Membre éclairé
    Inscrit en
    Novembre 2006
    Messages
    249
    Détails du profil
    Informations forums :
    Inscription : Novembre 2006
    Messages : 249
    Par défaut
    Bonjour à tous,
    Merci pour votre critique, cela m'a fait du bien.


  6. #6
    Modérateur

    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Janvier 2005
    Messages
    5 826
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Janvier 2005
    Messages : 5 826
    Par défaut
    Citation Envoyé par iberserk
    Pas sur que fsmrel serait d'accord sur la forme :-), la valeur est atomique puisque ce n'est pas une séquence (sont type nchar en est la preuve).
    Attention : je n'ai rien contre le stockage d'une liste de valeurs dans une colonne si celle-ci dépend d'un identifiant, et ici ce n'est pas le cas.
    En ce sens je me suis trompé de forme normale : c'est la seconde

    Mais bon, je sais que je prêche un convaincu

    @++

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Trigger : incrémenter un champ selon un autre champ
    Par David55 dans le forum Requêtes
    Réponses: 9
    Dernier message: 28/03/2011, 18h26
  2. trigger sur plusieurs champs d'une table
    Par PxPar dans le forum PL/SQL
    Réponses: 1
    Dernier message: 27/08/2009, 15h31
  3. Plusieurs champs auto incrémenté
    Par jobouga dans le forum SQL Procédural
    Réponses: 5
    Dernier message: 15/08/2007, 19h29
  4. Trigger UPDATED sur plusieurs champs
    Par Maroxye dans le forum Développement
    Réponses: 4
    Dernier message: 03/04/2007, 14h08
  5. Réponses: 7
    Dernier message: 01/02/2006, 15h49

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo