Mes questions sur les Procédures stockées [VB/TSQL/SQL-Server] - SQL/NoSQL - Programmation
Marsh Posté le 18-05-2007 à 01:12:56
Ouh là là...
Non, ça fait absolument pas ce que tu dis que ça va faire...
Là, tu viens écraser @identifiant passé en paramètre "IN" (donc en lecture seule) avec une valeur. Ceci est absolument pas bon du tout, je suis étonné que t'arrive même à compiler ou lancer ta fonction.
Pour @champ1 et @champ2, étant déclarés localement, ça va jamais les retourner !
Bouge pas, je teste un truc et je poste un truc qui marche (de toute façon, vu l'heure, tu dois pioncer ferme, tu risques pas de bouger )
Marsh Posté le 18-05-2007 à 01:25:06
Le procédure correcte (avec jeu de test complet) :
Code :
|
Et le code VBScript : ('tain j'en ai chié grave, j'ai plus l'habitude de faire du VBS moi )
Code :
|
Marsh Posté le 18-05-2007 à 02:04:01
ok merci, c pas facile de faire des procédures stockées quand on y a pas été formé
Les print c juste pour de l'affichage ?
EDIT : je teste çà lundi, pour l'instant c'est le week-end
Marsh Posté le 18-05-2007 à 02:08:45
-- tiens, tu dormais pas --
je remonte le topic pour afficher mes corrections
oui, print ça fait juste de l'affichage, c'est pour le test dans sql query editor. toi tout ce qui t'intéresse, c'est la partie entre "create procedure" et "end"
pour plus d'info sur la manipulation des objets parameter et command, va sur ce site (ça va faire 10 ans que je m'en sert au moins une fois par moi )
http://www.devguru.com <- section "ADO"
Marsh Posté le 18-05-2007 à 15:35:46
Merci pour tes réponses, je pense qu'avec tout ça j'ai tout ce qu'il me faut pour pouvoir m'en sortir lundi. Sinon c pas du VBS que j'utilise c du VB (bien que je connaisse pas vraiment les différences) ...
Marsh Posté le 20-05-2007 à 20:43:27
sa procédure est correcte, du moins ça fonctionne avec SQL SERVER 2005...
On peut acraser une variable passé en paramètre. Aucun problème à ce niveau la. Elles sont passé par copie de toute façon (IN).
Pour ce qui est de rembaler la valeur, en SQL SERVER 2005 suffit de faire un RETURN de ta variable locale.
Même si on est dans une procédure, ça marche, mais faut exécuter un executeScalar (avec C# par exemple) pour correctement la récupérer.
Ce que je dis est valable pour SQL SERVER 2005, je n'ai jms bossé avec les versions antérieurs.
Marsh Posté le 21-05-2007 à 09:34:14
moi23372 a écrit : sa procédure est correcte, du moins ça fonctionne avec SQL SERVER 2005... |
Le souci, c'est que sans "return", le paramètre ne prends pas la valeur modifiée. Car comme tu dis, elle est passée en copie et non en référence. Du coup, on arrive à un comportement inatendu. C'est le même qu'en VB d'ailleurs, où les paramètres en entrée ne sont pas en lecture seule. En ADA par exemple, c'est protégé : tout en ce qui passé en "in" est en lecture seule. En C# (et VB.NET) il me que c'est aussi protégé. Ou il doit y avoir un warning à la compilation.
Enfin bref, il vaut mieu éviter de venir écraser une variable passée en paramète sans le mot-clé "ouput", car le comportement risque d'être inatendu, surtout pour une personne habituée à traîter ces paramètres comme lecture seule.
Pour ce qui est de faire un "return" sur une "procedure", je trouve ça goret aussi, de la même façon. Une procédure ne retourne sémantiquement rien. Autant utiliser comme dans mon exemple une série de variables "output". C'est plus propre.
Marsh Posté le 21-05-2007 à 09:36:44
Sinon, entre VBS et VB, pas de différences majeures.
- Utilisation de "CreateObject" en VBS : En VB, même si ça marche, on préférera lier les bibliothèques au projet
- Utilisation de variables non typées en VBS : En VB, on peut typer les variables. En VBS c'est impossible. Utilise donc le typage.
Pour le reste, là y'a rien qui vient. Si, byref qui n'existe pas en VBS, ainsi que d'autres trucs. M'enfin bref, copier/coller de mon code dans VB6 doit marcher. Par contre, je t'invite à le rendre un peu plus propre et conforme à VB6
Marsh Posté le 21-05-2007 à 10:47:02
Merci pour votre aide je pense que ça ira pour ma procédure
Maintenant je me bats avec Visual Studio pour faire un 1er test unitaire ... Je trouve pas le moyen de simuler un appel de mon module ActiveX avec les paramètres et tout ...
Marsh Posté le 21-05-2007 à 14:40:52
Petite question sur les lignes 8 à 10 de ta procédure stockée MagicBuzz :
Les variables sont certes en OUTPUT mais elles sont déclarées comme paramètres de la procédure, et donc à son appel elle s'attend à recevoir ces paramètres, alors que je ne lui envoie que l'identifiant ...
En fait j'ai pas encore bien saisi les syntaxes possibles pour les paramètres que la procédure stockée reçoit et ceux qu'elle retourne.
Marsh Posté le 21-05-2007 à 17:25:53
C'est tout bon, mon programme VB et ma procédure stockée fonctionnent de bout en bout.
Merci encore pour votre aide
EDIT : Voilà ce que j'ai fait au final :
|
Finalement c'était d'une simplicité affligente ... Mais ça me permet de faire tout ce que je voulais : si l'identifiant n'est pas trouvé je fais un test sur RecordSet.EOF dans mon programme VB et si c'est à TRUE ça veut dire qu'il n'a rien trouvé. Simple comme bonjour ...
Mais forcément quand on débarque et qu'on voit plein d'exemples partout on cherche à faire pareil alors qu'au final c'est beaucoup plus simple qu'on ne le pense
Marsh Posté le 22-05-2007 à 09:34:42
Sauf que là, c'est sale...
Y'a que SQL Server qui ramène les lignes retrouvées à l'intérieur d'une PS. De plus, si demain la PS change pour faire un appel à une fonction par exemple, qui ne retourne pas le lignes, t'es baisé. Tout comme si demain elle exécute deux requêtes de suite pour faire une vérification supplémentaire.
Regarde mon exemple. Je colle des paramètres pour les output, et je les relis. Un bête copier/coller de mon code FONCTIONNE sous VB (t'as juste à adapter la connexion string).
Base-toi sur cet exemple, c'est la syntaxe propre, qui te garanti une compatibilité avec n'importe quelle version de SQL Server ou autre SGBD.
Marsh Posté le 22-05-2007 à 10:44:11
Pour le code VB j'ai dû utiliser des fonctions déjà existantes pour l'appel de ma PS et j'avais un problème avec les paramètres en OUTPUT pour lesquels on me demandait des valeurs en INPUT, ce dont je n'ai pas besoin ... J'aurais aimé mettre en place une procédure sur le modèle de la tienne mais comme je suis sur une mission relativement courte et qu'il y a encore pas mal de boulot derrière je reste sur cette solution pour l'instant. Si jamais par la suite j'arrive à mieux comprendre ces histoires d'appels de PS et que j'ai le temps de m'y pencher un plus, j'hésiterai pas à en changer
Merci encore pour le temps que tu as consacré à mon problème : même si je n'ai pas repris ton code, ça m'a permis de piger quelques petits trucs qui m'ont bien aidé
Marsh Posté le 22-05-2007 à 14:07:57
Y'a pas de valeur INPUT pour un paramètre OUTPUT.
Par contre, comme dans mon exemple, il faut les binder avec des PARAMETER, qui vont prendre les valeurs de sortie.
Suffit de lire mon code. Les paramètres p2, p3 et p3, je leur affecte jamais de .Value, par contre une fois la PS appelée, c'est eux que je vais lire pour voir ce que m'a retourné la procédure...
Marsh Posté le 30-05-2007 à 14:35:02
Je reprends mon topic histoire de pas en créer plein ...
Toujours concernant les procédures stockées, je veux créer la mienne en me basant sur d'autres PS déjà existantes, et dans l'une d'elles j'ai ça :
set @query = 'SELECT DISTINCT H.NOIDPR, H.NOIDLE, |
Ce qui me dérange là dedans, c toutes ces simples-quotes qui s'enchaînent ... Moi je suis habitué au principe du \' mais là je vois pas trop ce que je vais avoir au final dans ma variable @query ... J'arrive pas à saisir la logique de lecture ...
help plz, expliquez-moi comment je dois lire ça
Marsh Posté le 31-05-2007 à 11:16:46
Aujourd'hui nouveau problème (en plus de celui d'hier sur les simples-quotes pour lequel j'attend toujorus une réponse )
Toujours en TSQL dans une procédure stockée :
Code :
|
L'erreur qu'il me sort c'est : Syntaxe incorrecte vers le mot clé 'CASE'.
Quand je mets SELECT CASE, alors il me dit : Syntaxe incorrecte vers le mot clé 'CASE'. (mais pour celui qui est 6 lignes plus bas ... )
Quand je mets SELECT CASE 1, alors il me dit : syntaxe incorrecte vers '='.
Bref je voudrais savoir comment faire un CASE, sachant que dans chacun de mes WHEN il y a une requête différente à exécuter. ... oui, une belle question de noob, mais quand on connaît pas la syntaxe
Marsh Posté le 31-05-2007 à 11:54:27
Kirvel a écrit : Je reprends mon topic histoire de pas en créer plein... |
Le forum est blindé de topic (donc 80% sans aucun intérêt ni pour celui qui pose la question ni pour ceux qui y répondent) mais pourtant il est bien plus aisé de suivre l'évolution du forum en se référant aux nouveaux topics plutôt que de voir un topic déterré refaire surface : de nombreuses personnes ne vont même pas le lire, en se disant "encore un qui sait pas lire et qui veut qu'on lui réexplique ce qu'on a passé 10 pages à expliquer..."
Bon, je m'en retourne lire ton problème
Marsh Posté le 31-05-2007 à 11:59:41
Kirvel a écrit : Ce qui me dérange là dedans, c toutes ces simples-quotes qui s'enchaînent ... Moi je suis habitué au principe du \' mais là je vois pas trop ce que je vais avoir au final dans ma variable @query ... J'arrive pas à saisir la logique de lecture ... |
D'ailleurs, ça confirme ce que je disais... J'ai passé la soirée hier et ce matin à passer sur le forum, et j'avais pas vu cette question
La norme SQL est différente de la norme ANSI en ce qui concerne l'échappement des caractères.
Certains SGBD supportent les deux (Oracle, MySQL, etc.) mais d'autre ne supportent que le SQL Strict (SQL Server, Access, etc.)
La norme SQL est très simple, c'est la même qui a été adoptée en Visual Basic par exemple : j'ai un caractère à la con ? Je l'échappe en le doublant :
pour écrire un ' dans une chaîne de caractère, je le remplace par ''.
pour écrire un % dans un LIKE, je le remplace par %% etc.
Ce qui donne donc :
set @query = 'SELECT DISTINCT H.NOIDPR, H.NOIDLE, H.CORISQ, ''' + @xx + ''', ''G1'' from ' + @xx + '..G1_ID_DECOMPTE H'
Avec @xx = "toto" :
select distinct h.noidpr, h.noidle, h.corisq, 'toto', 'G1' from toto..g1_id_decompte h
(j'imagine que @xx est le nom d'une base de données non ?)
Marsh Posté le 31-05-2007 à 12:04:03
Kirvel a écrit : L'erreur qu'il me sort c'est : Syntaxe incorrecte vers le mot clé 'CASE'. |
CASE attend une valeur pour effectuer les tests.
Idem, WHEN attends des constantes
C'est exactement comme le switch en C :
Code :
|
Ca se traduit en T-SQL :
Code :
|
EDIT :
J'ai dit une connerie
D'après la doc :
Code :
|
Donc ta syntaxe devrait marcher...
En fait, trouvé :
Code :
|
=> Marche pas
Mais...
Code :
|
=> Marche. Il faut mettre ta requête entre () pour ça ça marche, pour faire comprendre à l'interpréteur que c'est ton select qui doit être mis en résultat.
Effectivement, à l'exécution, il va retenir cette requête au final :
Code :
|
Si on ne met pas les (), il se dit "ouh là là, deux select de suite, y va pas bien le gars"
Pour en revenir à ton cas, pourquoi tu utilises CASE ? On dirait que tu n'as qu'un cas... Utilise un IF à la place, et tu pourras faire tout ce que tu veux comme tests
IF <tests>
BEGIN
-- Trucs à faire quand les tests vont bien
END
ELSE
BEGIN
-- Trucs à faire quand ça foire
END
Marsh Posté le 31-05-2007 à 12:17:19
@xx est bien un nom de base de données
pour l'histoire de l'échappement j'étais resté sur '' = \' . Donc c'est un peu ça finalement
Sinon le case que j'ai mis plus haut je n'ai mis qu'1 seul WHEN pour pas spammer la page, parce qu'en fait j'en ai 18 des requêtes dans mon CASE
Sinon je viens de passer pas mal de temps à me renseigner sur le mot clé CASE, et apparemment en TSQL on peut pas l'utiliser en dehors d'une requête, et il faudrait donc que je me rabatte sur du IF/ELSEIF ... Mais ça me parait bizarre quand même, c'est quand même bien pratique un CASE
Je verrai ça cet après-midi, c'est l'heure de manger
Marsh Posté le 31-05-2007 à 12:18:37
PS : Le CASE ne peut apparement s'utiliser que dans une requête contrairement à IF qui ne peut s'utilise que comme statement T-SQL.
Marsh Posté le 31-05-2007 à 12:19:48
Kirvel a écrit : @xx est bien un nom de base de données |
c'est même complètement ça
Marsh Posté le 31-05-2007 à 12:20:53
Kirvel a écrit : @xx est bien un nom de base de données |
ben...
select *
from (case ...) as t
=> Ca va faire ce que tu veux
PS : Et relis mon post d'avant je l'ai complèté et corrigé
Marsh Posté le 31-05-2007 à 12:24:21
Je viens de lire ton EDIT, les parenthèses ont l'air de fonctionner mais maintenant il me dit :
syntaxe incorrecte vers '.' à la ligne : @xx..G1_DESTINATAIRE AS Dest
Là je comprends vraiment pas parce que c'est bien comme ça qu'on désigne une base de donnée ... à moins que ça soit la variable @xx qui le dérange ...
Marsh Posté le 31-05-2007 à 12:54:24
il faut la construire comme dans ta question précédente, et utiliser alors un exec.
Marsh Posté le 31-05-2007 à 13:44:14
MagicBuzz a écrit : il faut la construire comme dans ta question précédente, et utiliser alors un exec. |
C'est ce que j'avais fait au départ ... Je faisais mon SELECT FROM, puis je construisais mon WHERE au fur et à mesure dans une chaîne, mais par la suite j'ai discuté avec un mec du service BdD et le 1er truc qu'il m'a dit c'est : "Qui c'est qui a codé ce charabia ?"
En gros il me dit qu'en construisant la requête on est obligé de "recompiler" la requête à chaque fois qu'on l'appelle car elle sera quasi-toujours différente et que niveau performance c'est pas ce qu'il y a de mieux car on perd l'avantage de l'utilisation d'une procédure stockée. C'est lui qui m'a conseillé de faire plusieurs requêtes différentes mais invariables et que l'on appelle en fonction des paramètres envoyés. Comme ça, une fois la requête appelée une première fois, plus besoin de la "recompiler" et gain de performance.
NB. : Je dis recompiler mais je ne sais plus le terme exact qu'il m'a donné. C'était une histoire de plan de charge je crois ... Bref, l'équivalent des DECLARE, PREPARE, OPEN, EXECUTE, CLOSE en Oracle.
EDIT : Je vais faire ça avec des IF/ELSEIF et ça devrait aller ... mais je ne crois pas que ça résoudra le problème de mon post précédent sur la variable @xx
Marsh Posté le 31-05-2007 à 14:10:40
Je suis 100% d'accord avec ce que ton pote à dit.
Donc soit tu fais un EXEC tout pourri et tu peux utiliser une variable comme nom de base, soit tu fais autant de requêtes qu'il y a de base, et tu lances la bonne requête en fonction de ta variable.
Dans tous les cas, je pige pas trop pourquoi t'as 36 bases avec visiblement un modèle identique... A mon avis, c'est les tables qu'il faut repenser, afin d'inclure un champ discriminent qui permet d'éviter d'avoir 36 bases de données.
T'es chez Econocom avec François Roché ou quoi ? (le gars qui utilise Access, fait "enregistrer" => "voulez-vous créer un champ identifiant dans votre table ?" et qui te sors ça comme seul argument pour dire qu'il faut mettre des numéroauto en pk dans toutes les tables plutôt que d'utiliser des pk propres ou des clés composites -et à cause de ça, se retrouver avec 50 bases de données sur le même serveur, parcequ'il ne voulait pas rajouter un champ "codsoc" permettant d'identifier au niveau de la PK dans quelle société on travaillait ) <- ça m'a traumatisé.
Marsh Posté le 31-05-2007 à 14:28:38
En fait le principe des différentes bases c'est que chacune correspond à un mois comptable :
la base 0 c'est le mois en cours, la base 1 c'est janvier 2006, base 2 février 2007, etc le tout sur 2 ans (ou 4), et quand on arrive à une fin de mois comptable on écrase la base la plus ancienne avec la base 0 et on repart en base 0 avec le nouveau mois comptable ...
L'architecture est telle qu'elle est depuis certainement plus longtemps que je ne suis dans le monde du travail, donc c pas un intérimaire qui n'est là que depuis 3 semaines comme moi qui va la refaire.
Sinon en gros tu me dis : soit j'utilise un "exec @query" et je peux utiliser ma variable @xx, soit je fais mon IF/ELSEIF et faut que je réécrive mes 18 requêtes autant de fois que j'ai de bases ?
Si c'est ça je laisse tomber direct et je reprends mon code initial qui tenait en 30 lignes ... Surtout que pour le faire ce code je m'étais déjà inspiré de PS existantes et qui elles non plus n'étaient pas optimisées niveau performance ! Certes je ne connais pas leur contexte d'utilisation mais bon ... pas moyen que j'écrive 48*18 fois la même requête avec à chaque fois 2 caractères qui changent
Marsh Posté le 31-05-2007 à 14:31:37
Bienvenue dans un système mis en place par un écervelé qui se croyait certainement très fort...
Pis alors 18 mois d'historique (si j'ai bien compris) ça c'est bien total n'importe quoi...
Marsh Posté le 31-05-2007 à 14:35:00
Non non c 2 ou 4 ans d'historique !
18 c le nombre de clauses WHERE possibles
Quant à la taille de l'historique, ça ne me parait pas aberrant ... tout dépend du métier
Marsh Posté le 31-05-2007 à 14:36:55
Kirvel a écrit : Sinon en gros tu me dis : soit j'utilise un "exec @query" et je peux utiliser ma variable @xx, soit je fais mon IF/ELSEIF et faut que je réécrive mes 18 requêtes autant de fois que j'ai de bases ? |
Alors ? C'est bien ça que tu me dis ? Tu es sûr qu'il n'y a pas d'autre moyen ?
Marsh Posté le 31-05-2007 à 15:26:06
J'ai demandé conseil à la personne du service BdD qui m'a fait la remarque hier sur les performances, et voici sa réponse :
Citation : OK, tu fais accès aux différentes bases de la |
Ca fait plaisir de perdre toute 1 journée dans le zeff sur une mission courte d'interim déjà pas évidente et en fonction de laquelle je peux avoir un CDI si ça se passe bien ...
M'enfin bon ... j'ai appris des trucs, j'ai pas complètement perdu mon temps non plus
Fin du débat, je repasserai sûrement par là si je rencontre d'autres problèmes. Merci à toi MagicBuzz
Spoiler : un rapport avec Magic Bus ? |
Marsh Posté le 31-05-2007 à 16:35:35
Et le nouveau problème n'a pas tardé à se montrer ! Voici ma PS initiale :
Code :
|
et voilà l'erreur qu'il me sort :
Citation : Erreur de syntaxe lors de la conversion de la valeur varchar ' SELECT |
C'est quoi cette erreur ? Un problème de concaténation ou alors ma requête qui est foireuse quelque part ? (genre dans la construction du WHERE, ce qui ne m'étonnerait pas)
Marsh Posté le 31-05-2007 à 16:40:14
euh... isole le bout de code qui merde, parceque là, c'est pas possible de trouver d'où ça vient comme ça...
duplique la PS.
change en @query = 'select 1'
et rajoute des petits bouts au fur et à mesure jusqu'à ce que tu produise l'erreur...
Marsh Posté le 31-05-2007 à 16:46:07
ReplyMarsh Posté le 31-05-2007 à 16:52:43
ReplyMarsh Posté le 31-05-2007 à 17:03:06
Apparemment ça vient de la ligne 64 ... Je comprends pas trop le problème mais je vais creuser un peu, merci pour l'astuce.
Marsh Posté le 31-05-2007 à 17:07:08
et voilà ... un test entre un VARCHAR(9) et un INT ...
Oracle ça le dérangeait pas ça ...
Marsh Posté le 16-05-2007 à 16:05:55
EDIT : Histoire de pas blinder le forum de plein de topics différents, je vais poser toutes mes questions ici. Le problème évoqué dans le 1er post est résolu, merci de vous rendre à la fin du topic pour connaître mon problème actuel, merci d'avance
Salut à tous
Je travaille sur une courte mission et je débarque totalement dans ce domaine, donc j'aimeraiss quelques conseils de spécialistes
Voici ma procédure stockée :
CREATE PROCEDURE MaProcedure
(
@identifiant char(9)
)
AS
DECLARE
@champ1 char(4),
@champ2 char(2)
SELECT
@identifiant = identifiant
@champ1 = champ1
@champ2 = champ2
FROM
MaTable
WHERE
@identifiant = identifiant
If @@Rowcount < 1
SELECT
@identifiant = 0
GO
Appel de la procédure en VB :
Set Rs = SQL_ExecuteReqPS(Obj, PBL_Conn, "MaProcedure", colAller, , , 0)
Le but de cette procédure est simple : on cherche un enregistrement unique dans une table via l'identifiant passé en paramètre; si on le trouve la procédure retourne l'identifant et les champs 1 et 2, et si on ne le trouve pas on retourne juste l'identifant avec pour valeur "0"
Maintenant pour les questions :
La structure et la syntaxe vous semblent-elles correctes ? (je peux pas encore vraiment tester)(résolu)Plz help
Message édité par Kirvel le 31-05-2007 à 11:04:44
---------------
MyAnimeList