[SQL] Petit "cours" sur l'optimisation

Petit "cours" sur l'optimisation [SQL] - SQL/NoSQL - Programmation

Marsh Posté le 30-03-2005 à 21:03:17    

Salut.
 
Y'a un moment déjà, j'avais indiqué que ce qui a le plus d'importance après les index, c'est l'ordre des tables dans la clause FROM.
 
Je reviens à la charge avec une petite modification : dans certains cas (j'ai pu tester), c'est bien plus important que les index, y compris pour les tables insignifiantes (celles qu'on ne se soucie généralement pas trop d'optimiser).
 
Comment ça marche ?
 
Les SGBD (tous ?), du moins, Oracle et SQL Server sont TRES sensibles à l'ordre des tables dans la clause FROM. Avec ou sans clés étrangères explicites, ces deux SGBD en tout cas, font confiance à l'ordre des tables dans la clause FROM pour optimiser leur plan d'éxécution.
 
Première chose qu'on remarque : les SGBD lisent cette clause "à l'envers". En effet, alors qu'on serait tenté de mettre "le point d'entrée" de la requête en premier, c'est au contraire en dernier qu'il faut le mettre !
 
Exemple basic :
J'ai une table "SOCIETE", avec les champs "SOC_ID" et "NOM". J'ai une table "EMPLOYE" avec les champs "SOC_ID", "EMP_ID" et "NOM". J'ai ensuite une table "CONGES" avec les champs "EMP_ID", "DATE_DEB" et "DATE_FIN".
 
Je veux retouver la durée totale des congés de tous les employés toutes sociétés confondues, pour l'année 2004.
Voici ce qu'on serait tenté d'écrire, ici avec la syntaxe SQL Server.
Vous noterez que je n'utilise pas la syntaxe "... INNER JOIN ... ON ...", pour deux raisons :
- Le parseur SQL se moque éperduement de cette syntaxe, ça ne l'aide en rien à faire les requêtes.
- Je trouve cette syntaxe lourde, et particulièrement difficile à lire, notamment quand on a un grand nombre de tables ou/et des jointures entre plusieurs tables à la fois.
- Pour moi, le critère de jointure est un filtre comme un autre. Je préfère donc le spécifier dans la clause WHERE dédiée aux filtres.
- Contrôler l'ordre des tables dans la clause FROM quand on utilise une telle syntaxe est quasi-impossible à faire.
 

Code :
  1. SELECT SOCIETE.NOM, EMPLOYE.NOM, sum(datediff(d, CONGES.DATE_DEB, CONGES.DATE_FIN))
  2. FROM SOCIETE, EMPLOYE, CONGES
  3. WHERE EMPLOYE.SOC_ID = SOCIETE.SOC_ID
  4. AND CONGES.EMP_ID = EMPLOYE.EMP_ID
  5. AND year(CONGES.DATE_DEB) = 2004
  6. GROUP BY SOCIETE.NOM, EMPLOYE.NOM


 
Cette requête marche très bien. Etant donné la simplicité de cette dernière, l'odre des tables dans la clause FROM n'impactera que très peu la rapidité globale. Cependant, c'est une véritable catastrophe niveau optimisation !
 
Rappelez-vous, j'ai dit que le point d'entrée de la requête devait se trouver en dernier. Penser que "CONGES" avec le filtre sur l'année est le point d'entrée est une hérésie. Un point d'entrée est par définition la table la plus restrictive. Il y a normalement moins de sociétés que d'employés et moins d'employés que de congés (ou alors y'a un problème :D)
La requête optimisée aura alors pour syntaxe :
 

Code :
  1. SELECT SOCIETE.NOM, EMPLOYE.NOM, sum(datediff(d, CONGES.DATE_DEB, CONGES.DATE_FIN))
  2. FROM CONGES, EMPLOYE, SOCIETE
  3. WHERE EMPLOYE.SOC_ID = SOCIETE.SOC_ID
  4. AND CONGES.EMP_ID = EMPLOYE.EMP_ID
  5. AND year(CONGES.DATE_DEB) = 2004
  6. GROUP BY SOCIETE.NOM, EMPLOYE.NOM


 
Le changement n'est pas flagrant, mais il est suffisant pour améliorer considérablement les performances.
Vous noterez que l'odre des éléments de la clause SELECT et de la clause WHERE n'ont strictement aucun impact sur les performances.
 
Maintenant, un cas moins évident.
J'ai une table "EMPLOYE", avec la même structure que précédement. J'ai une table "DROITS" avec les champs "EMP_ID", "PROG_ID" et "TYPE_DROIT". Une autre table "PROGRAMME", avec les champs "PROG_ID" et "NOM", et enfin une table "TYPE_DROITS" avec les champs "TYPE_DROIT" et "LIBELLE".
 
Je veux retrouver tous les droits qu'on les utilisateurs de la société "Microsoft" sur le programme "Excel".
 
Premier jet :

Code :
  1. SELECT EMPLOYE.NOM, TYPE_DROITS.LIBELLE
  2. FROM SOCIETE, EMPOYE, DROITS, TYPE_DROITS, PROGRAMME
  3. WHERE SOCIETE.NOM = 'Microsoft'
  4. AND EMPLOYE.SOC_ID = SOCIETE.SOC_ID
  5. AND DROITS.EMP_ID = EMPLOYE.EMP_ID
  6. AND TYPE_DROITS.TYPE_DROIT = DROITS.TYPE_DROIT
  7. AND PROGRAMME.PROG_ID = DROITS.PROG_ID
  8. AND PROGRAMME.NOM = 'Excel'


 
Maintenant, on va réfléchir un peu.
 
J'ai 50 programmes différents.
J'ai 10 sociétés
J'ai 50 000 employés
J'ai 2 types de droits
J'ai 250 000 droits
 
Quel est le point d'entrée ? (donc qu'on va mettre en dernier, puis précéder des tables les plus restrictives suivantes)
Du tac au tac, on se dit "table la plus restrictive = table où y'a le moins de lignes, donc c'est type_droits".
MIIIP ! Mauvaise réponse.
La table la plus restrictive est la table qui, AVANT filtre contient un maximum de données, mais APRES filtre en contient le moins.
C'est donc soit PROGRAMME, soit SOCIETE. Pour programme, on grade seulement 2% des lignes (1/50) alors que pour la table société, on en garde 10% (1/10).
On garde donc la table "PROGRAMME", qui est la plus restrictive.
Ensuite, on est tenté de mettre la table "SOCIETE" en second. En effet, c'est la seconde la plus restrictive.
Ratté. En effet, les SGBD n'aiment pas trop devoir boucler sur plusieurs tables à la fois sans savoir qui est rattaché avec qui. La seconde sera donc "DROITS". Entre "EMPLOYE" et "TYPE_DROITS", qui est la plus restrictive ensuite ? Type_droits ? Non. Pourquoi ? Parcequ'on ne va pas la filter du tout. A partir de là, elle n'apporte rien à l'optimisation, mise à par ajouter un varchar en mémoire pour chaque ligne de droits répondant à l'application "Excel". "EMPLOYE", par contre, va nous premettre de filtrer énormément dès qu'on va le lier à "SOCIETE". Allez, zou, on ajoute donc "EMPLOYE" et "SOCIETE". Et enfin, on rajoute "TYPE_DROITS". C'est pas vraiment ce qu'on avait imaginé au début !
 
Ca donne donc, après optimisation :
 

Code :
  1. SELECT EMPLOYE.NOM, TYPE_DROITS.LIBELLE
  2. FROM TYPE_DROITS, SOCIETE, EMPOYE, DROITS, PROGRAMME
  3. WHERE SOCIETE.NOM = 'Microsoft'
  4. AND EMPLOYE.SOC_ID = SOCIETE.SOC_ID
  5. AND DROITS.EMP_ID = EMPLOYE.EMP_ID
  6. AND TYPE_DROITS.TYPE_DROIT = DROITS.TYPE_DROIT
  7. AND PROGRAMME.PROG_ID = DROITS.PROG_ID
  8. AND PROGRAMME.NOM = 'Excel'


 
Vous noterez que "par chance", la version "optimisée" est quasi identique à la requête qui était venue littéralement. Ne vous y fiez pas, c'est uniquement parcequ'il y avait plus de programmes de que sociétés. Si les nombres de lignes de ces deux tables avait été inversés, ça aurait été totalement différent :
 

Code :
  1. SELECT EMPLOYE.NOM, TYPE_DROITS.LIBELLE
  2. FROM TYPE_DROITS, PROGRAMME, DROITS, EMPOYE, SOCIETE
  3. WHERE SOCIETE.NOM = 'Microsoft'
  4. AND EMPLOYE.SOC_ID = SOCIETE.SOC_ID
  5. AND DROITS.EMP_ID = EMPLOYE.EMP_ID
  6. AND TYPE_DROITS.TYPE_DROIT = DROITS.TYPE_DROIT
  7. AND PROGRAMME.PROG_ID = DROITS.PROG_ID
  8. AND PROGRAMME.NOM = 'Excel'


 
Voilà. Je sais pas si ça va être utile à certains, en tout cas, après avoir été confronté à une requête qui mettais plus de 2 minutes à s'éxécuter hier, j'ai juste interverti deux tables en appliquant cette logique, et le temps d'éxécution est tombé à 3 secondes ! Pourtant, les index utilisés notamment n'ont pas changés entre les deux versions, et les deux tables interchangées contenaient respectivement 68 et 12 lignes, sâchant que dans les deux cas, je ne gardais qu'une seule ligne, retrouvée en utilisant l'index unique de la primary key clusterée. Comme quoi, c'est pas de l'omptimisation en l'air !

Reply

Marsh Posté le 30-03-2005 à 21:03:17   

Reply

Marsh Posté le 30-03-2005 à 22:53:15    

c'est plus long que je croyais, je lirai ça demain mating :D


---------------
As the plane took off, the pilot turned to the co-pilot and said, “Have you ever flown solo?” Co-pilot: No. Typically I fly much higher than this.
Reply

Marsh Posté le 30-03-2005 à 23:00:25    

Flemmard :o

Reply

Marsh Posté le 30-03-2005 à 23:02:29    

C'est interressant mais t'as des chiffres ?
Si tu retestes avec des chiffres essaies de le faire sur une base dont t'as les scripts et file tes sql de test que jpuisse tenter sur mes bases (je bosse sur sql server, oracle et firebird régulièrement, et sur d'autres du style mysql et access de temps en temps...) j'aimerai bien vérifier tout ça :)

Reply

Marsh Posté le 31-03-2005 à 00:59:49    

Ben là, ça va être chaud, parceque la base en question fait quelques centaines de méga, et certaines tables font plusieurs centaintes de milliers de lignes...
Sans parler de la requête avec laquelle j'ai obtenu le résutlat (passer de 2 minutes à 3 secondes, c'est quand même pas mal :D) qui fait plus d'une cinquantaine de lignes (jointures sur une douzaine de tables, avec un joli union au milieu :D)
 
Mais demain, si j'y pense, je vais me faire une petite base de test pour valider ces résultats, et pourquoi pas affiner la méthode. En effet, j'ai remarqué en écrivant le topic qu'il y a une légère incohérence par rapport à la requête que j'ai ainsi optimisé. M'enfin globalement, ça reste vrai tout de même, mais pas parfait.


Message édité par Arjuna le 31-03-2005 à 01:00:01
Reply

Marsh Posté le 31-03-2005 à 08:05:44    

comme Glod2, je veux des chiffres ! ;)
 
mais si les chiffres sont probants, ce serait un bon plan que je m'y plonge :)
 
quid des Sybase, DB2/400 et mysql ?


---------------
As the plane took off, the pilot turned to the co-pilot and said, “Have you ever flown solo?” Co-pilot: No. Typically I fly much higher than this.
Reply

Marsh Posté le 31-03-2005 à 09:34:49    

L'idéal ce serait que tu essayes sous Oracle aussi, car il me semble que c'est un peu différent, et puis ça dépend du mode d'optimisation choisi (stats / rule).
Il me semblait que le SGBD ne prenait pas nécessairement les tables dans l'ordre inverse à celui indiqué ... mais j'ai fait quelques test simples et c'est apparemment le cas aussi sous Oracle :)
 
En tout cas, sous Oracle, on peut utiliser un "hint" pour s'assurer de l'ordre d'utilisation des tables :
 

Code :
  1. SELECT /*+ ordered */ *
  2. FROM CUSTOMER, ACCOUNT
  3. WHERE ...;


 
Cette requête lira CUSTOMER d'abord, puis ACCOUNT ...

Reply

Marsh Posté le 31-03-2005 à 10:35:15    

TBone a écrit :

comme Glod2, je veux des chiffres ! ;)
 
mais si les chiffres sont probants, ce serait un bon plan que je m'y plonge :)
 
quid des Sybase, DB2/400 et mysql ?


Jamais bossé sur ces 3 bases, donc je ne peux rien dire ;)
 
Cela dit, si Oracle et MS SQL Server, qui sont deux des ténors des SGBD courants, en profitent, je suppose que ce fonctionnement est applicable à la plupart des autres SGBD.
 
A vérifier tout de même.
 
Là, j'ai rien à faire au boulot, je ne sais pas pour combien de temps. Je vais commencer à me faire une base de test, et si j'ai le temps de faire des mesures, je vous préviens, sinon il faudra attendre ce soir ;)

Reply

Marsh Posté le 31-03-2005 à 10:56:16    

Bon, le script d'initialisation d'une base simple est lancé.
 
Le volume de données ne va pas être énorme, mais vu que je tourne sur un Pii 400 avec 384 Mo de mémoire et un HD à 5400 trm :/
 
Arf, c'est marrant, au boulot il vient d'y avoir une micro-coupure de courant. Mon PC est tellement lent qu'il s'en est même pas rendu compte :D Tous les autres ont rebooté... Ironique, puisque je suis le seul à pas travailler :lol:
 
 
Le script d'allimentation de la base (SQL Server) :
J'espère que ça sera quand même représentatif d'une vraie base. (je mettrai le script de la base quand il aura fini, parceque là, ça ramme à mort, dans le forum ça écrit ce que j'écrit 20 secondes après, c'est pratique pour corriger les fautes de frappe :D)
 

Code :
  1. declare @i as numeric
  2. declare @j as numeric
  3. set @i = 1
  4. while @i <= 1000
  5. begin
  6. insert into t1 (t1_id, nom) values (@i, 'test t1 ' + cast(@i as varchar))
  7. insert into t3 (t3_id, nom) values (@i, 'test t3 ' + cast(@i as varchar))
  8. set @j = 1
  9. while @j <= 1000
  10. begin
  11.  insert into t2 (t1_id, t2_id, nom) values (@i, @j, 'test t2 ' + cast(@i as varchar) + ' ' + cast(@j as varchar))
  12.  insert into t23 (t1_id, t2_id, t3_id, libelle) values (@i, @j, @i, 'test t23 ' + cast(@i as varchar) + ' ' + cast(@j as varchar))
  13.  set @j = @j + 1
  14. end
  15. set @i = @i + 1
  16. end


Message édité par Arjuna le 31-03-2005 à 10:58:39
Reply

Marsh Posté le 31-03-2005 à 11:00:09    

:heink:
 
En 6 minutes, il a fait 52 passages dans la boucle principale. C'est pas gagné :D

Reply

Marsh Posté le 31-03-2005 à 11:00:09   

Reply

Marsh Posté le 31-03-2005 à 11:06:49    

Ca ne s'applique pas a MySQL, c'est l'optimizer qui va decider dans quel sens doit se faire la jointure.
Si tu veux specifier explicitement l'ordre de la jointure et court-circuiter l'optimizer il faut utiliser la syntaxe  A STRAIGHT_JOIN B au lieu de A,B. (donc la majorite des cas MySQL ordonne les tables de facon optimale, sauf cas assez rare et un peu tordu :))


Message édité par joce le 31-03-2005 à 11:09:24
Reply

Marsh Posté le 31-03-2005 à 11:10:34    

ese-aSH a écrit :

hm je savais pas du tout ca, mais ca me semble gros quand meme, ils pourraient pas developper un pti truc qui s'arrange pour trouver l'ordre optimal des tables dans ces  cas la ^^ (genre en essayant un peu toutes les combinaisons possibles et en gardant que la meilleure).
parceque bon les tables peuvent evoluer et du coup l'ordre optimal aussi non?


En effet, comme je l'ai souligné, l'ordre des tables est fortement influencé par leur contenu. Ainsi, les optimisations qui sont vraies aujourd'hui ne le sont pas forcément demain (d'où ma réaction aux questions du genre "donnez-moi vos règles principales pour optimiser un serveur SQL", phrase dénuée de sens, puisque ça dépend de "tout" ).
 
Comme l'a souligné Beegee, sous Oracle (peut-être sur SQL Server aussi, je ne maîtrise pas suffisament) on peut indiquer au parseur SQL différents modes d'optimisation. Tous ne sont pas aussi impactés par l'ordre des tables. Cela dit, cela reste le paramètre par défaut de ces deux SGBD, et pas conséquent, ce qu'on trouve en exploitation sur une très large majorité des serveurs.
Sinon, pour ce qui est de l'automatisation, c'est pas si évident à faire. Moi, je connais d'un point de vue logique ma base, et j'ai une idée précise du volume traîté et du volume attendu.
Le SGBD est incapable d'avoir une vision aussi globale. Donc pour optimiser, il faudrait qu'il fasse la requête par tous les moyens d'optimisation connus, puis qu'il garde la meilleur pour les utilisations ultérieures, ce qui est un peu lent :D
Deplus, la phase d'optimisation étant, dans la majorité des cas, la phase la plus lente de l'éxécution d'une requête, faciliter la tâche au parseur n'est pas plus mal, rien que là, on gagne en temps, sans compter sur l'exécution elle-même.

Reply

Marsh Posté le 31-03-2005 à 11:13:43    

joce a écrit :

Ca ne s'applique pas a MySQL, c'est l'optimizer qui va decider dans quel sens doit se faire la jointure.
Si tu veux specifier explicitement l'ordre de la jointure et court-circuiter l'optimizer il faut utiliser la syntaxe  A STRAIGHT_JOIN B au lieu de A,B. (donc la majorite des cas MySQL ordonne les tables de facon optimale, sauf cas assez rare et un peu tordu :))


C'est le cas aussi d'Oracle et SQL Server normalement. Sur une requête "simple", on a rarement des problèmes de ce genre, l'optimisation montre des résultats très faibles.
Cela dit, comme tu dis, dans les cas "tordus" (et quand on bosse sur des applis comme celles sur lesquelles je bosse, y'a que ça :D), je pense que MySQL va lui aussi s'emmêler les pinceaux, et à ce moment, l'optimiseur prend les tables dans l'ordre indiqué dans la clause FROM.
Je pense que dans tous les cas, étant donné que cette optimisation est parmis les plus simples possibles, il vaut mieu la faire systématiquement, au moins on est sûr d'avoir le meilleur résultat possible ;)
Allez Joce, réécris tout de suis le forum :o ;)


Message édité par Arjuna le 31-03-2005 à 11:14:08
Reply

Marsh Posté le 31-03-2005 à 11:19:26    

Joce, d'ailleurs, tu devrais pouvoir confirmer. Il me semble avoir lu (mais ça fait un bail) que pour l'optimiseur, en réalité, il tente de trouver le moyen d'exécuter la requête le plus rapidement possible (logique).
 
Mais si au bout d'un délais X il n'a pas encore trouvé d'optimisation satisfaisante, il annule tout, et lance la requête sans optimisation, afin d'éviter de perdre trop de temps à optimiser (en fin de compte, c'est con de passer 5 minutes à optimiser une jointure super complexe entre 100 tables, si chacune contient 2 lignes ;))
 
Je pense que si c'est bien le fonctionnement, c'est à ce moment que cette syntaxe devient vraiment utile.


Message édité par Arjuna le 31-03-2005 à 11:20:07
Reply

Marsh Posté le 31-03-2005 à 11:26:29    

Sinon, quand je parle de requêtes "complexes", c'est de ce type de truc que je parle ;)
 
C'est cette requête qui est passée de 2 minutes à 3 secondes après modification ;)
 

Code :
  1. CREATE procedure icsd_ARTAssetList
  2. (
  3. @orgid   int,
  4. @userid   int,
  5. @date1   datetime,
  6. @date2   datetime,
  7. @invdate1  datetime,
  8. @invdate2  datetime,
  9. @leasescheduleid int = 0,
  10. @leasecontractid int = 0,
  11. @duration  int = 0,
  12. @brand   varchar(255) = '%',
  13. @lineOfProduct  varchar(255) = '%',
  14. @manufacturerRef varchar(255) = '',
  15. @description  varchar(255) = '%',
  16. @category  varchar(255) = '',
  17. @cat1   varchar(255) = '',
  18. @cat2   varchar(255) = '',
  19. @cat3   varchar(255) = '',
  20. @serialnumber  varchar(255) = '%',
  21. @supplier  varchar(255) = '%',
  22. @invoice  varchar(255) = '%',
  23. @ref1   varchar(255) = '%',
  24. @ref2   varchar(255) = '%',
  25. @ref3   varchar(255) = '%',
  26. @ref4   varchar(255) = '%',
  27. @ref5   varchar(255) = '%',
  28. @assetid  int = 0,
  29. @orderby  varchar(255) = 'org'
  30. )
  31. AS
  32. BEGIN
  33. select orgid,
  34. name,
  35. leasescheduleid,
  36. schedule,
  37. -- inventory row
  38. -- inventory autoincrement
  39. manufacturerRef,
  40. SerialNumber,
  41. description,
  42. brand,
  43. lineOfProduct,
  44. category,
  45. cat1,
  46. cat2,
  47. cat3,
  48. supplier,
  49. invoice,
  50. invcreation,
  51. ref1,
  52. ref2,
  53. ref3,
  54. ref4,
  55. ref5,
  56. leasecontractid,
  57. contract,
  58. assetid
  59. from (
  60.  select o.orgid,
  61.   o.name,
  62.   ls.leasescheduleid,
  63.   ls.schedule,
  64. -- inventory row
  65. -- inventory autoincrement
  66.   a.ProductCode manufacturerRef,
  67.   a.SerialNumber,
  68.   a.description,
  69.   a.manufacture brand,
  70.   a.brand lineOfProduct,
  71.   zg.category,
  72.   a.catalogType cat1,
  73.   at1.value cat2,
  74.   at2.value cat3,
  75.   v.name supplier,
  76.   zf.numero invoice,
  77.   zf.datecreation invcreation,
  78.   a.reference1 ref1,
  79.   a.reference2 ref2,
  80.   a.reference3 ref3,
  81.   a.reference4 ref4,
  82.   a.reference5 ref5,
  83.   lc.leasecontractid,
  84.   lc.contract,
  85.   a.assetid
  86.  from organization v, zfactures zf, zfactureasset zfa, sdViewAttributesValues at2, sdViewAttributesValues at1, zCatalogTypeGenre zg, asset a, zloyer zl, contractline cl, leasecontract lc, leaseschedule ls, zUserContractPerimeter zu, organization o, UserOrg uo
  87.  where (uo.orgid = @orgid or @orgid = 0)
  88.  and uo.userid = @userid
  89.  and o.type = 'C'
  90.  and o.active = 'A'
  91.  and o.orgid = uo.orgid
  92.  and zu.userid = uo.userid
  93.  and zu.orgid = uo.orgid
  94.  and (zu.scheduleid = @leasescheduleid or @leasescheduleid = 0)
  95.  and ls.orgid = o.orgid
  96.  and ls.leasescheduleid = zu.scheduleid
  97.  and ls.active = 'A'
  98.  and ls.userights = 1
  99.  and lc.orgid = ls.orgid
  100.  and lc.leasescheduleid = ls.leasescheduleid
  101.  and (lc.leasecontractid = @leasecontractid or @leasecontractid = 0)
  102.  and (lc.leasecontractid = zu.contractid or zu.contractid = -1)
  103.  and lc.active = 'A'
  104.  and (lc.leasestopdate between @date1 and @date2 or @date1 > @date2 or @date1 is null or @date2 is null)
  105.  and (datediff(m, lc.leasestartdate, lc.leasestopdate) = @duration or @duration = 0)
  106.  and cl.contractlineid = (select max(cl2.contractlineid)
  107.      from contractline cl2
  108.      where cl2.leasecontractid = lc.leasecontractid
  109.      and cl2.active = 'A')
  110.  and cl.active = 'A'
  111.  and zl.leasecontractid = cl.leasecontractid
  112.  and zl.contractlineid = cl.ContractLineID
  113.  and a.orgid = o.orgid
  114.  and (a.assetid = @assetid or @assetid = 0)
  115.  and a.linetype in ('B', 'S')
  116.  and a.assetid = zl.assetid
  117.  and a.manufacture like '%' + @brand + '%'
  118.  and a.brand like  '%' + @lineOfProduct + '%'
  119.  and a.description like  '%' + @description + '%'
  120.  and (a.catalogtype = @cat1 or @cat1 = '')
  121.  and (a.productCode = @manufacturerRef or @manufacturerRef = '')
  122.  and zg.catalogType = a.catalogType
  123.  and zg.orgid = a.orgid
  124.  and (zg.category = @category or @category = '')
  125.  and at1.assetid =* a.assetid
  126.  and at1.attributename =* zg.subcat1
  127.  and (at1.value = @cat2 or @cat2 = '')
  128.  and at2.assetid =* a.assetid
  129.  and at2.attributename =* zg.subcat2
  130.  and (at2.value = @cat3 or @cat3 = '')
  131.  and zfa.assetid = a.assetid
  132.  and zfa.leasescheduleid = ls.leasescheduleid
  133.  and zfa.leasecontractid = lc.leasecontractid
  134.  and zfa.contractlineid = cl.contractlineid
  135.  and zf.factureid = zfa.factureid
  136.  and zf.type = 'F'
  137.  and a.serialNumber like  '%' + @serialNumber + '%'
  138.  and v.orgid = a.VendorOrgID
  139.  and v.type = 'V'
  140.  and zf.numero like  '%' + @invoice + '%'
  141.  and (zf.datecreation between @invdate1 and @invdate2 or @invdate1 > @invdate2 or @invdate1 is null or @invdate2 is null)
  142.  and a.reference1 like  '%' + @ref1 + '%'
  143.  and a.reference2 like  '%' + @ref2 + '%'
  144.  and a.reference3 like  '%' + @ref3 + '%'
  145.  and a.reference4 like  '%' + @ref4 + '%'
  146.  and a.reference5 like  '%' + @ref5 + '%'
  147.  union all
  148.  select o.orgid,
  149.   o.name,
  150.   ls.leasescheduleid,
  151.   ls.schedule,
  152. -- inventory row
  153. -- inventory autoincrement
  154.   a.ProductCode manufacturerRef,
  155.   a.SerialNumber,
  156.   a.description,
  157.   a.manufacture brand,
  158.   a.brand lineOfProduct,
  159.   zg.category,
  160.   a.catalogType cat1,
  161.   at1.value cat2,
  162.   at2.value cat3,
  163.   v.name supplier,
  164.   zf.numero invoice,
  165.   zf.datecreation invcreation,
  166.   a.reference1 ref1,
  167.   a.reference2 ref2,
  168.   a.reference3 ref3,
  169.   a.reference4 ref4,
  170.   a.reference5 ref5,
  171.   lc.leasecontractid,
  172.   lc.contract,
  173.   a.assetid
  174.  from organization v, zfactures zf, zfactureasset zfa, sdViewAttributesValues at2, sdViewAttributesValues at1, zCatalogTypeGenre zg, asset a, zloyer zl, contractline cl, leasecontract lc, leaseschedule ls, organization o, UserOrg uo
  175.  where (uo.orgid = @orgid or @orgid = 0)
  176.  and uo.userid = @userid
  177.  and o.type = 'C'
  178.  and o.active = 'A'
  179.  and o.orgid = uo.orgid
  180.  and ls.orgid = o.orgid
  181.  and (ls.leasescheduleid = @leasescheduleid or @leasescheduleid = 0)
  182.  and ls.active = 'A'
  183.  and ls.userights = 0
  184.  and lc.orgid = ls.orgid
  185.  and lc.leasescheduleid = ls.leasescheduleid
  186.  and lc.active = 'A'
  187.  and (lc.leasecontractid = @leasecontractid or @leasecontractid = 0)
  188.  and (lc.leasestopdate between @date1 and @date2 or @date1 is null or @date2 is null or @date1 > @date2)
  189.  and (datediff(m, lc.leasestartdate, lc.leasestopdate) = @duration or @duration = 0)
  190.  and cl.contractlineid = (select max(cl2.contractlineid)
  191.      from contractline cl2
  192.      where cl2.leasecontractid = lc.leasecontractid
  193.      and cl2.active = 'A')
  194.  and cl.active = 'A'
  195.  and zl.leasecontractid = cl.leasecontractid
  196.  and zl.contractlineid = cl.ContractLineID
  197.  and a.orgid = o.orgid
  198.  and (a.assetid = @assetid or @assetid = 0)
  199.  and a.linetype in ('B', 'S')
  200.  and a.assetid = zl.assetid
  201.  and a.manufacture like  '%' + @brand + '%'
  202.  and a.brand like  '%' + @lineOfProduct + '%'
  203.  and a.description like  '%' + @description + '%'
  204.  and (a.catalogtype = @cat1 or @cat1 = '')
  205.  and (a.productCode = @manufacturerRef or @manufacturerRef = '')
  206.  and zg.catalogType = a.catalogType
  207.  and zg.orgid = a.orgid
  208.  and (zg.category = @category or @category = '')
  209.  and at1.assetid =* a.assetid
  210.  and at1.attributename =* zg.subcat1
  211.  and (at1.value = @cat2 or @cat2 = '')
  212.  and at2.assetid =* a.assetid
  213.  and at2.attributename =* zg.subcat2
  214.  and (at2.value = @cat3 or @cat3 = '')
  215.  and zfa.assetid = a.assetid
  216.  and zfa.leasescheduleid = ls.leasescheduleid
  217.  and zfa.leasecontractid = lc.leasecontractid
  218.  and zfa.contractlineid = cl.contractlineid
  219.  and zf.factureid = zfa.factureid
  220.  and zf.type = 'F'
  221.  and a.serialNumber like  '%' + @serialNumber + '%'
  222.  and v.orgid = a.VendorOrgID
  223.  and v.type = 'V'
  224.  and zf.numero like  '%' + @invoice + '%'
  225.  and (zf.datecreation between @invdate1 and @invdate2 or @invdate1 > @invdate2 or @invdate1 is null or @invdate2 is null)
  226.  and a.reference1 like  '%' + @ref1 + '%'
  227.  and a.reference2 like  '%' + @ref2 + '%'
  228.  and a.reference3 like  '%' + @ref3 + '%'
  229.  and a.reference4 like  '%' + @ref4 + '%'
  230.  and a.reference5 like  '%' + @ref5 + '%'
  231. ) tmp
  232. order by case @orderby
  233.  when 'org'  then name
  234.  when 'manufacturerRef' then manufacturerRef
  235.  when 'SerialNumber' then SerialNumber
  236.  when 'description' then description
  237.  when 'brand'  then brand
  238.  when 'lineOfProduct' then lineOfProduct
  239.  when 'category'  then category
  240.  when 'cat1'  then cat1
  241.  when 'cat2'  then cat2
  242.  when 'cat3'  then cat3
  243.  when 'supplier'  then supplier
  244.  when 'numero'  then invoice
  245.  when 'datecreation' then convert(varchar(8), invcreation, 112)
  246.  when 'reference1' then ref1
  247.  when 'reference2' then ref2
  248.  when 'reference3' then ref3
  249.  when 'reference4' then ref4
  250.  when 'reference5' then ref5
  251. end, case @orderby
  252.  when 'org'  then schedule
  253.  when 'manufacturerRef' then manufacturerRef
  254.  when 'SerialNumber' then SerialNumber
  255.  when 'description' then description
  256.  when 'brand'  then brand
  257.  when 'lineOfProduct' then lineOfProduct
  258.  when 'category'  then category
  259.  when 'cat1'  then cat1
  260.  when 'cat2'  then cat2
  261.  when 'cat3'  then cat3
  262.  when 'supplier'  then supplier
  263.  when 'numero'  then invoice
  264.  when 'datecreation' then convert(varchar(8), invcreation, 112)
  265.  when 'reference1' then ref1
  266.  when 'reference2' then ref2
  267.  when 'reference3' then ref3
  268.  when 'reference4' then ref4
  269.  when 'reference5' then ref5
  270. end
  271. END
  272. GO


 
Vous comprendrez que je vais avoir du mal à vous fournir une base de test correspondant ;)

Reply

Marsh Posté le 31-03-2005 à 11:37:33    

Arjuna a écrit :

Je pense que dans tous les cas, étant donné que cette optimisation est parmis les plus simples possibles, il vaut mieu la faire systématiquement, au moins on est sûr d'avoir le meilleur résultat possible ;)
Allez Joce, réécris tout de suis le forum :o ;)

t'inquietes le forum est deja ecrit de facon optimale au niveau des requetes :D
 
Pour MySQL, y a bcp de changement qui ont ete introduit dans MySQL 5 et qui permettent de controller le comportement de l'optimizer et l'heuristique qu'il va utiliser dans le cas des jointures.
Je te recommande de lire :
 
http://dev.mysql.com/doc/mysql/en/ [...] mizer.html
 
 

Reply

Marsh Posté le 31-03-2005 à 11:39:12    

Je lirai ça plus tard :D Je bosse pas du tout avec MySQL :D
 
Bon, voilà ma base de test :

Code :
  1. /****** Objet :  Base de données testsd    Date du script : 31/03/2005 11:31:24 ******/
  2. IF EXISTS (SELECT name FROM master.dbo.sysdatabases WHERE name = N'testsd')
  3. DROP DATABASE [testsd]
  4. GO
  5. CREATE DATABASE [testsd]  ON (NAME = N'testsd_Data', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL\data\testsd_Data.MDF' , SIZE = 42, FILEGROWTH = 10%) LOG ON (NAME = N'testsd_Log', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL\data\testsd_Log.LDF' , SIZE = 1, FILEGROWTH = 10%)
  6. GO
  7. exec sp_dboption N'testsd', N'autoclose', N'false'
  8. GO
  9. exec sp_dboption N'testsd', N'bulkcopy', N'false'
  10. GO
  11. exec sp_dboption N'testsd', N'trunc. log', N'false'
  12. GO
  13. exec sp_dboption N'testsd', N'torn page detection', N'true'
  14. GO
  15. exec sp_dboption N'testsd', N'read only', N'false'
  16. GO
  17. exec sp_dboption N'testsd', N'dbo use', N'false'
  18. GO
  19. exec sp_dboption N'testsd', N'single', N'false'
  20. GO
  21. exec sp_dboption N'testsd', N'autoshrink', N'false'
  22. GO
  23. exec sp_dboption N'testsd', N'ANSI null default', N'false'
  24. GO
  25. exec sp_dboption N'testsd', N'recursive triggers', N'false'
  26. GO
  27. exec sp_dboption N'testsd', N'ANSI nulls', N'false'
  28. GO
  29. exec sp_dboption N'testsd', N'concat null yields null', N'false'
  30. GO
  31. exec sp_dboption N'testsd', N'cursor close on commit', N'false'
  32. GO
  33. exec sp_dboption N'testsd', N'default to local cursor', N'false'
  34. GO
  35. exec sp_dboption N'testsd', N'quoted identifier', N'false'
  36. GO
  37. exec sp_dboption N'testsd', N'ANSI warnings', N'false'
  38. GO
  39. exec sp_dboption N'testsd', N'auto create statistics', N'true'
  40. GO
  41. exec sp_dboption N'testsd', N'auto update statistics', N'true'
  42. GO
  43. if( ( (@@microsoftversion / power(2, 24) = 8) and (@@microsoftversion & 0xffff >= 724) ) or ( (@@microsoftversion / power(2, 24) = 7) and (@@microsoftversion & 0xffff >= 1082) ) )
  44. exec sp_dboption N'testsd', N'db chaining', N'false'
  45. GO
  46. use [testsd]
  47. GO
  48. if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[FK_t2_t1]') and OBJECTPROPERTY(id, N'IsForeignKey') = 1)
  49. ALTER TABLE [dbo].[t2] DROP CONSTRAINT FK_t2_t1
  50. GO
  51. if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[FK_t23_t3]') and OBJECTPROPERTY(id, N'IsForeignKey') = 1)
  52. ALTER TABLE [dbo].[t23] DROP CONSTRAINT FK_t23_t3
  53. GO
  54. if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[FK_t23_t2]') and OBJECTPROPERTY(id, N'IsForeignKey') = 1)
  55. ALTER TABLE [dbo].[t23] DROP CONSTRAINT FK_t23_t2
  56. GO
  57. /****** Objet :  Table [dbo].[t23]    Date du script : 31/03/2005 11:31:30 ******/
  58. if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[t23]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
  59. drop table [dbo].[t23]
  60. GO
  61. /****** Objet :  Table [dbo].[t2]    Date du script : 31/03/2005 11:31:30 ******/
  62. if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[t2]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
  63. drop table [dbo].[t2]
  64. GO
  65. /****** Objet :  Table [dbo].[t1]    Date du script : 31/03/2005 11:31:30 ******/
  66. if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[t1]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
  67. drop table [dbo].[t1]
  68. GO
  69. /****** Objet :  Table [dbo].[t3]    Date du script : 31/03/2005 11:31:30 ******/
  70. if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[t3]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
  71. drop table [dbo].[t3]
  72. GO
  73. /****** Objet :  Nom d'accès interclass_user    Date du script : 31/03/2005 11:31:24 ******/
  74. if not exists (select * from master.dbo.syslogins where loginname = N'interclass_user')
  75. BEGIN
  76. declare @logindb nvarchar(132), @loginlang nvarchar(132) select @logindb = null, @loginlang = N'Français'
  77. if @logindb is null or not exists (select * from master.dbo.sysdatabases where name = @logindb)
  78.  select @logindb = N'master'
  79. if @loginlang is null or (not exists (select * from master.dbo.syslanguages where name = @loginlang) and @loginlang <> N'us_english')
  80.  select @loginlang = @@language
  81. exec sp_addlogin N'interclass_user', null, @logindb, @loginlang
  82. END
  83. GO
  84. /****** Objet :  Nom d'accès BUILTIN\Administrateurs    Date du script : 31/03/2005 11:31:24 ******/
  85. exec sp_addsrvrolemember N'BUILTIN\Administrateurs', sysadmin
  86. GO
  87. /****** Objet :  Utilisateur dbo    Date du script : 31/03/2005 11:31:24 ******/
  88. /****** Objet :  Table [dbo].[t1]    Date du script : 31/03/2005 11:31:32 ******/
  89. CREATE TABLE [dbo].[t1] (
  90. [t1_id] [numeric](18, 0) NOT NULL ,
  91. [nom] [varchar] (50) NOT NULL
  92. ) ON [PRIMARY]
  93. GO
  94. /****** Objet :  Table [dbo].[t3]    Date du script : 31/03/2005 11:31:33 ******/
  95. CREATE TABLE [dbo].[t3] (
  96. [t3_id] [numeric](18, 0) NOT NULL ,
  97. [nom] [varchar] (50) NOT NULL
  98. ) ON [PRIMARY]
  99. GO
  100. /****** Objet :  Table [dbo].[t2]    Date du script : 31/03/2005 11:31:34 ******/
  101. CREATE TABLE [dbo].[t2] (
  102. [t1_id] [numeric](18, 0) NOT NULL ,
  103. [t2_id] [numeric](18, 0) NOT NULL ,
  104. [nom] [varchar] (50) NOT NULL
  105. ) ON [PRIMARY]
  106. GO
  107. /****** Objet :  Table [dbo].[t23]    Date du script : 31/03/2005 11:31:34 ******/
  108. CREATE TABLE [dbo].[t23] (
  109. [t1_id] [numeric](18, 0) NOT NULL ,
  110. [t2_id] [numeric](18, 0) NOT NULL ,
  111. [t3_id] [numeric](18, 0) NOT NULL ,
  112. [libelle] [varchar] (50) NOT NULL
  113. ) ON [PRIMARY]
  114. GO
  115. ALTER TABLE [dbo].[t1] WITH NOCHECK ADD
  116. CONSTRAINT [PK_t1] PRIMARY KEY  CLUSTERED
  117. (
  118.  [t1_id]
  119. )  ON [PRIMARY]
  120. GO
  121. ALTER TABLE [dbo].[t3] WITH NOCHECK ADD
  122. CONSTRAINT [PK_t3] PRIMARY KEY  CLUSTERED
  123. (
  124.  [t3_id]
  125. )  ON [PRIMARY]
  126. GO
  127. ALTER TABLE [dbo].[t2] WITH NOCHECK ADD
  128. CONSTRAINT [PK_t2] PRIMARY KEY  CLUSTERED
  129. (
  130.  [t1_id],
  131.  [t2_id]
  132. )  ON [PRIMARY]
  133. GO
  134. ALTER TABLE [dbo].[t23] WITH NOCHECK ADD
  135. CONSTRAINT [PK_t23] PRIMARY KEY  CLUSTERED
  136. (
  137.  [t1_id],
  138.  [t2_id],
  139.  [t3_id]
  140. )  ON [PRIMARY]
  141. GO
  142. ALTER TABLE [dbo].[t2] ADD
  143. CONSTRAINT [FK_t2_t1] FOREIGN KEY
  144. (
  145.  [t1_id]
  146. ) REFERENCES [dbo].[t1] (
  147.  [t1_id]
  148. )
  149. GO
  150. ALTER TABLE [dbo].[t23] ADD
  151. CONSTRAINT [FK_t23_t2] FOREIGN KEY
  152. (
  153.  [t1_id],
  154.  [t2_id]
  155. ) REFERENCES [dbo].[t2] (
  156.  [t1_id],
  157.  [t2_id]
  158. ),
  159. CONSTRAINT [FK_t23_t3] FOREIGN KEY
  160. (
  161.  [t3_id]
  162. ) REFERENCES [dbo].[t3] (
  163.  [t3_id]
  164. )
  165. GO


 
Vais boire un café, et après je fais quelques tests, mais j'ai peur qu'il y ait trop peu de tables pour que ce soit significatif :spamafote:

Reply

Marsh Posté le 31-03-2005 à 12:01:56    

j'ai deja eu un cas ou sur deux tables l'optimizer se plantait dans l'ordre de la jointure a cause d'un mauvais calcul de la cardinalite par l'optimizer :D une honte :o

Reply

Marsh Posté le 31-03-2005 à 12:15:42    

Bon, c'est chiant, la base est trop petite. Vu que toutes les requêtes (sauf une) mettent 0 secondes à tourner, je ne peux rien mesurer.
 
Par contre, j'ai remarqué un point relativement important dans le domaine des optimisation.
 
T1
-----
t1_id PK / Culstered
nom
 
T2
-----
t1_id PK / Clustered / FK vers t1.t1_id
t2_id PK / Clustered
nom
 
T1
-----
t3_id PK / Culstered
nom
 
T23
-----
t1_id PK / Clustered / FK vers t2.t1_id et t2.t2_id
t2_id PK / Clustered / FK vers t2.t1_id et t2.t2_id
t3_id PK / Clustered / FK vers t3.t3_id
libelle
 
Il y a quelques temps, j'ai annoncé, sûr de moi, que les FK ne servaient à rien au niveau optimisation des requêtes de type SELECT. En réponse, on m'a volé dans les plumes en me disant que c'était totalement faux.
J'ai la preuve, chiffres à l'appui que si, ça ne sert ABSOLUMENT à rien. Même pire, ça peu, dans certains cas, ralentir les requêtes, à cause de l'ajout de tables de correspondances inutiles.
 
Si je suis mes FK, je dois passer par T2, puis vers T1 pour lier T23 à T1.
Mais vu que T23 contient t1_id, je peux aussi me passer du passage par T2, et faire fi des FK.
 
Requêtes :

Code :
  1. select t3.t3_id, t1.nom, t23.libelle, t3.nom
  2. from t3, t23, t2, t1
  3. where t1.t1_id between 200 and 210
  4. and t2.t1_id = t1.t1_id
  5. and t23.t1_id = t2.t1_id
  6. and t23.t2_id = t2.t2_id
  7. and t3.t3_id = t23.t3_id
  8. -- 2 secondes
  9. select t3.t3_id, t1.nom, t23.libelle, t3.nom
  10. from t3, t23, t1
  11. where t1.t1_id between 200 and 210
  12. and t23.t1_id = t1.t1_id
  13. and t3.t3_id = t23.t3_id
  14. -- 0 secondes


 
Les résultats sont sans appel : passer par la FK est énormément plus lent que de passer outre.

Reply

Marsh Posté le 31-03-2005 à 13:15:13    

les FK servent surtout de garde-fou quand on fait joujou avec les datas (non ?)


Message édité par TBone le 31-03-2005 à 13:15:28

---------------
As the plane took off, the pilot turned to the co-pilot and said, “Have you ever flown solo?” Co-pilot: No. Typically I fly much higher than this.
Reply

Marsh Posté le 31-03-2005 à 17:46:57    

Ben oui. Mais les index générés par les FK n'apportent rien à l'optimisation.

Reply

Marsh Posté le 05-04-2005 à 17:39:50    

J'ai quelques nouveaux chiffres.
 
Je viens de changer une procédure du même type que celle que j'avais posté. Je suis passé de 19 secondes à 2 secondes, en prenant comme point d'entrée une table qui à première vue n'y avait rien à faire.
 
J'ai un user. Un user a accès à plusieurs organisations. Une organisation contient des schedule. Un schedule contient des contract. Un contract contient des contractline. Une contractline correspond à un loyer. Un loyer correspond à un asset. (plus quelques autres tables, mais ça c'est les principales).
 
Dans l'asset, j'ai toutes les FK correspondant aux autres tables (sauf loyer, qui est une table de correspondance).
 
Au départ, j'étais parti de l'utilisateur, puis des organisations, des schedules, etc.
 
Finalement, je suis parti d'Asset. Et c'est plus rapide !
 
En fait, plus ça va, et j'ai l'impression que la méthode est légèrement différente : il faut que la table contenant le plus de lignes soit la première. Ca expliquerait pourquoi MySQL peut optimiser de façon plus efficace que les autres SGBD, puisque le COUNT(*) est stocké dans la base pour chaque table, alors qu'aucun autre SGBD ne stocke cette infos, résultat, il ne peuvent que faire une approximation du nombre de ligne lors de l'optimisation.


Message édité par Arjuna le 05-04-2005 à 17:40:30
Reply

Marsh Posté le 05-04-2005 à 17:41:32    

Pour résumer le bordel :
 

Code :
  1. CREATE procedure icsd_ARTAssetList
  2. (
  3. @orgid   int,
  4. @userid   int,
  5. @date1   datetime,
  6. @date2   datetime,
  7. @invdate1  datetime,
  8. @invdate2  datetime,
  9. @leasescheduleid int = 0,
  10. @leasecontractid int = 0,
  11. @duration  int = 0,
  12. @brand   varchar(255) = '%',
  13. @lineOfProduct  varchar(255) = '%',
  14. @manufacturerRef varchar(255) = '',
  15. @description  varchar(255) = '%',
  16. @category  varchar(255) = '',
  17. @cat1   varchar(255) = '',
  18. @cat2   varchar(255) = '',
  19. @cat3   varchar(255) = '',
  20. @serialnumber  varchar(255) = '%',
  21. @supplier  varchar(255) = '%',
  22. @invoice  varchar(255) = '%',
  23. @ref1   varchar(255) = '%',
  24. @ref2   varchar(255) = '%',
  25. @ref3   varchar(255) = '%',
  26. @ref4   varchar(255) = '%',
  27. @ref5   varchar(255) = '%',
  28. @assetid  int = 0,
  29. @orderby  varchar(255) = 'org'
  30. )
  31. AS
  32. BEGIN
  33. select orgid,
  34. name,
  35. leasescheduleid,
  36. schedule,
  37. -- inventory row
  38. -- inventory autoincrement
  39. manufacturerRef,
  40. SerialNumber,
  41. description,
  42. brand,
  43. lineOfProduct,
  44. category,
  45. cat1,
  46. cat2,
  47. cat3,
  48. supplier,
  49. invoice,
  50. invcreation,
  51. ref1,
  52. ref2,
  53. ref3,
  54. ref4,
  55. ref5,
  56. leasecontractid,
  57. contract,
  58. assetid
  59. from (
  60.  select o.orgid,
  61.   o.name,
  62.   ls.leasescheduleid,
  63.   ls.schedule,
  64. -- inventory row
  65. -- inventory autoincrement
  66.   a.ProductCode manufacturerRef,
  67.   a.SerialNumber,
  68.   a.description,
  69.   a.manufacture brand,
  70.   a.brand lineOfProduct,
  71.   zg.category,
  72.   a.catalogType cat1,
  73.   at1.value cat2,
  74.   at2.value cat3,
  75.   v.name supplier,
  76.   zf.numero invoice,
  77.   zf.datecreation invcreation,
  78.   a.reference1 ref1,
  79.   a.reference2 ref2,
  80.   a.reference3 ref3,
  81.   a.reference4 ref4,
  82.   a.reference5 ref5,
  83.   lc.leasecontractid,
  84.   lc.contract,
  85.   a.assetid
  86.  from organization v, zfactures zf, zfactureasset zfa, sdViewAttributesValues at2, sdViewAttributesValues at1, zCatalogTypeGenre zg, asset a, zloyer zl, contractline cl, leasecontract lc, leaseschedule ls, zUserContractPerimeter zu, organization o, UserOrg uo
  87.  where (uo.orgid = @orgid or @orgid = 0)
  88.  and uo.userid = @userid
  89.  and o.type = 'C'
  90.  and o.active = 'A'
  91.  and o.orgid = uo.orgid
  92.  and zu.userid = uo.userid
  93.  and zu.orgid = uo.orgid
  94.  and (zu.scheduleid = @leasescheduleid or @leasescheduleid = 0)
  95.  and ls.orgid = o.orgid
  96.  and ls.leasescheduleid = zu.scheduleid
  97.  and ls.active = 'A'
  98.  and ls.userights = 1
  99.  and lc.orgid = ls.orgid
  100.  and lc.leasescheduleid = ls.leasescheduleid
  101.  and (lc.leasecontractid = @leasecontractid or @leasecontractid = 0)
  102.  and (lc.leasecontractid = zu.contractid or zu.contractid = -1)
  103.  and lc.active = 'A'
  104.  and (lc.leasestopdate between @date1 and @date2 or @date1 > @date2 or @date1 is null or @date2 is null)
  105.  and (datediff(m, lc.leasestartdate, lc.leasestopdate) = @duration or @duration = 0)
  106.  and cl.contractlineid = (select max(cl2.contractlineid)
  107.      from contractline cl2
  108.      where cl2.leasecontractid = lc.leasecontractid
  109.      and cl2.active = 'A')
  110.  and cl.active = 'A'
  111.  and zl.leasecontractid = cl.leasecontractid
  112.  and zl.contractlineid = cl.ContractLineID
  113.  and a.orgid = o.orgid
  114.  and (a.assetid = @assetid or @assetid = 0)
  115.  and a.linetype in ('B', 'S')
  116.  and a.assetid = zl.assetid
  117.  and a.manufacture like '%' + @brand + '%'
  118.  and a.brand like  '%' + @lineOfProduct + '%'
  119.  and a.description like  '%' + @description + '%'
  120.  and (a.catalogtype = @cat1 or @cat1 = '')
  121.  and (a.productCode = @manufacturerRef or @manufacturerRef = '')
  122.  and zg.catalogType = a.catalogType
  123.  and zg.orgid = a.orgid
  124.  and (zg.category = @category or @category = '')
  125.  and at1.assetid =* a.assetid
  126.  and at1.attributename =* zg.subcat1
  127.  and (at1.value = @cat2 or @cat2 = '')
  128.  and at2.assetid =* a.assetid
  129.  and at2.attributename =* zg.subcat2
  130.  and (at2.value = @cat3 or @cat3 = '')
  131.  and zfa.assetid = a.assetid
  132.  and zfa.leasescheduleid = ls.leasescheduleid
  133.  and zfa.leasecontractid = lc.leasecontractid
  134.  and zfa.contractlineid = cl.contractlineid
  135.  and zf.factureid = zfa.factureid
  136.  and zf.type = 'F'
  137.  and a.serialNumber like  '%' + @serialNumber + '%'
  138.  and v.orgid = a.VendorOrgID
  139.  and v.type = 'V'
  140.  and zf.numero like  '%' + @invoice + '%'
  141.  and (zf.datecreation between @invdate1 and @invdate2 or @invdate1 > @invdate2 or @invdate1 is null or @invdate2 is null)
  142.  and a.reference1 like  '%' + @ref1 + '%'
  143.  and a.reference2 like  '%' + @ref2 + '%'
  144.  and a.reference3 like  '%' + @ref3 + '%'
  145.  and a.reference4 like  '%' + @ref4 + '%'
  146.  and a.reference5 like  '%' + @ref5 + '%'
  147.  union all
  148.  select o.orgid,
  149.   o.name,
  150.   ls.leasescheduleid,
  151.   ls.schedule,
  152. -- inventory row
  153. -- inventory autoincrement
  154.   a.ProductCode manufacturerRef,
  155.   a.SerialNumber,
  156.   a.description,
  157.   a.manufacture brand,
  158.   a.brand lineOfProduct,
  159.   zg.category,
  160.   a.catalogType cat1,
  161.   at1.value cat2,
  162.   at2.value cat3,
  163.   v.name supplier,
  164.   zf.numero invoice,
  165.   zf.datecreation invcreation,
  166.   a.reference1 ref1,
  167.   a.reference2 ref2,
  168.   a.reference3 ref3,
  169.   a.reference4 ref4,
  170.   a.reference5 ref5,
  171.   lc.leasecontractid,
  172.   lc.contract,
  173.   a.assetid
  174.  from organization v, zfactures zf, zfactureasset zfa, sdViewAttributesValues at2, sdViewAttributesValues at1, zCatalogTypeGenre zg, asset a, zloyer zl, contractline cl, leasecontract lc, leaseschedule ls, organization o, UserOrg uo
  175.  where (uo.orgid = @orgid or @orgid = 0)
  176.  and uo.userid = @userid
  177.  and o.type = 'C'
  178.  and o.active = 'A'
  179.  and o.orgid = uo.orgid
  180.  and ls.orgid = o.orgid
  181.  and (ls.leasescheduleid = @leasescheduleid or @leasescheduleid = 0)
  182.  and ls.active = 'A'
  183.  and ls.userights = 0
  184.  and lc.orgid = ls.orgid
  185.  and lc.leasescheduleid = ls.leasescheduleid
  186.  and lc.active = 'A'
  187.  and (lc.leasecontractid = @leasecontractid or @leasecontractid = 0)
  188.  and (lc.leasestopdate between @date1 and @date2 or @date1 is null or @date2 is null or @date1 > @date2)
  189.  and (datediff(m, lc.leasestartdate, lc.leasestopdate) = @duration or @duration = 0)
  190.  and cl.contractlineid = (select max(cl2.contractlineid)
  191.      from contractline cl2
  192.      where cl2.leasecontractid = lc.leasecontractid
  193.      and cl2.active = 'A')
  194.  and cl.active = 'A'
  195.  and zl.leasecontractid = cl.leasecontractid
  196.  and zl.contractlineid = cl.ContractLineID
  197.  and a.orgid = o.orgid
  198.  and (a.assetid = @assetid or @assetid = 0)
  199.  and a.linetype in ('B', 'S')
  200.  and a.assetid = zl.assetid
  201.  and a.manufacture like  '%' + @brand + '%'
  202.  and a.brand like  '%' + @lineOfProduct + '%'
  203.  and a.description like  '%' + @description + '%'
  204.  and (a.catalogtype = @cat1 or @cat1 = '')
  205.  and (a.productCode = @manufacturerRef or @manufacturerRef = '')
  206.  and zg.catalogType = a.catalogType
  207.  and zg.orgid = a.orgid
  208.  and (zg.category = @category or @category = '')
  209.  and at1.assetid =* a.assetid
  210.  and at1.attributename =* zg.subcat1
  211.  and (at1.value = @cat2 or @cat2 = '')
  212.  and at2.assetid =* a.assetid
  213.  and at2.attributename =* zg.subcat2
  214.  and (at2.value = @cat3 or @cat3 = '')
  215.  and zfa.assetid = a.assetid
  216.  and zfa.leasescheduleid = ls.leasescheduleid
  217.  and zfa.leasecontractid = lc.leasecontractid
  218.  and zfa.contractlineid = cl.contractlineid
  219.  and zf.factureid = zfa.factureid
  220.  and zf.type = 'F'
  221.  and a.serialNumber like  '%' + @serialNumber + '%'
  222.  and v.orgid = a.VendorOrgID
  223.  and v.type = 'V'
  224.  and zf.numero like  '%' + @invoice + '%'
  225.  and (zf.datecreation between @invdate1 and @invdate2 or @invdate1 > @invdate2 or @invdate1 is null or @invdate2 is null)
  226.  and a.reference1 like  '%' + @ref1 + '%'
  227.  and a.reference2 like  '%' + @ref2 + '%'
  228.  and a.reference3 like  '%' + @ref3 + '%'
  229.  and a.reference4 like  '%' + @ref4 + '%'
  230.  and a.reference5 like  '%' + @ref5 + '%'
  231. ) tmp
  232. order by case @orderby
  233.  when 'org'  then name
  234.  when 'manufacturerRef' then manufacturerRef
  235.  when 'SerialNumber' then SerialNumber
  236.  when 'description' then description
  237.  when 'brand'  then brand
  238.  when 'lineOfProduct' then lineOfProduct
  239.  when 'category'  then category
  240.  when 'cat1'  then cat1
  241.  when 'cat2'  then cat2
  242.  when 'cat3'  then cat3
  243.  when 'supplier'  then supplier
  244.  when 'numero'  then invoice
  245.  when 'datecreation' then convert(varchar(8), invcreation, 112)
  246.  when 'reference1' then ref1
  247.  when 'reference2' then ref2
  248.  when 'reference3' then ref3
  249.  when 'reference4' then ref4
  250.  when 'reference5' then ref5
  251. end, case @orderby
  252.  when 'org'  then schedule
  253.  when 'manufacturerRef' then manufacturerRef
  254.  when 'SerialNumber' then SerialNumber
  255.  when 'description' then description
  256.  when 'brand'  then brand
  257.  when 'lineOfProduct' then lineOfProduct
  258.  when 'category'  then category
  259.  when 'cat1'  then cat1
  260.  when 'cat2'  then cat2
  261.  when 'cat3'  then cat3
  262.  when 'supplier'  then supplier
  263.  when 'numero'  then invoice
  264.  when 'datecreation' then convert(varchar(8), invcreation, 112)
  265.  when 'reference1' then ref1
  266.  when 'reference2' then ref2
  267.  when 'reference3' then ref3
  268.  when 'reference4' then ref4
  269.  when 'reference5' then ref5
  270. end
  271. END
  272. GO
  273. -- 19 SECONDES
  274. NEW
  275. CREATE procedure icsd_ARTAssetList
  276. (
  277. @orgid   int,
  278. @userid   int,
  279. @date1   datetime,
  280. @date2   datetime,
  281. @invdate1  datetime,
  282. @invdate2  datetime,
  283. @leasescheduleid int = 0,
  284. @leasecontractid int = 0,
  285. @duration  int = 0,
  286. @brand   varchar(255) = '%',
  287. @lineOfProduct  varchar(255) = '%',
  288. @manufacturerRef varchar(255) = '',
  289. @description  varchar(255) = '%',
  290. @category  varchar(255) = '',
  291. @cat1   varchar(255) = '',
  292. @cat2   varchar(255) = '',
  293. @cat3   varchar(255) = '',
  294. @serialnumber  varchar(255) = '%',
  295. @supplier  varchar(255) = '%',
  296. @invoice  varchar(255) = '%',
  297. @ref1   varchar(255) = '%',
  298. @ref2   varchar(255) = '%',
  299. @ref3   varchar(255) = '%',
  300. @ref4   varchar(255) = '%',
  301. @ref5   varchar(255) = '%',
  302. @assetid  int = 0,
  303. @orderby  varchar(255) = 'org'
  304. )
  305. AS
  306. BEGIN
  307. select orgid,
  308. name,
  309. leasescheduleid,
  310. schedule,
  311. -- inventory row
  312. -- inventory autoincrement
  313. manufacturerRef,
  314. SerialNumber,
  315. description,
  316. brand,
  317. lineOfProduct,
  318. category,
  319. cat1,
  320. cat2,
  321. cat3,
  322. supplier,
  323. invoice,
  324. invcreation,
  325. ref1,
  326. ref2,
  327. ref3,
  328. ref4,
  329. ref5,
  330. leasecontractid,
  331. contract,
  332. assetid
  333. from (
  334.  select o.orgid,
  335.   o.name,
  336.   ls.leasescheduleid,
  337.   ls.schedule,
  338. -- inventory row
  339. -- inventory autoincrement
  340.   a.ProductCode manufacturerRef,
  341.   a.SerialNumber,
  342.   a.description,
  343.   a.manufacture brand,
  344.   a.brand lineOfProduct,
  345.   zg.category,
  346.   a.catalogType cat1,
  347.   at1.value cat2,
  348.   at2.value cat3,
  349.   v.name supplier,
  350.   zf.numero invoice,
  351.   zf.datecreation invcreation,
  352.   a.reference1 ref1,
  353.   a.reference2 ref2,
  354.   a.reference3 ref3,
  355.   a.reference4 ref4,
  356.   a.reference5 ref5,
  357.   lc.leasecontractid,
  358.   lc.contract,
  359.   a.assetid
  360.  from organization v, zfactures zf, zfactureasset zfa, sdViewAttributesValues at2, sdViewAttributesValues at1, zCatalogTypeGenre zg, zUserContractPerimeter zu, organization o, UserOrg uo, leaseschedule ls, leasecontract lc, contractline cl, zloyer zl, asset a
  361.  where 1 = 1
  362.  and (a.orgid = @orgid or @orgid = 0)
  363.  and (a.assetid = @assetid or @assetid = 0)
  364.  and a.linetype in ('B', 'S')
  365.  and a.manufacture like '%' + @brand + '%'
  366.  and a.brand like  '%' + @lineOfProduct + '%'
  367.  and a.description like  '%' + @description + '%'
  368.  and (a.catalogtype = @cat1 or @cat1 = '')
  369.  and (a.productCode = @manufacturerRef or @manufacturerRef = '')
  370.  and a.serialNumber like  '%' + @serialNumber + '%'
  371.  and a.reference1 like  '%' + @ref1 + '%'
  372.  and a.reference2 like  '%' + @ref2 + '%'
  373.  and a.reference3 like  '%' + @ref3 + '%'
  374.  and a.reference4 like  '%' + @ref4 + '%'
  375.  and a.reference5 like  '%' + @ref5 + '%'
  376.  and (a.leasescheduleid = @leasescheduleid or @leasescheduleid = 0)
  377.  and (a.leasecontractid = @leasecontractid or @leasecontractid = 0)
  378.  and a.contractlineid = (select max(cl2.contractlineid)
  379.      from contractline cl2
  380.      where cl2.leasecontractid = a.leasecontractid
  381.      and cl2.active = 'A')
  382.  and zl.leasecontractid = a.leasecontractid
  383.  and zl.contractlineid = a.ContractLineID
  384.  and zl.assetid = a.assetid
  385.  and cl.contractlineid = zl.contractlineid
  386.  and cl.leasecontractid = zl.leasecontractid
  387.  and cl.active = 'A'
  388.  and lc.orgid = a.orgid
  389.  and lc.leasescheduleid = a.leasescheduleid
  390.  and lc.leasecontractid = zl.leasecontractid
  391.  and lc.active = 'A'
  392.  and (lc.leasestopdate between @date1 and @date2 or @date1 > @date2 or @date1 is null or @date2 is null)
  393.  and (datediff(m, lc.leasestartdate, lc.leasestopdate) = @duration or @duration = 0)
  394.  and ls.orgid = lc.orgid
  395.  and ls.leasescheduleid = lc.leasescheduleid
  396.  and ls.active = 'A'
  397.  and ls.userights = 1
  398.  and uo.orgid = ls.orgid
  399.  and uo.userid = @userid
  400.  and o.type = 'C'
  401.  and o.active = 'A'
  402.  and o.orgid = uo.orgid
  403.  and zu.userid = uo.userid
  404.  and zu.orgid = uo.orgid
  405.  and zu.scheduleid = ls.leasescheduleid
  406.  and (zu.contractid = lc.leasecontractid or zu.contractid = -1)
  407.  and zg.catalogType = a.catalogType
  408.  and zg.orgid = a.orgid
  409.  and (zg.category = @category or @category = '')
  410.  and at1.assetid =* a.assetid
  411.  and at1.attributename =* zg.subcat1
  412.  and (at1.value = @cat2 or @cat2 = '')
  413.  and at2.assetid =* a.assetid
  414.  and at2.attributename =* zg.subcat2
  415.  and (at2.value = @cat3 or @cat3 = '')
  416.  and zfa.assetid = a.assetid
  417.  and zfa.leasescheduleid = ls.leasescheduleid
  418.  and zfa.leasecontractid = lc.leasecontractid
  419.  and zfa.contractlineid = cl.contractlineid
  420.  and zf.factureid = zfa.factureid
  421.  and zf.type = 'F'
  422.  and v.orgid = a.VendorOrgID
  423.  and v.type = 'V'
  424.  and zf.numero like  '%' + @invoice + '%'
  425.  and (zf.datecreation between @invdate1 and @invdate2 or @invdate1 > @invdate2 or @invdate1 is null or @invdate2 is null)
  426.  union all
  427.  select o.orgid,
  428.   o.name,
  429.   ls.leasescheduleid,
  430.   ls.schedule,
  431. -- inventory row
  432. -- inventory autoincrement
  433.   a.ProductCode manufacturerRef,
  434.   a.SerialNumber,
  435.   a.description,
  436.   a.manufacture brand,
  437.   a.brand lineOfProduct,
  438.   zg.category,
  439.   a.catalogType cat1,
  440.   at1.value cat2,
  441.   at2.value cat3,
  442.   v.name supplier,
  443.   zf.numero invoice,
  444.   zf.datecreation invcreation,
  445.   a.reference1 ref1,
  446.   a.reference2 ref2,
  447.   a.reference3 ref3,
  448.   a.reference4 ref4,
  449.   a.reference5 ref5,
  450.   lc.leasecontractid,
  451.   lc.contract,
  452.   a.assetid
  453.  from organization v, zfactures zf, zfactureasset zfa, sdViewAttributesValues at2, sdViewAttributesValues at1, zCatalogTypeGenre zg, organization o, UserOrg uo, leaseschedule ls, leasecontract lc, contractline cl, zloyer zl, asset a
  454.  where 1 = 1
  455.  and (a.orgid = @orgid or @orgid = 0)
  456.  and (a.assetid = @assetid or @assetid = 0)
  457.  and a.linetype in ('B', 'S')
  458.  and a.manufacture like '%' + @brand + '%'
  459.  and a.brand like  '%' + @lineOfProduct + '%'
  460.  and a.description like  '%' + @description + '%'
  461.  and (a.catalogtype = @cat1 or @cat1 = '')
  462.  and (a.productCode = @manufacturerRef or @manufacturerRef = '')
  463.  and a.serialNumber like  '%' + @serialNumber + '%'
  464.  and a.reference1 like  '%' + @ref1 + '%'
  465.  and a.reference2 like  '%' + @ref2 + '%'
  466.  and a.reference3 like  '%' + @ref3 + '%'
  467.  and a.reference4 like  '%' + @ref4 + '%'
  468.  and a.reference5 like  '%' + @ref5 + '%'
  469.  and (a.leasescheduleid = @leasescheduleid or @leasescheduleid = 0)
  470.  and (a.leasecontractid = @leasecontractid or @leasecontractid = 0)
  471.  and a.contractlineid = (select max(cl2.contractlineid)
  472.      from contractline cl2
  473.      where cl2.leasecontractid = a.leasecontractid
  474.      and cl2.active = 'A')
  475.  and zl.leasecontractid = a.leasecontractid
  476.  and zl.contractlineid = a.ContractLineID
  477.  and zl.assetid = a.assetid
  478.  and cl.contractlineid = zl.contractlineid
  479.  and cl.leasecontractid = zl.leasecontractid
  480.  and cl.active = 'A'
  481.  and lc.orgid = a.orgid
  482.  and lc.leasescheduleid = a.leasescheduleid
  483.  and lc.leasecontractid = zl.leasecontractid
  484.  and lc.active = 'A'
  485.  and (lc.leasestopdate between @date1 and @date2 or @date1 > @date2 or @date1 is null or @date2 is null)
  486.  and (datediff(m, lc.leasestartdate, lc.leasestopdate) = @duration or @duration = 0)
  487.  and ls.orgid = lc.orgid
  488.  and ls.leasescheduleid = lc.leasescheduleid
  489.  and ls.active = 'A'
  490.  and ls.userights = 0
  491.  and uo.orgid = ls.orgid
  492.  and uo.userid = @userid
  493.  and o.type = 'C'
  494.  and o.active = 'A'
  495.  and o.orgid = uo.orgid
  496.  and zg.catalogType = a.catalogType
  497.  and zg.orgid = a.orgid
  498.  and (zg.category = @category or @category = '')
  499.  and at1.assetid =* a.assetid
  500.  and at1.attributename =* zg.subcat1
  501.  and (at1.value = @cat2 or @cat2 = '')
  502.  and at2.assetid =* a.assetid
  503.  and at2.attributename =* zg.subcat2
  504.  and (at2.value = @cat3 or @cat3 = '')
  505.  and zfa.assetid = a.assetid
  506.  and zfa.leasescheduleid = ls.leasescheduleid
  507.  and zfa.leasecontractid = lc.leasecontractid
  508.  and zfa.contractlineid = cl.contractlineid
  509.  and zf.factureid = zfa.factureid
  510.  and zf.type = 'F'
  511.  and v.orgid = a.VendorOrgID
  512.  and v.type = 'V'
  513.  and zf.numero like  '%' + @invoice + '%'
  514.  and (zf.datecreation between @invdate1 and @invdate2 or @invdate1 > @invdate2 or @invdate1 is null or @invdate2 is null)
  515. ) tmp
  516. order by case @orderby
  517.  when 'org'  then name
  518.  when 'manufacturerRef' then manufacturerRef
  519.  when 'SerialNumber' then SerialNumber
  520.  when 'description' then description
  521.  when 'brand'  then brand
  522.  when 'lineOfProduct' then lineOfProduct
  523.  when 'category'  then category
  524.  when 'cat1'  then cat1
  525.  when 'cat2'  then cat2
  526.  when 'cat3'  then cat3
  527.  when 'supplier'  then supplier
  528.  when 'numero'  then invoice
  529.  when 'datecreation' then convert(varchar(8), invcreation, 112)
  530.  when 'reference1' then ref1
  531.  when 'reference2' then ref2
  532.  when 'reference3' then ref3
  533.  when 'reference4' then ref4
  534.  when 'reference5' then ref5
  535. end, case @orderby
  536.  when 'org'  then schedule
  537.  when 'manufacturerRef' then manufacturerRef
  538.  when 'SerialNumber' then SerialNumber
  539.  when 'description' then description
  540.  when 'brand'  then brand
  541.  when 'lineOfProduct' then lineOfProduct
  542.  when 'category'  then category
  543.  when 'cat1'  then cat1
  544.  when 'cat2'  then cat2
  545.  when 'cat3'  then cat3
  546.  when 'supplier'  then supplier
  547.  when 'numero'  then invoice
  548.  when 'datecreation' then convert(varchar(8), invcreation, 112)
  549.  when 'reference1' then ref1
  550.  when 'reference2' then ref2
  551.  when 'reference3' then ref3
  552.  when 'reference4' then ref4
  553.  when 'reference5' then ref5
  554. end
  555. END
  556. GO
  557. -- 2 SECONDES !!!

Reply

Marsh Posté le 05-04-2005 à 18:36:47    

j'ai aussi des pbs de lenteur avec MySql pour une requête issue d'un moteur de recherche multicritères. Pourtant, j'ai pas tant que ça d'enregistrements :
- 5000 demandes
- pour chaque demande, un historique de 8 enregistrements en moyenne
 
Ca doit bien prendre 7 ou 8 secondes pour trouver les demandes et encore, je pagine avec LIMIT, mais ça ne me fait pas gagner tant que ça de temps. Mon principal pb, c'est que je travaille avec un mysql 3.23.58 qui ne gère pas les requêtes imbriquées : bilan, je dois passer par une table temporaire :( J'ai tenté une optimisation, mais elle marche que dans certains cas (ça dépend des critères sélectionnés par l'utilisateur)... Ta piste est intéressant en tout cas et je vais regarder mysql 5.

Reply

Marsh Posté le 05-04-2005 à 19:22:55    

Question bête : t'as des LIKE ?
Si oui, alors c'est certainement eux qui plombent ta requête.
Essaie de voir si tu peux implémenter (et utiliser :D) les fonctions d'indexation de texte, afin d'utiliser FREETEXT et CONTAINS notamment. C'est infiniment plus rapide, et ça permet de faire des recherhces plus naturelles.

Reply

Marsh Posté le 06-04-2005 à 10:45:49    

Arjuna a écrit :

Question bête : t'as des LIKE ?
Si oui, alors c'est certainement eux qui plombent ta requête.
Essaie de voir si tu peux implémenter (et utiliser :D) les fonctions d'indexation de texte, afin d'utiliser FREETEXT et CONTAINS notamment. C'est infiniment plus rapide, et ça permet de faire des recherhces plus naturelles.


 
Oui, j'ai du LIKE (plusieurs même). Le soucis, c'est que je ne veux pas faire du SQL trop spécifique à MySQl, l'un des principes de l'appli étant d'être indépendante du SGBD...

Reply

Marsh Posté le 06-04-2005 à 11:17:39    

Les fonctions de texte intégral sont relativement similaires d'un SGBD à l'autre.
 
Je te conseille de faire ce que j'ai fait pour mon site Manga-Torii.
Une fonction PHP qui génère le code SQL correspondant à l'utilisation des index de texte, que tu colles au reste de ta requête plus générique.
Ainsi, tu pourras aisément faire les adaptations selon les SGBD utilisés (voir une version avec like) sans casser la requête.
 
Pour information, SQL Server utilise les mêmes procédures que MySQL, plus d'autres supplémentaires (avec quelques déviances niveau syntaxe cependant)
Oracle, quant à lui, a une syntaxe plus spécifique.

Reply

Marsh Posté le 06-04-2005 à 12:22:05    

j'ai une petite question concernant ta requete au dessus. Y'a combien d'enregistrements à 'scanner'? Quelle est le volume de la plus grosse tables?
 
Bref, des details permettant de mettre en relation les 2 secondes d'execution et le volume.
 
:jap:


---------------
MZP est de retour
Reply

Marsh Posté le 06-04-2005 à 12:22:24    

Comme autre SGBD, je pensais entre autre à Oracle, mais surtout à PostgreSql.
 
T'aurais un ex de ta fonction de cote sql à me montrer? je pense avoir saisi le truc, mais je voudrais m'en assurer avant de recoder cette partie. Merci :jap:

Reply

Marsh Posté le 06-04-2005 à 14:26:02    

cinocks:

Code :
  1. select count(*) from organization
  2. -----------
  3. 69
  4. select count(*) from zfactures
  5. -----------
  6. 242
  7. select count(*) from zfactureasset
  8. -----------
  9. 93
  10. select count(*) from sdViewAttributesValues -- vue
  11. -----------
  12. 44660
  13. select count(*) from zCatalogTypeGenre
  14. -----------
  15. 400
  16. select count(*) from zUserContractPerimeter
  17. -----------
  18. 0
  19. select count(*) from UserOrg
  20. -----------
  21. 46
  22. select count(*) from leaseschedule
  23. -----------
  24. 5
  25. select count(*) from leasecontract
  26. -----------
  27. 26
  28. select count(*) from contractline
  29. -----------
  30. 46
  31. select count(*) from zloyer
  32. -----------
  33. 9363
  34. select count(*) from asset
  35. -----------
  36. 27699


 
Tiens, y'en a beaucoup moins que ce que j'avais prévu :heink:
 
Ca va faire mal, parcequ'on va pas tarder à avoir plus de 500 000 lignes dans la table asset, avec au minimum le double dans la vue sdViewAttributesValues :ouch:
 
Va falloir optimiser encore un peu je crois :D

Reply

Marsh Posté le 06-04-2005 à 14:26:35    

rufo a écrit :

Comme autre SGBD, je pensais entre autre à Oracle, mais surtout à PostgreSql.
 
T'aurais un ex de ta fonction de cote sql à me montrer? je pense avoir saisi le truc, mais je voudrais m'en assurer avant de recoder cette partie. Merci :jap:


J'ai rien compris du tout :D
 
Tu veux quoi ?

Reply

Marsh Posté le 06-04-2005 à 14:27:27    

Ah, ok, je viens de piger. Euh... Si j'arrive à me connecter au FTP de mon site ce soir, je te poste ma fonction (elle est fait un peur, et est améliorable :D)

Reply

Marsh Posté le 06-04-2005 à 14:45:50    

Arjuna a écrit :

Les fonctions de texte intégral sont relativement similaires d'un SGBD à l'autre.
 
Je te conseille de faire ce que j'ai fait pour mon site Manga-Torii.
Une fonction PHP qui génère le code SQL correspondant à l'utilisation des index de texte, que tu colles au reste de ta requête plus générique.
Ainsi, tu pourras aisément faire les adaptations selon les SGBD utilisés (voir une version avec like) sans casser la requête.
 
Pour information, SQL Server utilise les mêmes procédures que MySQL, plus d'autres supplémentaires (avec quelques déviances niveau syntaxe cependant)
Oracle, quant à lui, a une syntaxe plus spécifique.


 
 
je parle de cette fct :)

Reply

Marsh Posté le 06-04-2005 à 14:46:32    

Arjuna a écrit :

Ah, ok, je viens de piger. Euh... Si j'arrive à me connecter au FTP de mon site ce soir, je te poste ma fonction (elle est fait un peur, et est améliorable :D)


 
bah, même pas peur :D

Reply

Marsh Posté le 06-04-2005 à 16:27:04    

Arjuna a écrit :

cinocks:

Code :
  1. select count(*) from organization
  2. -----------
  3. 69
  4. select count(*) from zfactures
  5. -----------
  6. 242
  7. select count(*) from zfactureasset
  8. -----------
  9. 93
  10. select count(*) from sdViewAttributesValues -- vue
  11. -----------
  12. 44660
  13. select count(*) from zCatalogTypeGenre
  14. -----------
  15. 400
  16. select count(*) from zUserContractPerimeter
  17. -----------
  18. 0
  19. select count(*) from UserOrg
  20. -----------
  21. 46
  22. select count(*) from leaseschedule
  23. -----------
  24. 5
  25. select count(*) from leasecontract
  26. -----------
  27. 26
  28. select count(*) from contractline
  29. -----------
  30. 46
  31. select count(*) from zloyer
  32. -----------
  33. 9363
  34. select count(*) from asset
  35. -----------
  36. 27699


 
Tiens, y'en a beaucoup moins que ce que j'avais prévu :heink:
 
Ca va faire mal, parcequ'on va pas tarder à avoir plus de 500 000 lignes dans la table asset, avec au minimum le double dans la vue sdViewAttributesValues :ouch:
 
Va falloir optimiser encore un peu je crois :D


 
Tout depend du serveur et de sa puissance derriere. Mais ta requete ne tiendra pas longtemps une montée en charge. Car pour le moment, il n'y a pas bcp de lignes dans les tables. Etant donné que tu passes par une procedure stockée, autant en profiter pour decouper ta requete en plusieurs sous-requetes et tables de travail.  
 
Avec ton union, tu as deux blocs requetes qui au debut recupere les memes données. Ce n'est qu'à partir du and ls.userights = 1 qu'elles se distinguent. Du coup, ce qui se fait avant est identique entre les 2 blocs.  
Pourquoi ne pas mutualiser ce qui est identique par requetes intermediaires et tables de travail, puis partir de ce resultat pour construire le reste.
 
Personnellement, c'est ce que l'on utilise au boulot, et ce sont des volumes variant entre 20 000 000 et 30 000 000 de lignes  dans les tables. ;)


Message édité par cinocks le 06-04-2005 à 16:28:57

---------------
MZP est de retour
Reply

Marsh Posté le 06-04-2005 à 16:43:20    

Parceque potentiellement, la requête peut retourner TOUTES les lignes (tous les filtres sont facultatifs). Par conséquent, si je fait ça dans des tables temporaires, je fais passer 2 heures à recopier toutes les données dans la base tempdb, jusqu'à planter le serveur par manque d'espace.
 
Deplus, étant donné que les filtres se trouvent à tous les niveaux, et que la notion de droit impact les filtres sur plusieurs tables, stocker des infos selon un nombre limité de filtres va provoquer le stockage d'un nombre important de lignes qui ne seront très certainement pas retenues dans les filtres finaux.

Reply

Marsh Posté le 06-04-2005 à 16:55:28    

Tout depend du nombre de lignes retournée depuis asset quand les filtres sont inactifs. Quel est le % d'enregistrements retournés si tu executes:
 
# and a.contractlineid = (select max(cl2.contractlineid)
#                      from contractline cl2
#                      where cl2.leasecontractid = a.leasecontractid
#                      and cl2.active = 'A')
 
 
idem cette sous-requete peut se preparer pour devenir
 
select cl2.leasecontractid, max(cl2.contractlineid)
into #contractline  
from contractline cl2
where cl2.active = 'A'
group by cl2.leasecontractid
 
Ca evitera de faire autant d'aggregats qu'il n'y a d'enregistrement dans a.


---------------
MZP est de retour
Reply

Marsh Posté le 06-04-2005 à 17:45:19    

bon je profite de ce topic pour reposer une question auquel personne ne ma encore repondu : comment peut on faire pour implementer facilement dans sql server un systeme de gestion des droits des utilisateurs ??? ex: tel user a le droit de modifier tel colonne ou tel ligne ........

Reply

Marsh Posté le 06-04-2005 à 17:52:14    

cinocks > il y a très peu de "doublons" dans la table contractline. En fait, chaque nouvelle ligne correspond à un avenant à un contrat. Hors, un contrat qui arrive à échéance, dans le mode de fonctionnement de la bdd dans laquelle va tourner ma requête, un nouveau contrat est créé. Donc je dirais que ce "max" va rammener entre 90 et 100% des lignes. Je pense qu'il n'est donc pas utile de le compiler à l'avance.
 
Ensuite, il va y avoir environ 40 000 clients. Chaque client ayant 1 à 5 contrats actifs dans la base, donc ça fait pas mal de lignes à mettre dans la table temporaire.
 
Quand j'aurai les données réelles, je ferais peut-être le test, mais je pense que je vais perdre du temps plutôt qu'en gagner avec le sous-select. En effet, les tables étant bien indexées, le nombre de lignes n'influe que très peu sur la vitesse d'éxécution de la requête. Par contre, dès que le volume de données augmente, l'utilisation de tables temporaire plombe tous les traîtements.

Reply

Marsh Posté le 06-04-2005 à 17:52:52    

red faction > ben à partir de l'interface, ça marche tout seul. sinon, il y a des fonction systèmes qui permettent de gérer les droits dans sql server.

Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

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