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

Requêtes MySQL Discussion :

Trouver les lignes qui vérifient des valeurs différentes pour une même colonne, sans OR


Sujet :

Requêtes MySQL

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Février 2008
    Messages
    59
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2008
    Messages : 59
    Par défaut Trouver les lignes qui vérifient des valeurs différentes pour une même colonne, sans OR
    Bonjour à tous,

    C'est un truc qui me fait toujours taper la tête contre les murs et qui doit être simple une fois qu'on l'a fait une fois !

    Mon souci est plus parlant par un exemple :

    Soit une table "annonces_specific_fields" composée comme çà :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    id | annonce_id | specific_field_id | real_value
    Disons que j'aimerais faire la requête suivante sans OR, mais avec des AND :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    SELECT DISTINCT(annonce_id) FROM annonces_specific_fields
    WHERE specific_field_id = X AND real_value <= Y
    OR (specific_field_id = Z AND real_value = "toto")
    OR (specific_field_id = W AND (real_value BETWEEN t AND u))
    En fait, trouver les lignes qui valident toutes ces conditions et non pas au moins une.
    Bien entendu, si je remplace les OR par des AND, çà ne va pas puisque aucune ligne ne validera plusieurs valeurs pour le champ real_value.

    J'avais quelque chose qui marchait presque avec une clause EXISTS.

    Voici le code avec des valeurs réelles :
    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
     
    SELECT distinct(annonce_id) FROM annonces_specific_fields AS spec 
    WHERE specific_field_id IN 
    ( 
    SELECT id FROM specific_fields WHERE slug IN ('surface','pieces') 
    ) 
     
    AND `spec`.`real_value` <= 50 
     
    AND exists 
    (
    SELECT annonce_id FROM annonces_specific_fields 
    WHERE (real_value = "2") 
    AND `spec`.`annonce_id` = `annonces_specific_fields`.`annonce_id`
    )
    Mais le client a trouvé un cas où çà ne marche pas !

    Suis-je sur la bonne voie avec cette clause EXISTS() et si oui où est mon erreur ?
    Ou bien y a-t-il une autre façon de faire ?

    Par avance merci pour vos éclairages !

  2. #2
    Membre averti
    Profil pro
    Inscrit en
    Février 2008
    Messages
    59
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2008
    Messages : 59
    Par défaut
    Bon, j'ai trouvé une solution qui semble fonctionner, mais je ne suis pas sûr qu'elle soit optimale, à cause du produit cartésien au début et de la requête imbriquée "dupliquée"...

    Quand je fais un EXPLAIN, même si j'ai toujours du mal à en décrypter les infos, il me semble que c'est pas génial...

    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
     
    SELECT distinct(spec.annonce_id) from annonces_specific_fields AS spec,
    annonces_specific_fields AS spec2
    WHERE (spec.specific_field_id 
        IN (
            SELECT id FROM specific_fields
            WHERE slug = 'pieces'
        )
        AND spec.real_value = 2
    )
    AND (spec2.specific_field_id 
        IN (
            SELECT id FROM specific_fields
            WHERE slug = 'surface'
        )
        AND spec2.real_value <= 50
    )
    AND spec.annonce_id = spec2.annonce_id
    Résultats du Explain
    Images attachées Images attachées  

  3. #3
    Expert éminent
    Avatar de CinePhil
    Homme Profil pro
    Ingénieur d'études en informatique
    Inscrit en
    Août 2006
    Messages
    16 818
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur d'études en informatique
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2006
    Messages : 16 818
    Billets dans le blog
    14
    Par défaut
    trouver les lignes qui valident toutes ces conditions et non pas au moins une.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    SELECT DISTINCT(annonce_id) FROM annonces_specific_fields
    WHERE specific_field_id = X AND real_value <= Y
    OR (specific_field_id = Z AND real_value = "toto")
    OR (specific_field_id = W AND (real_value BETWEEN t AND u))
    Tu as trois conditions, il faut donc compter si annonce_id répond aux trois conditions.
    Supprime le distinct et fait un regroupement :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    SELECT annonce_id 
    FROM annonces_specific_fields
    WHERE (specific_field_id = X AND real_value <= Y)
    OR (specific_field_id = Z AND real_value = "toto")
    OR (specific_field_id = W AND real_value BETWEEN t AND u)
    GROUP BY annonce_id
    HAVING COUNT(*) = 3
    Philippe Leménager. Ingénieur d'étude à l'École Nationale Supérieure de Formation de l'Enseignement Agricole, en retraite... mais toujours Autoentrepreneur à l'occasion.
    Mon ancien blog sur la conception des BDD, le langage SQL, le PHP... et mon nouveau blog sur les mêmes sujets.
    « Ce que l'on conçoit bien s'énonce clairement, et les mots pour le dire arrivent aisément ». (Nicolas Boileau)
    À la maison comme au bureau, j'utilise la suite Linux Mageïa !

  4. #4
    Membre averti
    Profil pro
    Inscrit en
    Février 2008
    Messages
    59
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2008
    Messages : 59
    Par défaut
    Merci CinePhil pour ton idée, mais cela ne marche pas...

    Voici ma requête actuelle :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    SELECT distinct(`spec`.`annonce_id`) from annonces_specific_fields AS spec, 
    annonces_specific_fields AS spec2, 
    annonces_specific_fields AS spec3 
    WHERE (`spec`.`specific_field_id` IN ( SELECT id FROM specific_fields WHERE slug = "km" ) 
            AND (`spec`.`real_value` <= 100000) 
    )
    AND (`spec2`.`specific_field_id` IN ( SELECT id FROM specific_fields WHERE slug = "annee-mod" ) 
            AND (`spec2`.`real_value` between 2000 and 2005) 
    )
    AND (`spec3`.`specific_field_id` IN ( SELECT id FROM specific_fields WHERE slug = "energie" ) 
            AND (`spec3`.`real_value` = "Essence") 
    )
    AND `spec`.`annonce_id` = `spec3`.`annonce_id`
    Résultat : annonce_id = 7

    Et voici la tienne :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    SELECT `annonce_id` from annonces_specific_fields AS spec
    WHERE (`spec`.`specific_field_id` IN ( SELECT id FROM specific_fields WHERE slug = "km" ) 
            AND (`spec`.`real_value` <= 100000) 
    )
    OR (`spec`.`specific_field_id` IN ( SELECT id FROM specific_fields WHERE slug = "annee-mod" ) 
            AND (`spec`.`real_value` between 2000 and 2005) 
    )
    OR (`spec`.`specific_field_id` IN ( SELECT id FROM specific_fields WHERE slug = "energie" ) 
            AND (`spec`.`real_value` = "Essence") 
    )
    GROUP BY annonce_id
    HAVING COUNT(*) = 3
    Résultat : vide

  5. #5
    Expert éminent
    Avatar de CinePhil
    Homme Profil pro
    Ingénieur d'études en informatique
    Inscrit en
    Août 2006
    Messages
    16 818
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur d'études en informatique
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2006
    Messages : 16 818
    Billets dans le blog
    14
    Par défaut
    Citation Envoyé par avairet Voir le message
    Et voici la tienne :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    SELECT `annonce_id` from annonces_specific_fields AS spec
    WHERE (`spec`.`specific_field_id` IN ( SELECT id FROM specific_fields WHERE slug = "km" ) 
            AND (`spec`.`real_value` <= 100000) 
    )
    OR (`spec`.`specific_field_id` IN ( SELECT id FROM specific_fields WHERE slug = "annee-mod" ) 
            AND (`spec`.`real_value` between 2000 and 2005) 
    )
    OR (`spec`.`specific_field_id` IN ( SELECT id FROM specific_fields WHERE slug = "energie" ) 
            AND (`spec`.`real_value` = "Essence") 
    )
    GROUP BY annonce_id
    HAVING COUNT(*) = 3
    Euh... non, ce n'est pas ma requête !
    Jamais je n'écrirais une horreur pareille !

    Exprime clairement ton besoin avec les vrais noms de tables et de colonnes et les vraies valeurs parce que là je n'arrive pas à comprendre ta requête !

    J'ai quand même fait l'effort de réécrire ta requête telle que je la comprends.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    SELECT annonce_id 
    FROM annonces_specific_fields
    WHERE (slug = 'km' AND real_value <= 100000) 
      OR (slug = 'annee-mod' AND real_value BETWEEN 2000 AND 2005
      OR (slug = 'energie' AND real_value = 'Essence') 
    GROUP BY annonce_id
    HAVING COUNT(*) = 3
    Elle devrait donner les annonces_id pour lesquelles les kilomètres sont <= 100 000 ET l'année est comprise entre 2000 et 2005 inclus ET l'énergie est l'essence.

    On fait un OU sur les conditions et on compte le nombre de lignes par annonce_id puis on ne retient que les annonce_id qui ont trois lignes, c'est à dire qui répondent aux 3 conditions. Ainsi, ça transforme la série de OU en ET.

    Au passage, les valeurs textuelles s'écrivent entre apostrophes, pas entre guillemets.
    Philippe Leménager. Ingénieur d'étude à l'École Nationale Supérieure de Formation de l'Enseignement Agricole, en retraite... mais toujours Autoentrepreneur à l'occasion.
    Mon ancien blog sur la conception des BDD, le langage SQL, le PHP... et mon nouveau blog sur les mêmes sujets.
    « Ce que l'on conçoit bien s'énonce clairement, et les mots pour le dire arrivent aisément ». (Nicolas Boileau)
    À la maison comme au bureau, j'utilise la suite Linux Mageïa !

  6. #6
    Membre averti
    Profil pro
    Inscrit en
    Février 2008
    Messages
    59
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2008
    Messages : 59
    Par défaut
    Euh... non, ce n'est pas ma requête !
    Jamais je n'écrirais une horreur pareille !
    Désolé, je voulais dire "voilà ton schéma de requête appliqué à mon besoin" !
    Merci quand même pour le compliment...

    J'ai quand même fait l'effort de réécrire ta requête telle que je la comprends.
    Merci et bravo
    C'est bien cela, j'ai juste oublié de préciser que "slug" n'est pas dans "annonces_specific_fields" (d'où mes "horribles" requêtes imbriquées).

    Elle devrait donner les annonces_id pour lesquelles les kilomètres sont <= 100 000 ET l'année est comprise entre 2000 et 2005 inclus ET l'énergie est l'essence.
    Exactement
    Mais cela ne marche pas, désolé ! Il y a bien dans mes tables une annonce et une seule qui matche ces 3 conditions à la fois, or ta requête ne renvoie rien... Même en ajoutant une jointure avec la table "specific_fields" qui contient les slugs.

    Au passage, les valeurs textuelles s'écrivent entre apostrophes, pas entre guillemets.
    Quand on génère la requête en PHP avec des concaténations sur des strings, parfois le guillemet est bien pratique et ne pose aucun souci à MySQL...

Discussions similaires

  1. Réponses: 4
    Dernier message: 31/01/2014, 11h38
  2. Select multiple qui envoi des valeurs différents
    Par novasenha dans le forum Langage
    Réponses: 5
    Dernier message: 14/05/2009, 19h27
  3. Réponses: 2
    Dernier message: 14/04/2009, 10h27
  4. Sélection de lignes qui ont des valeurs maximales
    Par sicnarf dans le forum Requêtes et SQL.
    Réponses: 2
    Dernier message: 31/10/2008, 15h42
  5. Réponses: 6
    Dernier message: 25/03/2008, 16h13

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