8768 sujets

Développement web côté serveur, CMS

Bonjour à tous
Le problème est le suivant:
J'ai deux tables pi et w qui contiennent chacune 3 champs a, b, c
Si j'écris

SELECT
IF(pi.a IS NULL, w.a, pi.a) AS A,
IF(pi.b IS NULL, w.b, pi.b) AS B,
IF(pi.c IS NULL, w.c, pi.c) AS C,

ça marche très bien
Par contre ce que je voudrais obtenir c'est quelque chose comme

IF(pi.a IS NULL AND pi.b IS NULL AND pi.c IS NULL)
THEN 
    w.a AS A, w.b AS B, w.c AS C
ELSE
    pi.a AS A, pi.b AS B, pi.c AS C
END IF

Je crois comprendre que cette syntaxe (IF... THEN... ELSE... END IF) n'est pas utilisable telle quelle dans un SELECT
Quelle est la bonne syntaxe?
Merci de votre aide
Modérateur
Et l'eau,

if jamais utilisé. Mais par contre ton is null est une erreur de syntaxe. Ce n'est pas ça la syntaxe....


SELECT 
    un_autre_champ
FROM 
    une_table 
WHERE 
    ISNULL(un_champ);

*syntaxe mysql / postgres
Modifié par niuxe (06 Jul 2020 - 20:53)
SQL a été inventé dans les années 1970 et amélioré par la suite.
Pendant 20 ans on ne pouvait l'utiliser que dans les laboratoires d'IBM, car la quantité de mémoire et la puissance CPU nécessaires à son fonctionnement étaient trop importants pour qu'une entreprise puisse acquérir le matériel nécessaire.
A partir du moment où le coût des machines est devenu raisonnable, cette techno s'est rapidement généralisée.
A cette époque, je ne développais plus beaucoup moi même, je n'ai donc pas de pratique.
Je me fie à un bouquin qui a l'air d'être sérieux et reprend le cours de Chantal Gribaumont sur OpenClassroom et pour le peu que j'en ai à faire cela me suffit.
Je suppose qu'il doit exister plusieurs dialectes.
J'ai fini par contourner le problème et voici la requête qui marche (dans son contexte, donc c'est beaucoup plus long)

SELECT DISTINCT
	p.progID,
	p.title AS programTitle,
	p.plannedDate AS programDate,
	p.comments AS programComments,
	pi.rank AS rank,
	pi.authorID,
	pi.workID,
	IF(pi.title IS NULL, w.title, pi.title) AS title,
	w2.title AS mainWorkTitle,
	IF(pi.solo IS NULL AND pi.choir IS NULL AND pi.instrument IS NULL, w.solo, pi.solo) AS solo,
	IF(pi.solo IS NULL AND pi.choir IS NULL AND pi.instrument IS NULL, w.choir, pi.choir) AS choir,
	IF(pi.solo IS NULL AND pi.choir IS NULL AND pi.instrument IS NULL, w.instrument, pi.instrument) AS instrument,
	pi.comments AS comments,
	w.mvtNum
FROM ProgItems AS pi
LEFT JOIN Works AS w
	ON w.authorID = pi.authorID AND w.workID = pi.workID AND w.mvtID = pi.mvtID
LEFT OUTER JOIN Works as w2
	ON (w2.authorID = w.authorID AND w2.workID = w.workID AND w2.mvtNum = 0)
JOIN Programs AS p
	ON p.progID = pi.progID
WHERE
	pi.progID in ($PROGLIST)
ORDER BY p.plannedDate DESC, pi.rank
LIMIT 10000
;

On remarque que la syntaxe a bien l'air d'être "variable IS NULL". Je suppose que "ISNULL(variable)" doit aussi bien fonctionner, mais je trouve déjà assez pénible d'apprendre les bases d'un dialecte sans me familiariser avec les autres si je n'en ai pas besoin.
J'ai vu également dans ce bouquin p 343

IF condition
THEN instructions
[ELSEIF instructions2 THEN ...]
[ELSEIF instructions3 THEN ...]
[ELSE instructions]
END IF

mais cela ne doit fonctionner que dans un contexte particulier, il faudra que je regarde cela de près, j'espère que quelqu'un pourra me donner des instructions sur l'utilisation de cette syntaxe.
En attendant la syntaxe

IF(condition, variable1, variable2)

donne le résultat espéré.
Je trouve tout que même que répéter 3 fois la condition (une fois par variable) est un peu lourd, mais comme ça marche, je m'en contenterai.
Si j'ai besoin de faire ça pour 25 variables, il faudra que j'améliore mes compétences.
Modifié par PapyJP (06 Jul 2020 - 22:52)
PapyJP a écrit :
Bonjour à tous
Le problème est le suivant:
J'ai deux tables pi et w qui contiennent chacune 3 champs a, b, c
Si j'écris

SELECT
IF(pi.a IS NULL, w.a, pi.a) AS A,
IF(pi.b IS NULL, w.b, pi.b) AS B,
IF(pi.c IS NULL, w.c, pi.c) AS C,

ça marche très bien
Par contre ce que je voudrais obtenir c'est quelque chose comme

IF(pi.a IS NULL AND pi.b IS NULL AND pi.c IS NULL)
THEN 
    w.a AS A, w.b AS B, w.c AS C
ELSE
    pi.a AS A, pi.b AS B, pi.c AS C
END IF

Je crois comprendre que cette syntaxe (IF... THEN... ELSE... END IF) n'est pas utilisable telle quelle dans un SELECT
Quelle est la bonne syntaxe?
Merci de votre aide

SQL est (très) loin d'être ma tasse de thé mais une construction de type CASE ne pourrait-elle être utilisée ?
CASE a 
       WHEN 1 THEN 'un'
       WHEN 2 THEN 'deux'
       WHEN 3 THEN 'trois'
       ELSE 'autre'
END

Juste une suggestion... pas une certitude.
Salut,

Si je comprends bien ton besoin, tu voudrais récupérer les enregistrements d'une table si ceux-ci n'existent pas dans une autre table, les 2 tables possédant une relation. Pour moi c'est la définition d'une jointure gauche.

Ensuite, tu voudrais combiner les champs de 2 tables, ces champs étant identiques. C'est la définition d'une union.

Donc pour moi, la solution passe par une sous requête d'union entre la table ProgItems et la table Works sur les champs solo, choir et instrument. Dans la table ProgItems, seuls les enregistrements complets nous intéressent, donc il faut filtrer sur les champs non null. Sur la table Works, seuls les champs qui n'existent pas dans ProgItems nous intéressent, donc il faut ajouter une jointure gauche en filtrant sur l'absence de solo, choir et instrument dans la table ProgItems.

Cela donnerait quelque chose comme ça :

SELECT DISTINCT ...
FROM (
    SELECT solo, choir, instrument
    FROM ProgItems
    WHERE solo IS NOT NULL
    AND choir IS NOT NULL
    AND instrument IS NOT NULL
  UNION
    SELECT w.solo, w.choir, w.instrument
    FROM Works w
    LEFT JOIN ProgItems pi ON w.authorID = pi.authorID 
        AND w.workID = pi.workID AND w.mvtID = pi.mvtID
    WHERE pi.solo IS NULL
    AND pi.choir IS NULL
    AND pi.instrument IS NULL
) AS items
LEFT JOIN Works w2 ...
Merci pour vos réponses, mais elles ne répondent pas directement à mon problème, je me suis certainement mal exprimé.
Comme j’ai une solution (même si elle me paraît bien lourde) il n’y a pas urgence. Je vais me replonger dans la doc et si je trouve une réponse je vous en ferai part.
Exprimer ton problème pour qu'il soit mieux compris en prenant du recul par rapport au SQL, te permettrait peut-être d'y voir plus clair aussi.
Hmm! Mon problème est justement un problème de compréhension du langage, difficile d'en faire abstraction Smiley smile
Après avoir relu le chapitre 32 je crois comprendre que la structure IF...THEN...ELSE ... END IF ne s'utilise que dans les procédures stockées, donc ce n'est pas approprié à ce que je voulais en faire.

Alors reprenons le problème autrement
Pour autant que j'ai pu comprendre, une requête c'est

SELECT
une série de paramètres (colonnes ou calcul sur les colonnes)
FROM
une table, éventuellement jointe à d'autres
WHERE
des conditions permettant de choisir les lignes à sélectionner
(ORDER BY ... LIMIT...) optionnels
;


Mon problème consiste la liste des paramètres.
Pour chaque paramètre, on peut utiliser la fonction IF(condition, résultat1, résultat2) pour calculer un paramètre, par exemple

IF(pi.title IS NULL, w.title, pi.title) AS title

C'est à dire: s'il la colonne "title" de la table pi est nulle, prenez la colonne "tite" de la table w, sinon prenez la colonne "title" de la table pi.
Cela permet de choisir un paramètre ou un autre en fonction d'une condition.

Ce que je voudrais savoir c'est comment dire
"si telle condition prenez telle LISTE paramètres sinon prenez en une autre"

J'ai bêtement mis la même condition dans autant de lignes que de paramètres et ça marche, mais j'avais le sentiment, après avoir utilisé une cinquantaine au moins de langages informatiques en autant d'années d'expérience, qu'il devrait y avoir une autre solution.
Je pense que tu as du mal à te détacher du langage car tu pars du principe que la solution passe forcément par l'utilisation d'une structure conditionnelle. Tu es donc emprisonné dans cette construction que tu essayes de tordre. En exprimant le besoin fonctionnel, tu n'es plus contraint par une limitation technique et la solution prend une toute autre forme.

Il me semble que ma solution répond à ton besoin. On récupère bien une liste ou une autre en fonction des conditions. Pourquoi ne te convient-elle pas ?
Je te prie de m'excuser, ta solution me semble excellente et j'avais sauté par dessus à pieds joints. Je répondais aux explications sur WHERE et CASE.
Je teste cela demain et je vais certainement l'adopter.
Ce que je comprends des réponses que j'ai reçues, c'est qu'on ne peut pas faire de conditionnel comme je l'imaginais à partir de la lecture du bouquin. Savoir que c'est une voie sans issue est important, savoir comment faire pour obtenir le résultat autrement l'est encore plus.
Modifié par PapyJP (07 Jul 2020 - 19:57)
J'aurais mieux fait de m'en tenir à ce que j'avais décidé: attendre ce matin pour étudier cette solution. Je me serais couché plus tôt et j'aurais moins mal dormi
Je ne doute pas que cette solution fonctionne mais je ne sais pas la faire fonctionner.
Mon objectif est d'obtenir 15 paramètres sur un nombre conséquent de lignes, dépendant du ou des programmes dans la liste désignée par $PROGLIST

SELECT DISTINCT
	p.progID,
	p.title AS programTitle,
	p.plannedDate AS programDate,
	p.comments AS programComments,
	pi.rank AS rank,
	pi.authorID,
	pi.workID,
	IF(pi.title IS NULL, w.title, pi.title) AS title,
	w2.title AS mainWorkTitle,
	/* lignes à remplacer
        IF(pi.solo IS NULL AND pi.choir IS NULL AND pi.instrument IS NULL, w.solo, pi.solo) AS solo,
	IF(pi.solo IS NULL AND pi.choir IS NULL AND pi.instrument IS NULL, w.choir, pi.choir) AS choir,
	IF(pi.solo IS NULL AND pi.choir IS NULL AND pi.instrument IS NULL, w.instrument, pi.instrument) AS instrument,
       */
	pi.comments AS comments,
	w.mvtNum
FROM ProgItems AS pi
LEFT JOIN Works AS w
	ON w.authorID = pi.authorID AND w.workID = pi.workID AND w.mvtID = pi.mvtID
LEFT OUTER JOIN Works as w2
	ON (w2.authorID = w.authorID AND w2.workID = w.workID AND w2.mvtNum = 0)
JOIN Programs AS p
	ON p.progID = pi.progID
WHERE
	pi.progID in ($PROGLIST)
ORDER BY p.plannedDate DESC, pi.rank
LIMIT 10000
;

Comment faire pour "greffer" cette solution à base d'une UNION entre 2 requêtes pour que les paramètres solo, choir et instrument soient positionnés parmi les autres (ce peut être à la fin, l'ordre ne sert qu'à faciliter la lecture du code) ?

Toutes mes recherches sur l'utilisation de UNION ne donnent que des exemple où la requête n'est constituée que du résultat des 2 (ou plus) requêtes, la condition étant que le nombre de paramètres soit le même dans chacune des requêtes.
C'est du reste pourquoi je ne me suis pas penché sur cette méthode quand j'ai recherché comment obtenir les résultats désirés.
Ce n'était pas mon intention de t'empêcher de dormir. Smiley lol

Si tu regardes mon exemple, j'utilise une sous requête qui contient l'union. Le résultat se comporte comme une table, que j'ai appelée items ici. Cette sous requête peut être ajoutée à ta requête principale comme une table classique. On pourrait même la mettre dans une vue pour alléger la requête principale.

En revanche, je m'aperçois qu'il manque effectivement des éléments pour faire la jointure avec les autres tables. Il faudrait donc ajouter les champs authorID, workID et mvtID dans les requêtes de l'union pour les utiliser en jointure avec ProgItems.

Le principe d'une UNION est de fusionner plusieurs tables qui possèdent les mêmes champs. Le résultat est une table qui contient les champs sélectionnés. Donc dans l'exemple, tu peux y accéder à l'aide de items.solo, items.choir et items.instrument.

Cela donnerait quelque chose comme ça :

SELECT DISTINCT p.progID,
	p.title AS programTitle,
	items.solo, items.choir, items.instrument,
	...
FROM ProgItems AS pi
LEFT JOIN (
    SELECT authorID, workId, mvtID, solo, choir, instrument
    FROM ProgItems
    WHERE solo IS NOT NULL
    AND choir IS NOT NULL
    AND instrument IS NOT NULL
  UNION
    SELECT w.authorID, w.workId, w.mvtID, w.solo, w.choir, w.instrument
    FROM Works w
    LEFT JOIN ProgItems pi ON w.authorID = pi.authorID 
        AND w.workID = pi.workID AND w.mvtID = pi.mvtID
    WHERE pi.solo IS NULL
    AND pi.choir IS NULL
    AND pi.instrument IS NULL
) AS items ON items.authorID = pi.authorID AND items.workId = pi.workID AND items.mvtID = pi.mvtID
LEFT JOIN Works w2 ...

Merci de ta réponse qui éclaire ma journée comme un rayon de soleil Smiley biggrin

D'abord parce que cela m'apprend des choses que j'ignorais sur l'utilisation des sous-requêtes, ensuite parce que j'avais effectivement deviné à partir de ta réponse que la sous-requête générait une table temporaire, mais je ne voyais pas comment lui faire une jointure compte tenu des champs que cette table possédait, que c'est cela qui m'empêchait de dormir.
Je ne suis donc pas encore trop gâteux pour apprendre des choses dans un domaine proche de ceux qui ont occupé ma vie professionnelle.

A voir la complexité de la chose, il est peu vraisemblable que j'applique cette solution. Ma requête est déjà assez complexe comme ça, je préfère garder mes 3 IF. Par contre je garde cette approche pour d'autres requêtes si nécessaire.
Jean-Pierre-Bruneau a écrit :
Bonjour l'Ami qui as mal dormi Smiley confused
est-ce que ton but réel est de récupérer les seules valeurs distinctes dans une liste de colonnes spécifiée.
(je veux dire est-ce qu'il s'agit dans la complexité de ta demande, la piste dominante ?

Merci de ta sollicitude Smiley biggrin
En fait ma requête est un peu lourde et j'espérais la simplifier, me disant que ma connaissance très élémentaire du SQL devait m'avoir fait passer par un chemin cahoteux alors qu'il existait peut être une autoroute à proximité, ce qui n'a pas l'air d'être le cas.

Je n'ai aucune intention de devenir expert en SQL, c'est simplement parce que je n'ai pas vraiment le choix que je me résous à utiliser une machine outil pour enfoncer un clou.
Pour mes besoins actuels, cela me suffit.