Avatar billede it-dyret Nybegynder
13. juni 2006 - 15:21 Der er 10 kommentarer og
1 løsning

Optimering af forespørgsel

Jeg er ved at lave avanceret statistik på en simpel database indeholdende ordrelinier. I store dele af statistikken bliver der kun brugt en tabel, som er listet nedenfor i forkortet udgave:

id (int)
produkt (varchar 200)
kundenummer (int)
pakning (int)

Statistikken er koblet sammen med en grafisk brugergrænseflade, hvor brugeren har mulighed for selv at stykke en forespørgsel sammen, som jeg generer dynamisk.

Alt fungerer fint med undtagelse af en enkelt sum, som bliver vist ved siden af det resultSet, som den dynamiske forespørgsel giver anledning til.

Den problematiske sum skal "blot" summere antallet af forskellige kundenumre i den dynamiske forespørgsel og kan f.eks. se således ud:

SELECT COUNT(DISTINCT inner_nr) FROM (
SELECT Count(*) AS total, produkt, pakning, kundenummer AS inner_nr FROM ordrelinier GROUP BY produkt, pakning, inner_nr
) AS S1
JOIN (
SELECT Count(*) AS total, produkt, pakning FROM ordrelinier GROUP BY produkt, pakning HAVING total >= 10 AND total < 21
) AS S2
ON S1.produkt = S2.produkt AND S1.pakning = S2.pakning

Den dynamisk genererede forespørgsel svarer til S2 i ovenstående, hvor denne i S1 bliver manipuleret til altid at medtage kundenummer (altså inner_nr) men tilgengæld undlade HAVING-betingelsen. Disse to forespørgsler bliver efterfølgende joinet for at kunne tælle antallet af forskellige kundenumre.

Så vidt jeg kan finde ud af, virker forespørgslen, men afhængig af det valgte kan den være ekstrem langsom (adskillige minutter). Test har vist, at afvikling af henholdsvis S1 og S2 max. tager nogle hundrede millisekunder - så performance lider i forbindelse med join.

Hvordan får jeg forespørgsel til at blive afviklet hurtigere? Skal der en anden forespørgsel til - eller skal der bruges indexes (og hvis ja, hvilke)??
Avatar billede kjulius Novice
13. juni 2006 - 18:49 #1
Måske er det bare mig der ikke kan se det, men ville du ikke få det samme resultat (godt nok med et par felter mere returneret, men det betyder vel ikke noget) ved simpelthen at sige:

SELECT COUNT(DISTINCT kundenummer) AS kunder, produkt, pakning
FROM ordrelinier
GROUP BY produkt, pakning
HAVING COUNT(*) BETWEEN 10 AND 20
Avatar billede it-dyret Nybegynder
14. juni 2006 - 12:34 #2
Desværre er det ikke helt godt nok, da der for det første kommer flere linier med bl.a. count af kundenummer. Og disse linier kan jeg ikke blot ligge sammen, da der kan være gengangere imellem dem.

Der er muligvis flere problemer forbundet med søgningen...

Jeg har ganske enkelt brug for at kunne tælle, hvor mange FORSKELLIGE kundenumre, som har bidraget til salget af de ordrelinier, (hvor der er 10-20 af dem grupperet efter produkt og pakning).

Flere forslag er meget velkomne!
Avatar billede it-dyret Nybegynder
14. juni 2006 - 19:54 #3
>>>kjulius

Hvis jeg kører dit forslag får jeg følgende resultset:

kunder    produkt            pakning
7    Produkt_A    5
8    Produkt_B    5
9    Produkt_C    4
8    Produkt_D    1
8    Produkt_E    -2
6    Produkt_F    2

Og problemet er, at jeg ikke blot kan lægge første kolonne sammen (altså kunder), da det giver 46, hvilket er forkert, da der er dubletter imellem... Det rigtige resultat er således 44
Avatar billede kjulius Novice
19. juni 2006 - 01:11 #4
Okay, jeg har vist simplificeret problemstillingen lidt rigeligt. :-)
Hvad så med denne her...?

SELECT COUNT(DISTINCT kundenummer) AS kunder, produkt, pakning
FROM ordrelinier o1
INNER JOIN (
  (SELECT produkt, pakning
  FROM ordrelinier
  GROUP BY produkt, pakning
  HAVING COUNT(*) BETWEEN 10 AND 20
  ) AS o2
ON o1.produkt = o2.produkt AND o1.pakning = o2.pakning
Avatar billede kjulius Novice
19. juni 2006 - 01:12 #5
Sorry, for mange paranteser...

SELECT COUNT(DISTINCT kundenummer) AS kunder, produkt, pakning
FROM ordrelinier o1
INNER JOIN
  (SELECT produkt, pakning
  FROM ordrelinier
  GROUP BY produkt, pakning
  HAVING COUNT(*) BETWEEN 10 AND 20
  ) AS o2
ON o1.produkt = o2.produkt AND o1.pakning = o2.pakning
Avatar billede kjulius Novice
19. juni 2006 - 01:17 #6
SELECT COUNT(DISTINCT kundenummer) AS kunder, produkt, pakning
FROM ordrelinier o1
INNER JOIN
  (SELECT produkt, pakning
  FROM ordrelinier
  GROUP BY produkt, pakning
  HAVING COUNT(*) BETWEEN 10 AND 20
  ) AS o2
ON o1.produkt = o2.produkt AND o1.pakning = o2.pakning
GROUP BY produkt, pakning
Avatar billede kjulius Novice
19. juni 2006 - 01:20 #7
Hrmpf! Jeg glemmer åbenbart altid et par detaljer...

SELECT COUNT(DISTINCT o1.kundenummer) AS kunder, o1.produkt, o1.pakning
FROM ordrelinier o1
INNER JOIN
  (SELECT produkt, pakning
  FROM ordrelinier
  GROUP BY produkt, pakning
  HAVING COUNT(*) BETWEEN 10 AND 20
  ) AS o2
ON o1.produkt = o2.produkt AND o1.pakning = o2.pakning
GROUP BY o1.produkt, o1.pakning
Avatar billede kjulius Novice
19. juni 2006 - 01:31 #8
Ikke bare glemmer jeg detaljer, jeg er blind og døv!!!!
For at det skal virke, kan man jo ikke bare gruppere to gange. Det bliver resultatet da ikke bedre af. Men så må vi jo droppe de to felter, og nøjes med at returnere summen. Det var jo også det du bad om i dit oprindelige spørgsmål.

SELECT COUNT(DISTINCT kundenummer) AS kunder
FROM ordrelinier o1
INNER JOIN
  (SELECT produkt, pakning
  FROM ordrelinier
  GROUP BY produkt, pakning
  HAVING COUNT(*) BETWEEN 10 AND 20
  ) AS o2
ON o1.produkt = o2.produkt AND o1.pakning = o2.pakning
Avatar billede it-dyret Nybegynder
19. juni 2006 - 12:47 #9
Det ser umiddelbart lovende ud! jeg tester lige løs på den og vender tilbage. Det bliver dog muligvis først i aften...
Avatar billede it-dyret Nybegynder
20. juni 2006 - 11:01 #10
Det virker jo helt fantastisk, når man kombinerer din forespørgsel med de indexes, som mysql ikke ville bruge ved mit forslag... Den har åbenbart nemmere ved at gennemskue dit forslag, som jo også er noget kortere og simplere :)

Det, som før tog minutter, tager nu kun få sekunder i værste fald.

Smid fluks et svar, så jeg kan belønne dig!
Avatar billede kjulius Novice
20. juni 2006 - 16:33 #11
Dejligt at høre, at det til sidst lykkedes... :-)
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Computerworld tilbyder specialiserede kurser i database-management

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester