3 tables liées par ID une requête, pas de resultat ... - SQL/NoSQL - Programmation
Marsh Posté le 16-08-2004 à 16:28:42
SELECT s.script_id
FROM script s
WHERE s.script_id IN (SELECT ms.script_id
FROM machine_type_vs_script ms
WHERE ms.machine_type_id IN (SELECT m.machine_type_id
FROM machine_type m
WHERE m.machine_type_desc IN ('SCP')))
Marsh Posté le 16-08-2004 à 16:52:10
Quelle version de mySql ?
Avant la version 4, mySql ne permet pas de faire des select imbriqués de ce type
Marsh Posté le 16-08-2004 à 20:04:15
tu peux pas faire une vraie requête avec des jointures au lieu d'utiliser des IN comme un goret
Dans ton cas, tu n'as aucune utilité à passer par des IN...
select s.*
from script s, machine_type_vs_script ms, machine_type m
where m.machine_type_desc = 'SCP'
and ms.machine_type_id = m.machine_type_id
and s.script_id = ms.script_id
Marsh Posté le 16-08-2004 à 20:14:45
Pq tu dois à tout prix utiliser des IN ? Des jointures serait bcp plus appropriées ici
|
Et en fait, si tu ne veux que les script_id, tu dois même pas déranger la table script:
|
Marsh Posté le 16-08-2004 à 21:38:56
pardon je l'avais pas vu. Au moins ça a le mérite d'enfoncer le clou.
Marsh Posté le 17-08-2004 à 09:18:38
je viens d'arriver... je regarde tout ça, j'essaie et je dis quoi. Merci pour votre coup de patte !!
Marsh Posté le 17-08-2004 à 10:08:30
Ok voilà, j'ai un peu regardé tout ça...
Première solution (Arjuna) :
---------------------------
Ca marche très bien, si tu envisages qu'à un script ne corresponde que un type de machine. Si je cherche tous les scripts auxquels correspondent SCP ET BE, cette requête ne me trouvera rien.
select s.script_id
from script s, machine_type_vs_script ms, machine_type m
where m.machine_type_desc = 'SCP'
and m.machine_type_desc = 'BE'
and ms.machine_type_id = m.machine_type_id
and s.script_id = ms.script_id
Si j'ai bien compris comment ça marchait (jamais fait de DB auparavant).
Lorsqu'on joint deux ou trois tables ds ce cas-ci, SQL construit une table temporaire construite comme ceci : Chaque combinaison possible de chaque entrée de chaque table constitue une entrée de la table temporaire. A cette table est appliquée la requête... Ce qui veut dire que ds mon cas de recherche je ne pourrai jamais trouver les scripts installés sur deux machines (ou mm trois) en mm temps. Les lignes ainsi crées ds la table temporaire seraient comme par ex :
id x name15 author15 ... ... ... SCP ... ...
id x(le mm) name 15 author15 ... ... ... BE ... ...
si le script est installé sur le scp et le be... Mais ma requete cherchera POUR CHAQUE LIGNE SCP ET BE, ce qu'il ne trouvera jamais... enfin ça c'est si j'ai bien capté...
Deuxième solution (dividee) :
-----------------------------
J'ai tenté ceci :
SELECT s.script_id
INNER JOIN machine_type_vs_script ms ON s.script_id = ms.script_id
INNER JOIN machine_type m ON ms.machine_type_id = m.machine_type_id
WHERE m.machine_type_desc IN('SCP')
MySQL said:
#1064 - You have an error in your SQL syntax near 'INNER JOIN machine_type_vs_script ms ON s.script_id = ms.script_id
INNER JOIN ' at line 2
Et là je ne vois pas...
Mais même si je supprime cette erreur de syntaxe, n'aurais je pas le probleme, il y a t il une différence de principe entre la première solution et celle ci une fois que je veux sélectionner des scripts qui potentiellement peuvent etre sur deux machines différentes ??
En bref, je pense quand même que je dois utiliser des IN, mais j'ai une erreur :
Error
SQL-query :
SELECT s.script_id
FROM script s
WHERE s.script_id IN (
SELECT ms.script_id
FROM machine_type_vs_script ms
WHERE ms.machine_type_id IN (
SELECT m.machine_type_id
FROM machine_type m
WHERE m.machine_type_desc = 'SCP')))
LIMIT 0 , 30
MySQL said:
#1064 - You have an error in your SQL syntax near 'SELECT ms.script_id
FROM machine_type_vs_script ms
WHERE ms.machine_type_id
IN (' at line 5
J'espère avoir été clair, Si vous avez une idée... je suis assez bloqué...
Marsh Posté le 17-08-2004 à 10:12:14
dawei38 a écrit : |
Pour le premier niveau, évidement, si tu as une liste, tu utilises un IN (tu peux aussi très bien utiliser des OR, c'est de l'algèbre de bool, le B.A.BA de l'écriture d'une requête...)
select s.* |
Marsh Posté le 17-08-2004 à 10:22:13
Qu'entends tu par "avec des or"
select s.script_id
from script s, machine_type_vs_script ms, machine_type m
where m.machine_type_desc = 'SCP'
or m.machine_type_desc = 'BE'
and ms.machine_type_id = m.machine_type_id
and s.script_id = ms.script_id
ne me donnera pas ce que je veux... mais bien tous les scripts installés sur un BE et tous les scripts installés sur un SCP et forcément ceux installés sur les deux en mm temps... mais pas uniquement ceux installés sur le scp ET le be.
Quelqu'un a une idée de l'erreur quant aux IN ?
Merci bcp !
Tu dis aussi que je peux utiliser le IN, dont la requete est reprise plus haut, saurais tu quelle erreur s'y cache ?? Tout m'a l'air syntaxiquement correct et je reste persuadé que c'est la bonne solution...
Marsh Posté le 17-08-2004 à 12:23:18
Dans ma solution que tu as essayé, tu as oublié la ligne 'FROM script s'. Mais de toute façon, elle te donnera le même résultat que celle d'Arjuna.
Tu n'avais pas précisé que tu ne voulais que les scripts qui se trouvent sur tous les types machines citées.
Essaye ça:
|
(J'espère que ça fonctionne en MySQL)
Marsh Posté le 17-08-2004 à 12:51:51
Ca marche presque... Il me trouve bien toutes les scripts installés ou sur un SCP ou sur un BE, et installés sur exactement deux machines. Mais si un script est installé sur un SCP et une seule autre machine, il satisfait la requete et est repris...
Je continue à chercher. Pour l'utilisation de requête au sein d'un IN, j'ai pû lire que c'était faisable, mais apparement PHPMyadmin ne l'accepte pas... ou alors ma version de MySQL...
Marsh Posté le 17-08-2004 à 13:47:14
Je suis un peu embrouillé
Qu'est-ce que tu veux exactement ? Maintenant je pensais que tu voulais obtenir les scripts associés à TOUS les types de machines passés en paramètre. Est-ce que tu peux donner un énoncé clair et complet du résultat que tu veux obtenir ? Remarque que ta requête avec les 2 sous-requêtes imbriquées, si elle fonctionnait, donnerait le même résultat que la requête de ton avant-dernier post, avec le OR (bien que j'utiliserais des parenthèses car je suis pas sûr de la précédence du OR par rapport au AND), hormis les doublons que l'on peut supprimer avec un DISTINCT.
Et puis maintenant tu parles de machines au lieu de types de machines. J'imagine que tu as également une table "machine" dans ta base mais jusqu'ici j'imaginais que les machines n'étaient pas concernées par cette requête.
Marsh Posté le 17-08-2004 à 14:08:49
ok, je reprend (merci pour ton aide...)
Je dois créer une base de donnée populée par une webinterface et reprenant des infos concernant des scripts installés sur des machines...
Compostion de la DB :
- J'ai un table principale appellée "script" dans laquelle je viens mettre toutes les infos qui ne peuvent être doubles pour un même script, ex : nom du script, auteur, version, os, path...
champs : script_id, script_name, script_author, ...
- une table machine_type
Seulement certaines infos peuvent être doubles, càd qu'un script peut être installé sur plusieurs types de machines. J'ai pour ce faire créer une table indépendante pour chaque info à multiple occurence. (je n'ai parlé ici que du type de machine quiest ds ce cas, mais c pareil pour les autres)
champs : machine_type_id, machine_type_desc
- machine_type_vs_script
Pour lier ces deux tables, j'ai créé une table liant les deux id des deux tables différentes
champs : script_id, machine_type_id
L'utilisateur veut faire une recherche de script se trouvant ds la db. J'aimerais qu'il puisse faire une recherche parmi les scripts installés sur tel(s) ou tel(s) type(s) de machine.
Ex : tous les scripts de l'auteur 'auteur1' installés sur un 'BE' et sur un 'SCP'
Marsh Posté le 17-08-2004 à 16:58:16
J'avais compris la structure de la base (mais merci d'avoir précisé quand-même: je suppose que quand tu dis "machines" plus haut, tu voulais dire "types de machine" ), c'est surtout des détails sur la requête que je voulais.
Citation : Ex : tous les scripts de l'auteur 'auteur1' installés sur un 'BE' et sur un 'SCP' |
En détaillant, tu veux bien dire
"tous les scripts de 'auteur1' qui sont installés A LA FOIS sur un 'BE' et un 'SCP' (et éventuellement sur d'autres types de machines mais on s'en fout)" ?
Dans ce cas ma requête précédente doit être bonne, je pense. Je sais pas pq tu dis que si le script est installé sur un SCP et un autre [type de] machine il est pris par la requête, le WHERE permet de s'assurer qu'on ne s'intéresse qu'à ces deux types de machines...
Le mieux, si ça te convient tjs pas ou si tu crois qu'il y a une erreur dans ma requête, serait de donner un échantillon de donnée des tables (juste quelques lignes, mais qui couvrent tous les cas possibles), la description d'une requête et le résultat que tu voudrais obtenir.
Marsh Posté le 17-08-2004 à 17:11:49
Bon, quand ce sera clair, je repasserai, parceque là je suis plus embrouillé qu'autrechose
Marsh Posté le 17-08-2004 à 17:46:02
premièrement merci à tous... là j'implémente le problème autrement, ça ne me plait pas tellement, mais bon... ça doit avancer et je peux toujours changer mon code PHP après.
Je vais tenter de détailler :
Soit une base de données contenant 7 tables en tout :
- script : toutes les infos comme le nom du script, son auteur, la version la plus récente, chez quel client il tourne, sur quel os, etc... Ce sont des infos qui ne peuvent être doubles, par ex pour un même script, il n'est pas possible d'avoir deux noms (ce serait un non sens) ou deux os (Solaris ou HP-UX par ex). C'est pourquoi elles vont tres bien ds la même table.
- machine_type : un script peut etre installé sur plusieurs types de machines et c'est pourquoi, afin de savoir si tel ou tel script est installé sur telle ou telle machine, j'ai besoin de créer d'abord une table qui va contenir les différents types de machines possibles. Cette table ne contient que deux champs, soient
- machine_type_id
- machine_type_desc (BE, SCP, ...)
- machine_type_vs_script : sert à faire le lien entre le script et les types de machines (un ou plusieurs !!) qui lui sont associés.
Ex :
SCRIPT
------
script_id : 1 | 2 | 3
script_name : name1 | name2 | name3
script_author : auteur1 | auteur1 | auteur3
script_os : Sun Solaris | HP-UX | Sun Solaris
...
machine_type
------------
machine_type_desc : SMP | BE | SCP
machine_type_id : 1 | 2 | 3
et le lien entre script et machine type
machine_type_vs_script:
-----------------------
soit script avec ID 1 installé sur BE et SCP
soit script avec ID 2 installé sur BE seulement
soit script avec ID 3 installé sur SMP et SCP
Cette table contient alors :
script_id 1 | 1 | 2 | 3 | 3
machine_type_id 2 | 3 | 2 | 1 | 3
Ce que j'aimerais avec ma requete, c'est savoir exactement où le script est installé, ni plus ni moins ! Donc si je fais
si l'utilisateur veut les scripts installés sur
- BE et SCP, il doit me retourner script 1
- BE SEULEMENT, il doit me retourner script 2
- SMP et SCP, il doit me retourner script 3
C'est vraiment exclusif comme condition...
Sur ce je m'en vais, je saurai lire toute solution, mais faire aucun test avant demain, merci beaucoup et bonne soirée à tous !
Marsh Posté le 17-08-2004 à 17:51:46
Hmmm... OK. Là c'est bien plus chaud, je regarde ça dans la soirée. Là je termine un truc au taff
Marsh Posté le 17-08-2004 à 18:41:49
si on a la liste des machines en entrée (BE, SCP par exemple), il faut :
- un SELECT qui récupère les machine_type_id qui ne sont pas ceux en entrée (SMP dans notre exemple),
- en partant de ce SELECT, récupérer les scripts qui sont sur ces machines,
- choisir finalement les scripts qui sont installés sur les machines données en entrée, mais qui n'apparaissent pas dans ce SELECT.
En Oracle, ça s'écrirait :
Code :
|
Marsh Posté le 18-08-2004 à 00:53:52
merci dawei38 d'avoir précisé, maintenant c'est plus clair!
La requête de Beegee est presque bonne, mais il manque un truc:
tous les scripts qui se trouvent sur les machines qui ne sont pas demandées sont bien enlevés, mais la requête garde quand même les scripts qui ne sont pas installés sur toutes les machines demandées.
Dans le premier exemple, quand on demande les script qui se trouvent sur BE et SCP, la requête renvoie 1 et 2, au lieu de 1 uniquement.
Pour parer à cela il suffit de rajouter à la fin de la requête de BeeGee:
|
Ou bien, réécrite à ma manière (çàd en essayant d'éviter au maximum les select imbriqués):
|
Je n'ai pas trouvé le moyen de me passer complétement de select imbriqués...
EDIT: simplication de ma requête
Marsh Posté le 18-08-2004 à 10:29:31
Ok ok, vous avez réglé un problème, qui est la logique de la requête, là je pense Beegee et dividee, la requête est parfaite... en utilisant des IN imbriqués... et c'est là que j'ai surtout un problème depuis le début, apparement, il me refuse les IN imbriqués, pourquoi ??? je n'en sais rien !
cette requête fonctionne
------------------------
SELECT machine_type_id
FROM machine_type
WHERE machine_type_desc
IN ('SCP','BE')
Celle-ci non (alors que même syntaxe) :
--------------------------------------
SELECT script_id
FROM machine_type_vs_script
WHERE machine_type_id IN (
SELECT machine_type_id
FROM machine_type
WHERE machine_type_desc NOT IN ('SCP','BE'));
dès que je met un select imbriqué dans un IN, ça foire...
Voici le message :
Error
SQL-query :
SELECT script_id
FROM machine_type_vs_script
WHERE machine_type_id
IN (
SELECT machine_type_id
FROM machine_type
WHERE machine_type_desc NOT
IN (
'SCP', 'BE'
)
)
LIMIT 0 , 30
MySQL said:
#1064 - You have an error in your SQL syntax near 'SELECT machine_type_id
FROM machine_type
WHERE machine_type_desc NOT
IN ( 'SCP' at line 5
J'entre ça dans PHPMyadmin, mais j'ai aussi essayer d'envoyer la requête par mon code PHP, mais ça foire...
Je pense que c'est un problème de config de MySQL ou un truc du genre, parce que la syntaxe est bonne...
Marsh Posté le 18-08-2004 à 10:33:13
Voici les status (par defaut) de mon phpmyadmin :
Variable Value
Created tmp disk tables 0
Created tmp tables 0
Created tmp files 0
Delayed insert threads 0
Delayed writes 0
Delayed errors 0
Flush commands 1
Handler delete 0
Handler read first 107
Handler read key 414
Handler read next 3196
Handler read prev 0
Handler read rnd 5
Handler read rnd next 1018
Handler update 0
Handler write 0
Variable Value
Key blocks used 17
Key read requests 636
Key reads 17
Key write requests 0
Key writes 0
Max used connections 1
Not flushed key blocks 0
Not flushed delayed rows 0
Open tables 1
Open files 2
Open streams 0
Opened tables 81
Rpl status NULL
Select full join 6
Select full range join 0
Select range 4
Variable Value
Select range check 0
Select scan 268
Slave running OFF
Slave open temp tables 0
Slow launch threads 0
Slow queries 0
Sort merge passes 0
Sort range 0
Sort rows 5
Sort scan 1
Table locks immediate 612
Table locks waited 0
Threads cached 0
Threads created 280
Threads connected 1
Threads running 1
-----------------------------------------
et les variables...
Variable Value
back log 50
basedir C:\phpdev\mysql\
binlog cache size 32768
character set latin1
character sets latin1 big5 czech euc_kr gb2312 gbk latin1_de sjis tis620 ujis dec8 dos german1 hp8 koi8_ru latin2 swe7 usa7 cp1251 danish hebrew win1251 estonia hungarian koi8_ukr win1251ukr greek win1250 croat cp1257 latin5
concurrent insert ON
connect timeout 5
datadir C:\phpdev\mysql\data\
delay key write ON
delayed insert limit 100
delayed insert timeout 300
delayed queue size 1000
flush OFF
flush time 1800
ft min word len 4
ft max word len 254
ft max word len for sort 20
have bdb NO
have innodb NO
have isam NO
have raid NO
have symlink YES
have openssl NO
init file
interactive timeout 28800
join buffer size 131072
key buffer size 16773120
language C:\phpdev\mysql\share\english\
large files support ON
log OFF
log update OFF
log bin OFF
log slave updates OFF
log long queries OFF
long query time 10
low priority updates OFF
lower case table names 1
max allowed packet 1047552
max binlog cache size 4294967295
max binlog size 1073741824
max connections 100
max connect errors 10
max delayed threads 20
max heap table size 16777216
max join size 4294967295
max sort length 1024
max user connections 0
max tmp tables 32
max write lock count 4294967295
myisam bulk insert tree size 8388608
myisam max extra sort file size 256
myisam max sort file size 2047
myisam recover options OFF
myisam sort buffer size 8388608
net buffer length 16384
net read timeout 30
net retry count 10
net write timeout 60
open files limit 0
pid file C:\phpdev\mysql\data\david.pid
port 3306
protocol version 10
record buffer 131072
record rnd buffer 131072
rpl recovery rank 0
query buffer size 0
safe show database OFF
server id 0
slave net timeout 3600
skip locking ON
skip networking OFF
skip show database OFF
slow launch time 2
socket MySQL
sort buffer 2097144
sql mode 0
table cache 64
table type MYISAM
thread cache size 0
thread stack 131072
transaction isolation READ-COMMITTED
timezone Romance Daylight Time
tmp table size 33554432
tmpdir C:\DOCUME~1\DAVIDA~1\LOCALS~1\Temp\
version 4.0.0-alpha-nt
wait timeout 28800
On ne sait jamais...
En tous cas, merci encore pour votre coup de patte... là je continue à hardcoder la logique, mais je ne trouve pas ça beau...
Marsh Posté le 18-08-2004 à 10:43:25
Ben c'est surtout que MySQL version 4.0xx ne supporte pas les requêtes imbriquées, donc à mon avis le plus simple est de gérer ça avec PHP aussi.
Je réfléchis et je vois ce que je peux imaginer.
Marsh Posté le 18-08-2004 à 10:58:21
ahhhhhh me semblait bien... et les requêtes imbriquées sont gérées à partir de quelle version ? Moi je ne suis pas contre changer ça... merci pour l'info !
Marsh Posté le 18-08-2004 à 11:41:43
Pour les requêtes imbriquées et MySQL une petite recherche Google te donnera vite la réponse, je sais plus par coeur.
Par contre je ne crois pas que tu doives obligaroirement les utilser ici (a priori)
Pout obtenir les script qui sont sur la machine 1 ET sur la machine 3, tu fais
Code :
|
Cette requête fonctionne (elle retourne 3), après c'est pas très compliqué à gérer en php, mais faudrait voir les autres requêtes que tu désires !
Marsh Posté le 18-08-2004 à 11:56:57
dawei38 a écrit : |
Sachant que la 2ème présomption est fausse
-BE ET SCP, il doit retourner script 1
-BE seulement, il doit retourner script 1 et 2
- SMP ET SCP, il doit retourner script 3
Marsh Posté le 18-08-2004 à 12:10:51
Je continue à soliloquer
Le principe pour éviter les requêtes imbriquées, si tu recherches les scripts sur x machines
- créer x alias de la table machine_type_vs_script dans ta requête
- faire pour chaque alias un contrôle (s.script_id=x.script_id)
- pour chaque alias (x.machine_type_id), donner la valeur de la machine (x.machine_type_id="la valeur correspondant à la machine" )
EDIT : Si tu as un grand nombre de machines sur lesquelles faire tes recherches, ta requête va faire quelques kilomètres...
Et je ne sais pas ce que ça vaut point de vue rapidité.
Marsh Posté le 18-08-2004 à 13:42:59
merci beaucoup !!! je n'y avais pas pensé, je checke dans ce sens ...
Marsh Posté le 16-08-2004 à 16:24:44
petit problème sur une requête en MySQL... J'ai deux tables ayant un élément ID qui leur est propre. Une troisième table les lie en contenant comme colonnes : l'ID de la première table correspondant à l'ID de la seconde. Je dois faire une requête composée avec des IN
J'ai essayé ces requêtes là qui fonctionnent chacune indépendament pour tester :
A : SELECT script_id
FROM script
WHERE script_id IN ('4') ok!!!!!!!
B : SELECT script_id
FROM machine_type_vs_script
WHERE machine_type_id IN ('3') ok!!!!!!!
C : SELECT m.machine_type_id
FROM machine_type m
WHERE machine_type_desc IN ('SCP') ok!!!!!!!
Mais dès que je tente de les composer, ça foire...
Je m'xplique :
je devrais avoir
A et B et C
SELECT script_id
FROM script
WHERE script_id IN ('B')
avec B qui vaut :
SELECT script_id
FROM machine_type_vs_script
WHERE machine_type_id IN ('C')
Soit la requête suivante
SELECT script_id
FROM script
WHERE script_id IN (SELECT script_id
FROM machine_type_vs_script
WHERE machine_type_id IN (SELECT m.machine_type_id
FROM machine_type m
WHERE machine_type_desc IN ('SCP')))
Mais qui ne fonctionne pas
Je suis un peu bloqué pour continuer à créer la requete qui devra se construire dynamiquement en PHP en fonction de paramètres...
Merci pour votre aide (comme à chaque fois !)