Help pour une requete

Help pour une requete - SQL/NoSQL - Programmation

Marsh Posté le 02-12-2010 à 11:14:05    

Bonjour,  
 
je ne connais sans doute pas assez le langage sql pour savoir si ma requete est réalisable ou non  
J'essaie d'expliquer le pb: j'ai une table client (avec un id), et une table facture (avec un id, un client_id, et un magasin appartenant à un ensemble de 3 valeurs possibles: A, B, C). Je peux extraire toutes les factures d'un magasin donné (disons 'A') via:
 
select * from client join facture on (client.id=facture.client_id) where facture.magasin='A';
 
un client donné peut ne pas avoir de factures,
ou des factures d'un seul magasin
ou des factures de deux magasins
ou des factures des trois magasins.
 
Ce que je souhaite obtenir EN UNE SEULE REQUETE:  
requete 1: la liste de tous les clients (SANS DOUBLONS) qui ont des factures dans n'importe quel magasin
requete 2:                                                               qui ont des factures au magasin 'A' mais pas au 'B' ni 'C'
requete 3:                                                               qui ont des factures non effectuées au magasin 'A'
 
Le but étant (entre autre) de compter le nombre de clients en fonction des magasins.
 
Est-ce-possible et si oui comment?
 
Merci d'avance de votre aide.

Reply

Marsh Posté le 02-12-2010 à 11:14:05   

Reply

Marsh Posté le 02-12-2010 à 12:15:10    

Au pif:
requete 1:
 
Select distinct c.id from clients c, facture f where c.id=f.client_id
 
requete 2:
 
Select distinct c.id from clients c, facture f where c.id=f.client_id and f.magasin='A'
 
requete 3:
Select distinct c.id from clients c, facture f where c.id=f.client_id and f.magasin<>'A'

Reply

Marsh Posté le 02-12-2010 à 14:13:38    

Heu, merci, je devais être mal réveillé ce matin...
Je vais vérifier que cela répond bien à mon pb, et vérifier surtout que mon pb est bien celui que j'ai exposé... :)

Reply

Marsh Posté le 02-12-2010 à 14:56:30    

Je crois avoir retrouvé la requete qui me posait le pb: comment obtenir les clients qui ont des factures au magasin 'A' ET au magasin 'B'?
Si j'indique magasin='A' OR magasin='B' c'est faux, et si je remplace le OR par un AND, c'est également faux...
Merci encore.

Reply

Marsh Posté le 02-12-2010 à 15:09:35    

Fais une union entre ceux qui ont des factures au magasin A et ceux qui en ont au magasin B

Reply

Marsh Posté le 02-12-2010 à 15:49:32    

Je vois bien l'idée, mais pas la syntaxe, peux-tu m'indiquer comment cela s'écrit stp?
Merci.

Reply

Marsh Posté le 02-12-2010 à 16:28:51    

lopotkine a écrit :

Je crois avoir retrouvé la requete qui me posait le pb: comment obtenir les clients qui ont des factures au magasin 'A' ET au magasin 'B'?
Si j'indique magasin='A' OR magasin='B' c'est faux, et si je remplace le OR par un AND, c'est également faux...
Merci encore.

 

Pas besoin d'union.

 
Code :
  1. SELECT DISTINCT c.*
  2. FROM clients c
  3.          JOIN facture f ON c.id = f.client_id AND  f.magasin_id IN ('A', 'B')
 

[edit]

 

Non, j'avais mal compris.
Et c'est pas une union, c'est une intersection, que tu veux.


Message édité par skeye le 02-12-2010 à 16:30:17

---------------
Can't buy what I want because it's free -
Reply

Marsh Posté le 02-12-2010 à 16:31:27    

Code :
  1. SELECT DISTINCT c.*
  2. FROM clients c
  3.        JOIN facture f ON c.id = f.client_id
  4.        JOIN facture f2 ON c.id = f2.client_id
  5. WHERE f.magasin_id = 'A'
  6. AND f2.magasin_id = 'B'


---------------
Can't buy what I want because it's free -
Reply

Marsh Posté le 03-12-2010 à 16:39:26    

Si on se fout de ramener les infos des factures mais juste les informations clients, c'est plus propre et peut-être même plus rapide (si correctement indexé) comme ça IMHO :

Citation :


Select c.*
from client c
where exists (select 1
                  from facture f
                  where f.client_id = c.client_id
                  and f.magasin_id = 'A')
and    exists (select 1
                  from facture f2
                  where f2.client_id = c.client_id
                  and F2.magasin_id = 'B')

Message cité 1 fois
Message édité par E-Nyar le 03-12-2010 à 16:40:21

---------------
Don't fuck me, I'm anonymous.
Reply

Marsh Posté le 03-12-2010 à 16:41:56    

E-Nyar a écrit :

Si on se fout de ramener les infos des factures mais juste les informations clients, c'est plus propre et peut-être même plus rapide (si correctement indexé) comme ça IMHO :

Citation :


Select c.*
from client c
where exists (select 1
                  from facture f
                  where f.client_id = c.client_id
                  and f.magasin_id = 'A')
and    exists (select 1
                  from facture f2
                  where f2.client_id = c.client_id
                  and F2.magasin_id = 'B')


 

Plus rapide? 2 sous-requêtes par rapport à deux jointures? [:ula]

 

Et je vois pas non plus pourquoi ce serait plus propre...

Message cité 1 fois
Message édité par skeye le 03-12-2010 à 16:42:47

---------------
Can't buy what I want because it's free -
Reply

Marsh Posté le 03-12-2010 à 16:41:56   

Reply

Marsh Posté le 03-12-2010 à 16:53:01    

skeye a écrit :

 

Plus rapide? 2 sous-requêtes par rapport à deux jointures? [:ula]

 

Et je vois pas non plus pourquoi ce serait plus propre...

 

1 seul passage sur la table client (pas de distinct), pas de multiplication inutile via les jointures, les sous-requêtes seront montées en mémoire avec des bind-variables, si la table facture est bien indexée, 1 seul lecture / client.
Faudrait voir le plan d'exécution, mais d'instinct, je dirais plus rapide... le where doit bien plomber (vu que tu vas aller chercher toutes les combinaison f et f2 et ensuite appliquer ton prédicat sur la combinaison f = A ET f = B)

 

et pour ce qui est du plus propre, c'est juste que ça répond à la question posée, ni plus ni moins.

Message cité 1 fois
Message édité par E-Nyar le 03-12-2010 à 16:55:20

---------------
Don't fuck me, I'm anonymous.
Reply

Marsh Posté le 03-12-2010 à 16:55:03    

Le query engine va probablement generer le meme plan pour les 2 query mais les 2 jointures ont l'air bien plus propre et facile a comprendre en 1 coup d'oeuil.
 
Le probleme des "exists" c'est qu'on a trop vite fait de transformer en "not exists" et la c'est la misere totale.

Reply

Marsh Posté le 03-12-2010 à 17:09:32    

en testant rapidement avec ce que j'avais sous le coude (departement / commune) :
 
Select c.*
from departement c
where exists (select 1  
                  from commune f
                  where f.iddepartement = c.iddepartement
                  and f.numinsee = '28353')
and    exists (select 1  
                  from commune f2
                  where f2.iddepartement = c.iddepartement
                  and f2.numinsee = '28354');
 
http://hfr-rehost.net/thumb/self/pic/b7a44f91d11e2b23e4aaaa6abbe89afb8f6acdf0.png
 
Cout : 4
 
 
SELECT DISTINCT c.*
FROM departement c
        JOIN commune f ON f.iddepartement = c.iddepartement
        JOIN commune f2 ON f2.iddepartement = c.iddepartement
WHERE f.numinsee = '28353'
AND f2.numinsee = '28354';
 
http://hfr-rehost.net/thumb/self/pic/78e3007d95da464054d37cf622c9edc2b8db0fa7.png
 
Cout : 5
 
C'est le distinct qui plombe la 2ieme.


Message édité par E-Nyar le 03-12-2010 à 17:10:50

---------------
Don't fuck me, I'm anonymous.
Reply

Marsh Posté le 03-12-2010 à 17:09:56    

E-Nyar a écrit :


et pour ce qui est du plus propre, c'est juste que ça répond à la question posée, ni plus ni moins.


 
Euh, et ma version elle répond à quelle question, celle du voisin?[:pingouino]
Je veux les clients

Code :
  1. SELECT c.*
  2. FROM client c


 
qui ont des factures dans le magasin A
 

Code :
  1. JOIN facture f
  2. WHERE f.magasin ='A'


 
et des factures dans le magasin B
 

Code :
  1. JOIN facture f2
  2. WHERE f2.magasin = 'B'


 
 
[:w3c compliant]
 
 
Pour le plan d'exécution je zappe, flemme de tester. Mais au mieux je dirais que ça va donner le même résultat.
 


---------------
Can't buy what I want because it's free -
Reply

Marsh Posté le 03-12-2010 à 17:17:33    

skeye a écrit :


 
Euh, et ma version elle répond à quelle question, celle du voisin?[:pingouino]
Je veux les clients

Code :
  1. SELECT c.*
  2. FROM client c


 
qui ont des factures dans le magasin A
 

Code :
  1. JOIN facture f
  2. WHERE f.magasin ='A'


 
et des factures dans le magasin B
 

Code :
  1. JOIN facture f2
  2. WHERE f2.magasin = 'B'


 
 
[:w3c compliant]
 
 
Pour le plan d'exécution je zappe, flemme de tester. Mais au mieux je dirais que ça va donner le même résultat.
 


 
Ça dépend comment on veut répondre à la question, là tu vas chercher les clients et toutes leurs factures dans le magasins A et toutes leurs factures dans le magasins B et tu ne prends que ceux qui ont des factures dans les 2. D'ailleurs petite question pourquoi ne pas remonter le filtre sur magasin A et magasin B directement dans la jointure ? (bon ça change rien au plan d'exécution vu que l'optimiser va les remonter lui-même, mais je trouve ça plus compréhensible si par hasard tu te retrouves avec 4 ou 5 filtres sur les clients par la suite pour pas mélanger les choux et les carottes).
 
 
C'est différent de répondre par "Je vais chercher tous les clients pour lesquels il existe au moins 1 facture dans A et 1 facture dans B


---------------
Don't fuck me, I'm anonymous.
Reply

Marsh Posté le 03-12-2010 à 17:18:37    

Oliiii a écrit :

Le query engine va probablement generer le meme plan pour les 2 query mais les 2 jointures ont l'air bien plus propre et facile a comprendre en 1 coup d'oeuil.
 
Le probleme des "exists" c'est qu'on a trop vite fait de transformer en "not exists" et la c'est la misere totale.


 
Le not exists a traduire en jointure c'est pas mal non plus ;)


---------------
Don't fuck me, I'm anonymous.
Reply

Marsh Posté le 03-12-2010 à 17:21:51    

E-Nyar a écrit :


Ça dépend comment on veut répondre à la question, là tu vas chercher les clients et toutes leurs factures dans le magasins A et toutes leurs factures dans le magasins B et tu ne prends que ceux qui ont des factures dans les 2.


 
C'est une manière de l'interpréter.
 

E-Nyar a écrit :

D'ailleurs petite question pourquoi ne pas remonter le filtre sur magasin A et magasin B directement dans la jointure ? (bon ça change rien au plan d'exécution vu que l'optimiser va les remonter lui-même, mais je trouve ça plus compréhensible si par hasard tu te retrouves avec 4 ou 5 filtres sur les clients par la suite pour pas mélanger les choux et les carottes).


 
Par habitude.:D
J'ai des collègues qui en sont encore aux jointures uniquement via la clause where, si je mets autre chose que les identifiants dans le join ils ont tendance à s'y perdre.


---------------
Can't buy what I want because it's free -
Reply

Marsh Posté le 03-12-2010 à 17:24:57    

E-Nyar a écrit :


 
Le not exists a traduire en jointure c'est pas mal non plus ;)


 
mais non, faut juste avoir l'habitude.:D
Par contre il faut vraiment passer la condition dans le JOIN ce coup-ci, les collègues souffrent.:D
 
A mais pas B :
 

Code :
  1. SELECT DISTINCT c.*
  2. FROM clients c
  3.        JOIN facture f ON c.id = f.client_id AND f.magasin_id = 'A'
  4.        LEFT OUTER JOIN facture f2 ON c.id = f2.client_id AND f2.magasin_id = 'B'
  5. WHERE f2.id IS NULL


---------------
Can't buy what I want because it's free -
Reply

Marsh Posté le 03-12-2010 à 17:31:26    

skeye a écrit :

 

mais non, faut juste avoir l'habitude.:D
Par contre il faut vraiment passer la condition dans le JOIN ce coup-ci, les collègues souffrent.:D

 

A mais pas B :

 
Code :
  1. SELECT DISTINCT c.*
  2. FROM clients c
  3.        JOIN facture f ON c.id = f.client_id AND f.magasin_id = 'A'
  4.        LEFT OUTER JOIN facture f2 ON c.id = f2.client_id AND f2.magasin_id = 'B'
  5. WHERE f2.id IS NULL


 

Et si c'est les clients qui n'ont aucune facture dans A ni dans B  [:kunks] (oui je sais double outer join et double is null :o )

 

Bon je pinaille après chacun a ses petites habitudes d'écriture et ça joue à pas grand chose sur les perfs. :jap:

 

P.S. : pour les collègues, ils ont qu'à s'adapter ou mourrir. Viva la Evolution !   [:el che]


Message édité par E-Nyar le 03-12-2010 à 17:32:31

---------------
Don't fuck me, I'm anonymous.
Reply

Marsh Posté le 06-12-2010 à 12:50:38    

Heu, merci pour toutes ces infos, mais si j'en reviens à ma question de départ,
je constate que j'ai toujours un soucis sur la requete2.
 
Si j'utilise les solutions vues plus haut basées sur les jointures multiple, cela ne fonctionne pas: .... f1.magasin = 'A' AND f2.magasin != 'B'
(dans ma vraie base, facture.magasin est à remplacer par facture.type et A, B par
ADOM et MAG):
 
mysql> select client_id, type from facture where client_id=2327;
+-----------+------+
| client_id | type |
+-----------+------+
|      2327 | MAG  |
|      2327 | ADOM |
+-----------+------+
 
mysql> select distinct client.id, f1.type, f2.type from client join facture f1 on client.id=f1.client_id join facture f2 on client.id=f2.client_id where f1.type='ADOM' and f2.type!='MAG' and client.id = 2327;
+------+------+------+
| id   | type | type |
+------+------+------+
| 2327 | ADOM | ADOM |
+------+------+------+
1 row in set (0.00 sec)
 
Le distcint est tj là, car j'ai simplement rajouté un filtre sur l'id client, normalement, il me les faut tous de manière unique.

Reply

Marsh Posté le 06-12-2010 à 14:19:02    

TU veux compter les clients unique ok mais quand tu as un client qui a des factures dans 2 magasins, tu les comptes comment ?

Reply

Marsh Posté le 06-12-2010 à 14:29:03    

Je crois avoir pigé le truc, merci de me confirmer:
 
Requete 1: tous les clients distincts ayant des factures de type 'A':
select distinct ... from client join facture on facture.client_id=client.id and facture.type='A'
 
Requete 2: factures A mais PAS B:
select distinct ... from client join facture on facture.client_id=client.id and facture.type='A' left outer join facture f2 on f2.client_id=client.id and f2.type='B' where f2.id is null
 
Requete 3: clients ayant des factures A ET AUSSI B:
select distinct ... from client join facture on facture.client_id=client.id and facture.type='A' left outer join facture f2 on f2.client_id=client.id and f2.type='B' where f2.id is NOT null
 
J'ai bien tout compris?
D'avance, merci.

Reply

Marsh Posté le 06-12-2010 à 14:31:14    

La dernière te retournera le bon résultat, mais c'est très alambiqué, là, t'as pas besoin de jointure externe! :D
 
select distinct ... from client join facture on facture.client_id=client.id and facture.type='A' join facture f2 on f2.client_id=client.id and f2.type='B'


---------------
Can't buy what I want because it's free -
Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed