Précédent   Forum des professionnels en informatique > Bases de données > MS SQL-Server > Développement
Développement Forum d'entraide sur le Transact-SQL, le CLR, les procédures stockées, les triggers, les requêtes SQL
Partagez cette discussion sur d'autres réseaux sociaux : Viadeo Twitter Google Facebook Digg Delicious MySpace Yahoo
Réponse Proposer ce sujet en actualité
 
Outils de la discussion
Publicité
'
Vieux 13/01/2011, 18h01   #1
Membre éclairé
 
Avatar de tototiti2008
 
Inscription : octobre 2008
Messages : 327
Détails du profil
Informations forums :
Inscription : octobre 2008
Messages : 327
Points : 321
Points : 321
Par défaut [SQL SERVER 2005] Procédure Transac SQL pour un nul

Bonjour à tous,

J'essaie vainement de créer une procédure transac SQL et je suis super débutant...

Voilà à quoi ça ressemble

Code :
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
-- ================================================
-- Template generated from Template Explorer using:
-- Create Procedure (New Menu).SQL
--
-- Use the Specify Values for Template Parameters 
-- command (Ctrl-Shift-M) to fill in the parameter 
-- values below.
--
-- This block of comments will not be included in
-- the definition of the procedure.
-- ================================================
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: <Author,,Name>
-- Create date: <Create Date,,>
-- Description: <Description,,>
-- =============================================
CREATE PROCEDURE [dbo].[csp_CreateTest2] @Fam nvarchar
-- Add the parameters for the stored procedure here
AS
set nocount on
DECLARE @Fam3204 numeric
select @Fam3204 = dataid from filedata where lang_00 = @Fam and descid = 3204
insert into temp_test (tt) values(cast(@Fam3204 as varchar))
set nocount off
GO
alors, si je fait la requête select manuellement sur la table filedata et que je fixe le paramètre @Fam à une valeur 'TOTO', j'ai bien une réponse numérique (1542)

Code :
SELECT dataid FROM filedata WHERE lang_00='TOTO' AND descid = 3204
me renvoie 1542

la table temp_test a un champ unique TT de type varchar

si j'exécute la procédure en mettant en paramètre d'entrée TOTO, la table TEMP_TEST a bien un nouvel enregistrement mais il a pour valeur NULL...

Merci de m'avoir lu
__________________
tototiti2008 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 14/01/2011, 04h37   #2
Modérateur

 
Avatar de elsuket
 
Homme Nicolas Souquet
Administrateur de base de données
Inscription : janvier 2005
Messages : 4 665
Détails du profil
Informations personnelles :
Nom : Homme Nicolas Souquet
Âge : 30
Localisation : Thaïlande

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

Informations forums :
Inscription : janvier 2005
Messages : 4 665
Points : 8 707
Points : 8 707
Bonjour,

C'est en fait parce que vous avez déclaré @Fam au type nvarchar.
Comme vous n'avez pas précisé de largeur, celle-ci est par défaut à 1, donc 'TOTO' est tronqué à 'T'

Pour le vérifier, il suffit d'exécuter :

Code :
1
2
3
DECLARE @Fam nvarchar
SET	@Fam = 'Toto'
SELECT	@Fam
Qui vous retourne 'T'.

Si vous réécrivez votre procédure en vous débarrassant des fioritures produites par SQL Server Management Studio (SSMS), vous aurez donc :

Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
CREATE PROCEDURE dbo.csp_CreateTest2
	@Fam nvarchar
AS
BEGIN
	SET NOCOUNT ON
 
	DECLARE @Fam3204 NUMERIC
 
	SELECT	@Fam3204 = dataid 
	FROM	filedata 
	WHERE	lang_00 = @Fam 
	AND	descid = 3204
 
	INSERT INTO temp_test (tt) 
	VALUES(cast(@Fam3204 AS varchar))
END
Vous avez fait d'autres erreurs, que je vais vous donner par ordre décroissant de gravité :

- Si la colonne lang_00 ne doit jamais contenir des caractères autres que ceux de l'alphabet latin et des chiffres, passez au type varchar(n) si ce n'est pas déjà le cas.
En effet le type nvarchar stocke les caractères en Unicode, qui consomme deux octets par caractère.
varchar à l'inverse les stocke en ASCII, qui consomme un octet par caractère, mais ne supporte que les caractères latins (pas ceux des alphabets asiatiques, arabes, ...)
Vous pouvez changer le type de votre colonne comme suit :

Code :
1
2
ALTER TABLE filedata 
ALTER COLUMN lang_00 varchar(mêmeLongueurQueLaColonneNvarchar)
- Écrivez votre requête de façon ensembliste, comme SQL l'est.
Programmer en SQL est totalement différent de la façon de programmer en langages par objets ou procéduraux.
SQL est un langage déclaratif, donc il vous suffit d'écrire ce que vous souhaitez faire ou obtenir.
Dans Les langages par objets ou procéduraux, vous écrivez comment vous voulez arrivez au résultat.

Votre début de requête SELECT est :

Code :
1
2
SELECT	@Fam3204 = dataid 
FROM	filedata
Et dans ce cas vous supposez qu'il n'y aura qu'une seule ligne qui satisfasse la requête, alors qu'il peut y en avoir plus (ou ne peut pas y en avoir), et dans ce cas votre INSERT est "partiel", donc faux.

Voyez l'instruction INSERT que je vais vous donner plus bas.
Pour comprendre l'effet, vous pouvez lire ce billet qui traite des triggers, mais l'idée de fond est la même.

- Déclarez votre variable @Fam au même type que celui de la colonne lang_00, avec la longueur , par exemple varchar(5) ou nvarchar(4).
Cela évite à SQL Server de faire le transtypage qui peut résulter en une lecture complète de la table, au lieu d'utiliser un index de la table

- Pourquoi la colonne tt de la table temp_test n'est pas de type numeric ?
Regardez la liste des types disponibles sous SQL Server ici, notamment pour le type numeric.
Choisissez l'échelle et la précision en fonction de vos besoin, mais en prenant toujours le plus proche supérieur à vos besoins.
Par exemple si vous avez besoin de stocker des chaînes dont vous êtes certain qu'elle ne n'auront jamais une longueur plus grande que 40 caractères, il n'y pas besoin de mettre varchar(255)

- Qualifiez les noms des objets que vous manipulez (tables, fonctions, procédures, vues) par le nom du schéma auquel ils appartiennent.
Par défaut c'est dbo.

En bref, plus vous serez précis, moins vous polluerez votre base de données, plus elle a de chances d'être performante (sous réserve que son modèle soit correct !)

Finalement, votre procédure peut s'écrire :

Code :
1
2
3
4
5
6
7
8
9
10
11
12
CREATE PROCEDURE dbo.csp_CreateTest2
	@Fam varchar(5)
AS
BEGIN
	SET NOCOUNT ON
 
	INSERT	INTO dbo.temp_test (tt)
	SELECT	CAST(dataid AS varchar(5))
	FROM	dbo.filedata
	WHERE	lang_00 = @Fam 
	AND	descid = 3204
END
@++
__________________
En bases de données relationnelles SQL, il n'y a ni tableaux, ni enregistrements, ni champs: il y a des tables, des lignes et des colonnes.
Blog | Profil| Consulter ou télécharger les fichiers d'aide de SQL Server, des versions 2000 à 2012
elsuket est déconnecté   Envoyer un message privé Réponse avec citation 10
Vieux 14/01/2011, 12h26   #3
Membre éclairé
 
Avatar de tototiti2008
 
Inscription : octobre 2008
Messages : 327
Détails du profil
Informations forums :
Inscription : octobre 2008
Messages : 327
Points : 321
Points : 321
Bonjour elsuket,

Merci de cette réponse trés complète

Pour éviter les éventuels doublons dans mes réponses, et les null, je devrais peut-être faire une requête du type

Code :
1
2
3
4
    SELECT TOP 1    @Fam3204 = isnull(dataid, 0)
    FROM    filedata 
    WHERE    lang_00 = @Fam 
    AND    descid = 3204
?

En tout cas merci encore de vos précieux conseils
__________________
tototiti2008 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 14/01/2011, 13h15   #4
Modérateur

 
Avatar de elsuket
 
Homme Nicolas Souquet
Administrateur de base de données
Inscription : janvier 2005
Messages : 4 665
Détails du profil
Informations personnelles :
Nom : Homme Nicolas Souquet
Âge : 30
Localisation : Thaïlande

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

Informations forums :
Inscription : janvier 2005
Messages : 4 665
Points : 8 707
Points : 8 707
Héhé, ça c'est une solution de facilité qui vous posera plus de problèmes qu'autre chose

Vous pourriez traiter les doublons en amont.
Quelle est le traitement que vous effectuez et pourquoi avec-vous des doublons ou bien la colonne dataid à NULL ?

@++
__________________
En bases de données relationnelles SQL, il n'y a ni tableaux, ni enregistrements, ni champs: il y a des tables, des lignes et des colonnes.
Blog | Profil| Consulter ou télécharger les fichiers d'aide de SQL Server, des versions 2000 à 2012
elsuket est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 14/01/2011, 14h16   #5
Membre éclairé
 
Avatar de tototiti2008
 
Inscription : octobre 2008
Messages : 327
Détails du profil
Informations forums :
Inscription : octobre 2008
Messages : 327
Points : 321
Points : 321
Re,

Normalement il n'y a aucun doublon pour un descid et un lang_00 donné, le TOP 1 serait juste une mesure de "sécurité" (que je devrais coupler avec un tri, je pense)
dataid ne peut-être null que si la requête SELECT ne trouve aucun enregistrement correspondant... mais est-il vraiment NULL d'ailleurs, dans ce cas ?

peut-être comme ça ?

Code :
1
2
3
4
5
6
    SELECT TOP 1  @Fam3204 = dataid
    FROM    filedata 
    WHERE    lang_00 = @Fam 
    AND    descid = 3204
    ORDER BY dataid
Select @Fam3204  = isnull(@Fam3204, 0)
__________________
tototiti2008 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 17/01/2011, 17h52   #6
Membre éclairé
 
Avatar de tototiti2008
 
Inscription : octobre 2008
Messages : 327
Détails du profil
Informations forums :
Inscription : octobre 2008
Messages : 327
Points : 321
Points : 321
Bonjour à tous,

en tout cas, j'ai réussi à faire ma procédure grâce à vous alors merci et je considère le fil comme résolu

à bientôt
__________________
tototiti2008 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 18/01/2011, 08h46   #7
Modérateur

 
Avatar de elsuket
 
Homme Nicolas Souquet
Administrateur de base de données
Inscription : janvier 2005
Messages : 4 665
Détails du profil
Informations personnelles :
Nom : Homme Nicolas Souquet
Âge : 30
Localisation : Thaïlande

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

Informations forums :
Inscription : janvier 2005
Messages : 4 665
Points : 8 707
Points : 8 707
Citation:
le TOP 1 serait juste une mesure de "sécurité"
Justement non, il induira plutôt des erreurs dans vos données.
Lorsque vous vous en rendrez compte, il sera probablement déjà trop tard pour corriger le tir

Si vous ne voulez pas les NULL et que vous êtes certain de l'effet de votre requête (c'est à dire ce qu'elle signifie pour l'application que vous participez à écrire), il vous suffit de filtrer la clause SELECT de l'INSERT de la procédure stockée que je vous ai proposée :

Code :
1
2
3
4
5
6
INSERT INTO dbo.temp_test (tt)
SELECT CAST(dataid AS varchar(5))
FROM dbo.filedata
WHERE lang_00 = @Fam 
AND descid = 3204
AND dataid > 0
J'ai rajouté la dernière ligne. Comme NULL n'est pas une valeur, il ne vérifie pas le filtre.

Mais je continue de penser que si vous écrivez ce code par "mesure de sécurité", c'est qu'il y a quelque chose à revoir dans la façon dont les données arrivent dans la table dbo.filedata

@++
__________________
En bases de données relationnelles SQL, il n'y a ni tableaux, ni enregistrements, ni champs: il y a des tables, des lignes et des colonnes.
Blog | Profil| Consulter ou télécharger les fichiers d'aide de SQL Server, des versions 2000 à 2012
elsuket est déconnecté   Envoyer un message privé Réponse avec citation 10
Vieux 20/01/2011, 12h39   #8
Membre éclairé
 
Avatar de tototiti2008
 
Inscription : octobre 2008
Messages : 327
Détails du profil
Informations forums :
Inscription : octobre 2008
Messages : 327
Points : 321
Points : 321
Bonjour elsuket,

Merci, le mieux est que je fouille plus avant le fonctionnement actuel de l'application, ça permettra d'être sûr de la requête à réaliser pour être cohérent avec les données existantes
__________________
tototiti2008 est déconnecté   Envoyer un message privé Réponse avec citation 00
Réponse Proposer ce sujet en actualité Cette discussion est résolue.
Outils de la discussion



Fuseau horaire GMT +2. Il est actuellement 17h05.


 
 
 
 
Partenaires

Hébergement Web