OVH Cloud OVH Cloud

PB de performance sur une requete SUM + JOIN + HAVING.

3 réponses
Avatar
Olivier Besson
Bonjour

J'ai une table qui contient des plis : 1 ligne par pli avec une PK = PLI_ID
dans l'ordre ou je les veux, une colonne LOT_ID donnant l'appartenance à un
lot, une colonne PAT_POIDS donnant le poids.

PLI 1 / LOT 1 / 10 grammes
PLI 2 / LOT 1 / 20 grammes
PLI 3 / LOT 1 / 15 grammes
PLI X / LOT Y / z grammes

Je cherche à récupérer pour chaque lot tous les plis dans l'ordre de PLI_ID
jusqu'à ce que la somme des poids soit <= 6500 grammes.
Cette requete marche :

SELECT PLI.PLI_ID, PLI.PAT_POIDS, PLI.LOT_ID, SUM(T2.PAT_POIDS)
FROM #T_PLI_PLI PLI
LEFT OUTER JOIN #T_PLI_PLI T2 ON PLI.LOT_ID = T2.LOT_ID AND PLI.PLI_ID
>= T2.PLI_ID
GROUP BY PLI.PLI_ID, PLI.PAT_POIDS, PLI.LOT_ID
ORDER BY PLI.PLI_ID, PLI.PAT_POIDS, PLI.LOT_ID
HAVING SUM(T2.PAT_POIDS) <= 6500

Mais avec à peu près 160 000 ligne dans la table elle tourne pendant 5
minutes et je voudrais la réduire à quelques secondes sans passer par un
curseur qui parcours dans l'ordre et met à jour en calculant au fur et à
mesure. Par contre je suis prêt à créer plein de tables temporaires si
besoin. Le top serait une formulation de la requete qui la rendrait plus
rapide (j'aime rever) ...

Merci d'avance pour vos réponses.

Structure et petit jeux exemple.

CREATE TABLE #T_PLI_PLI (
PLI_ID INT IDENTITY(1, 1) PRIMARY KEY,
LOT_ID INT NULL, /* C'est une clé étrangère */
PAT_ID INT NOT NULL, /* C'est une clé étrangère */
PAT_POIDS INT NOT NULL, /* Le poids d'un pli */
)

INSERT #T_PLI_PLI (LOT_ID ,PAT_ID ,PAT_POIDS) VALUES (1, 3, 15)
INSERT #T_PLI_PLI (LOT_ID ,PAT_ID ,PAT_POIDS) VALUES (1, 4, 10)
INSERT #T_PLI_PLI (LOT_ID ,PAT_ID ,PAT_POIDS) VALUES (1, 5, 15)
INSERT #T_PLI_PLI (LOT_ID ,PAT_ID ,PAT_POIDS) VALUES (1, 6, 10)
INSERT #T_PLI_PLI (LOT_ID ,PAT_ID ,PAT_POIDS) VALUES (1, 7, 15)
INSERT #T_PLI_PLI (LOT_ID ,PAT_ID ,PAT_POIDS) VALUES (1, 8, 15)
INSERT #T_PLI_PLI (LOT_ID ,PAT_ID ,PAT_POIDS) VALUES (1, 9, 10)
INSERT #T_PLI_PLI (LOT_ID ,PAT_ID ,PAT_POIDS) VALUES (1, 10, 10)
INSERT #T_PLI_PLI (LOT_ID ,PAT_ID ,PAT_POIDS) VALUES (1, 11, 20)
INSERT #T_PLI_PLI (LOT_ID ,PAT_ID ,PAT_POIDS) VALUES (1, 12, 15)



--

Olivier Besson
Pour m'ecrire, remplacer le <xx >de mon adresse par <com>.
(Je ne lis pas mes mails frequement)
To write me, replace <xx> part of my address by <com> (i don't read my mails
very frequently).

3 réponses

Avatar
Bonjour,

as tu essayé de faire des requêtes intermédiaires une requête étant
recalculer fréquement quelle soit utilisé ou pas il y aurat ainsi peut de
mise a jour à faire

dans le query analyser tu peux essayer de lancer la trace du serveur et
ensuite utiliser le fichier de trace dans l'assistant de paramétrage d'index

personnellement je ferais la requête de select basique puis la requête de
group by avec le having qui reprend en source la première requête

tu peux aussi regarder le plan d'exécution il te montreras les temps les
plus long il y a peut être des optimisations possibles

Sebastien
"Olivier Besson" a écrit dans le message de
news:
Bonjour

J'ai une table qui contient des plis : 1 ligne par pli avec une PK PLI_ID
dans l'ordre ou je les veux, une colonne LOT_ID donnant l'appartenance à


un
lot, une colonne PAT_POIDS donnant le poids.

PLI 1 / LOT 1 / 10 grammes
PLI 2 / LOT 1 / 20 grammes
PLI 3 / LOT 1 / 15 grammes
PLI X / LOT Y / z grammes

Je cherche à récupérer pour chaque lot tous les plis dans l'ordre de


PLI_ID
jusqu'à ce que la somme des poids soit <= 6500 grammes.
Cette requete marche :

SELECT PLI.PLI_ID, PLI.PAT_POIDS, PLI.LOT_ID, SUM(T2.PAT_POIDS)
FROM #T_PLI_PLI PLI
LEFT OUTER JOIN #T_PLI_PLI T2 ON PLI.LOT_ID = T2.LOT_ID AND PLI.PLI_ID
>= T2.PLI_ID
GROUP BY PLI.PLI_ID, PLI.PAT_POIDS, PLI.LOT_ID
ORDER BY PLI.PLI_ID, PLI.PAT_POIDS, PLI.LOT_ID
HAVING SUM(T2.PAT_POIDS) <= 6500

Mais avec à peu près 160 000 ligne dans la table elle tourne pendant 5
minutes et je voudrais la réduire à quelques secondes sans passer par un
curseur qui parcours dans l'ordre et met à jour en calculant au fur et à
mesure. Par contre je suis prêt à créer plein de tables temporaires si
besoin. Le top serait une formulation de la requete qui la rendrait plus
rapide (j'aime rever) ...

Merci d'avance pour vos réponses.

Structure et petit jeux exemple.

CREATE TABLE #T_PLI_PLI (
PLI_ID INT IDENTITY(1, 1) PRIMARY KEY,
LOT_ID INT NULL, /* C'est une clé étrangère */
PAT_ID INT NOT NULL, /* C'est une clé étrangère */
PAT_POIDS INT NOT NULL, /* Le poids d'un pli */
)

INSERT #T_PLI_PLI (LOT_ID ,PAT_ID ,PAT_POIDS) VALUES (1, 3, 15)
INSERT #T_PLI_PLI (LOT_ID ,PAT_ID ,PAT_POIDS) VALUES (1, 4, 10)
INSERT #T_PLI_PLI (LOT_ID ,PAT_ID ,PAT_POIDS) VALUES (1, 5, 15)
INSERT #T_PLI_PLI (LOT_ID ,PAT_ID ,PAT_POIDS) VALUES (1, 6, 10)
INSERT #T_PLI_PLI (LOT_ID ,PAT_ID ,PAT_POIDS) VALUES (1, 7, 15)
INSERT #T_PLI_PLI (LOT_ID ,PAT_ID ,PAT_POIDS) VALUES (1, 8, 15)
INSERT #T_PLI_PLI (LOT_ID ,PAT_ID ,PAT_POIDS) VALUES (1, 9, 10)
INSERT #T_PLI_PLI (LOT_ID ,PAT_ID ,PAT_POIDS) VALUES (1, 10, 10)
INSERT #T_PLI_PLI (LOT_ID ,PAT_ID ,PAT_POIDS) VALUES (1, 11, 20)
INSERT #T_PLI_PLI (LOT_ID ,PAT_ID ,PAT_POIDS) VALUES (1, 12, 15)



--

Olivier Besson
Pour m'ecrire, remplacer le <xx >de mon adresse par <com>.
(Je ne lis pas mes mails frequement)
To write me, replace <xx> part of my address by <com> (i don't read my


mails
very frequently).






Avatar
lionelp
Bonjour,

Tu fais un produit cartésien sur 160000 lignes ce qui fait
qqch de l'ordre de 12 milliard de lignes qu'il va falloir
réduire via aggregations puis filtrer. C'est beaucoup de
travail.
Maintenant, si tu passes par un curseur, tu ne balaie ta
table qu'une fois d'après ce que je comprend de ton
traitement, et tu vas peut-être effectivement passer à
quelques secondes. Donc ne sois pas sectaire, le curseur a
des chances d'être ton ami dans ce cas précis.

Cordialement,
LionelP

-----Message d'origine-----
Bonjour

J'ai une table qui contient des plis : 1 ligne par pli


avec une PK = PLI_ID
dans l'ordre ou je les veux, une colonne LOT_ID donnant


l'appartenance à un
lot, une colonne PAT_POIDS donnant le poids.

PLI 1 / LOT 1 / 10 grammes
PLI 2 / LOT 1 / 20 grammes
PLI 3 / LOT 1 / 15 grammes
PLI X / LOT Y / z grammes

Je cherche à récupérer pour chaque lot tous les plis dans


l'ordre de PLI_ID
jusqu'à ce que la somme des poids soit <= 6500 grammes.
Cette requete marche :

SELECT PLI.PLI_ID, PLI.PAT_POIDS, PLI.LOT_ID, SUM


(T2.PAT_POIDS)
FROM #T_PLI_PLI PLI
LEFT OUTER JOIN #T_PLI_PLI T2 ON PLI.LOT_ID =


T2.LOT_ID AND PLI.PLI_ID
= T2.PLI_ID


GROUP BY PLI.PLI_ID, PLI.PAT_POIDS, PLI.LOT_ID
ORDER BY PLI.PLI_ID, PLI.PAT_POIDS, PLI.LOT_ID
HAVING SUM(T2.PAT_POIDS) <= 6500

Mais avec à peu près 160 000 ligne dans la table elle


tourne pendant 5
minutes et je voudrais la réduire à quelques secondes


sans passer par un
curseur qui parcours dans l'ordre et met à jour en


calculant au fur et à
mesure. Par contre je suis prêt à créer plein de tables


temporaires si
besoin. Le top serait une formulation de la requete qui


la rendrait plus
rapide (j'aime rever) ...

Merci d'avance pour vos réponses.

Structure et petit jeux exemple.

CREATE TABLE #T_PLI_PLI (
PLI_ID INT IDENTITY(1, 1) PRIMARY KEY,
LOT_ID INT NULL, /* C'est une clé


étrangère */
PAT_ID INT NOT NULL, /* C'est une clé étrangère


*/
PAT_POIDS INT NOT NULL, /* Le poids d'un pli */
)

INSERT #T_PLI_PLI (LOT_ID ,PAT_ID ,PAT_POIDS) VALUES (1,


3, 15)
INSERT #T_PLI_PLI (LOT_ID ,PAT_ID ,PAT_POIDS) VALUES (1,


4, 10)
INSERT #T_PLI_PLI (LOT_ID ,PAT_ID ,PAT_POIDS) VALUES (1,


5, 15)
INSERT #T_PLI_PLI (LOT_ID ,PAT_ID ,PAT_POIDS) VALUES (1,


6, 10)
INSERT #T_PLI_PLI (LOT_ID ,PAT_ID ,PAT_POIDS) VALUES (1,


7, 15)
INSERT #T_PLI_PLI (LOT_ID ,PAT_ID ,PAT_POIDS) VALUES (1,


8, 15)
INSERT #T_PLI_PLI (LOT_ID ,PAT_ID ,PAT_POIDS) VALUES (1,


9, 10)
INSERT #T_PLI_PLI (LOT_ID ,PAT_ID ,PAT_POIDS) VALUES (1,


10, 10)
INSERT #T_PLI_PLI (LOT_ID ,PAT_ID ,PAT_POIDS) VALUES (1,


11, 20)
INSERT #T_PLI_PLI (LOT_ID ,PAT_ID ,PAT_POIDS) VALUES (1,


12, 15)



--

Olivier Besson
Pour m'ecrire, remplacer le <xx >de mon adresse par <com>.
(Je ne lis pas mes mails frequement)
To write me, replace <xx> part of my address by <com> (i


don't read my mails
very frequently).




.



Avatar
Olivier Besson
Bonjour,

Merci à tous les 2, en fait je penses que j'ai presque trouvé ma solution en
rajoutant une colonne poids total à ma table et en faisant en gros
UPDATE MA_TABLE
SET MON_POIDS_TOTAL = (SELECT SUM(MON_POIDS) FROM MA_TABLE WHERE ID < ID)

Après j'ai une table

Pli Poids PoidsTotal
1 10 10
2 15 25
3 12 37
4 19 56
5 30 86

et je suis presque au bout.



--

Olivier Besson
Pour m'ecrire, remplacer le <xx >de mon adresse par <com>.
(Je ne lis pas mes mails frequement)
To write me, replace <xx> part of my address by <com> (i don't read my mails
very frequently).



"lionelp" a écrit dans le message de
news:293e901c465bf$dce96a20$
Bonjour,

Tu fais un produit cartésien sur 160000 lignes ce qui fait
qqch de l'ordre de 12 milliard de lignes qu'il va falloir
réduire via aggregations puis filtrer. C'est beaucoup de
travail.
Maintenant, si tu passes par un curseur, tu ne balaie ta
table qu'une fois d'après ce que je comprend de ton
traitement, et tu vas peut-être effectivement passer à
quelques secondes. Donc ne sois pas sectaire, le curseur a
des chances d'être ton ami dans ce cas précis.

Cordialement,
LionelP

-----Message d'origine-----
Bonjour

J'ai une table qui contient des plis : 1 ligne par pli


avec une PK = PLI_ID
dans l'ordre ou je les veux, une colonne LOT_ID donnant


l'appartenance à un
lot, une colonne PAT_POIDS donnant le poids.

PLI 1 / LOT 1 / 10 grammes
PLI 2 / LOT 1 / 20 grammes
PLI 3 / LOT 1 / 15 grammes
PLI X / LOT Y / z grammes

Je cherche à récupérer pour chaque lot tous les plis dans


l'ordre de PLI_ID
jusqu'à ce que la somme des poids soit <= 6500 grammes.
Cette requete marche :

SELECT PLI.PLI_ID, PLI.PAT_POIDS, PLI.LOT_ID, SUM


(T2.PAT_POIDS)
FROM #T_PLI_PLI PLI
LEFT OUTER JOIN #T_PLI_PLI T2 ON PLI.LOT_ID T2.LOT_ID AND PLI.PLI_ID
= T2.PLI_ID


GROUP BY PLI.PLI_ID, PLI.PAT_POIDS, PLI.LOT_ID
ORDER BY PLI.PLI_ID, PLI.PAT_POIDS, PLI.LOT_ID
HAVING SUM(T2.PAT_POIDS) <= 6500

Mais avec à peu près 160 000 ligne dans la table elle


tourne pendant 5
minutes et je voudrais la réduire à quelques secondes


sans passer par un
curseur qui parcours dans l'ordre et met à jour en


calculant au fur et à
mesure. Par contre je suis prêt à créer plein de tables


temporaires si
besoin. Le top serait une formulation de la requete qui


la rendrait plus
rapide (j'aime rever) ...

Merci d'avance pour vos réponses.

Structure et petit jeux exemple.

CREATE TABLE #T_PLI_PLI (
PLI_ID INT IDENTITY(1, 1) PRIMARY KEY,
LOT_ID INT NULL, /* C'est une clé


étrangère */
PAT_ID INT NOT NULL, /* C'est une clé étrangère


*/
PAT_POIDS INT NOT NULL, /* Le poids d'un pli */
)

INSERT #T_PLI_PLI (LOT_ID ,PAT_ID ,PAT_POIDS) VALUES (1,


3, 15)
INSERT #T_PLI_PLI (LOT_ID ,PAT_ID ,PAT_POIDS) VALUES (1,


4, 10)
INSERT #T_PLI_PLI (LOT_ID ,PAT_ID ,PAT_POIDS) VALUES (1,


5, 15)
INSERT #T_PLI_PLI (LOT_ID ,PAT_ID ,PAT_POIDS) VALUES (1,


6, 10)
INSERT #T_PLI_PLI (LOT_ID ,PAT_ID ,PAT_POIDS) VALUES (1,


7, 15)
INSERT #T_PLI_PLI (LOT_ID ,PAT_ID ,PAT_POIDS) VALUES (1,


8, 15)
INSERT #T_PLI_PLI (LOT_ID ,PAT_ID ,PAT_POIDS) VALUES (1,


9, 10)
INSERT #T_PLI_PLI (LOT_ID ,PAT_ID ,PAT_POIDS) VALUES (1,


10, 10)
INSERT #T_PLI_PLI (LOT_ID ,PAT_ID ,PAT_POIDS) VALUES (1,


11, 20)
INSERT #T_PLI_PLI (LOT_ID ,PAT_ID ,PAT_POIDS) VALUES (1,


12, 15)



--

Olivier Besson
Pour m'ecrire, remplacer le <xx >de mon adresse par <com>.
(Je ne lis pas mes mails frequement)
To write me, replace <xx> part of my address by <com> (i


don't read my mails
very frequently).




.