Dans le cadre d'une optimisation, je cherche à réduire le nombre de requêtes SQL faites lors du chargement d'une page. J'ai actuellement les quatre requêtes suivantes qui sont faites :
SELECT
P.[Starting Date]as current_promo
FROM [Sales Price] P
WHERE P.[Item No_] = :id
AND P.[Ending Date] >= getdate()
AND P.[Starting Date] <= getdate()
ORDER BY P.[Starting Date] ASC;
SELECT
P.[Starting Date]as current_promo
FROM [Purchase Line Discount] P
WHERE P.[Item No_] = :id
AND (P.[Line Discount_] > 0 OR P.[Line Discount_2] > 0)
AND (P.[Ending Date] >= getdate() OR P.[Ending Date] = '1753-01-01 00:00:00.000')
AND (P.[Starting Date] <= getdate() OR P.[Starting Date] = '1753-01-01 00:00:00.000')
ORDER BY P.[Starting Date] ASC;
SELECT
P.[Lot Quantity]as lot,
P.[Free Quantity]as free
FROM [Purchase Price] P
WHERE P.[Item No_] = :id
AND (P.[Ending Date] >= getdate() OR P.[Ending Date] = '1753-01-01 00:00:00.000')
AND (P.[Starting Date] <= getdate() OR P.[Starting Date] = '1753-01-01 00:00:00.000')
ORDER BY P.[Ending Date] ASC;
SELECT
P.[Starting Date]as next_promo
FROM [Purchase Price] P
WHERE P.[Item No_] = :id
AND P.[Minimum Quantity] = 1
AND P.[Ending Date] > P.[Starting Date]
AND P.[Starting Date] > getdate()
ORDER BY P.[Starting Date] ASC;
Dans chacune de ses requêtes, nous ne voulons en fait que le premier résultat pour l'affichage dans un tableau (BI et surveillance des données). Mettre un TOP 1 est facilement envisageable, mais cela ne change pas le fait qu'il faille réduire le nombre de requêtes. La première étape est donc la concaténation de ces 4 requêtes en une seule. Après de nombreuses recherches sur mon problème, j'en suis arrivé à ce type de requête :
SELECT
sales_promo,
purchase_promo,
lot, free,
next_promo
FROM
(
SELECT TOP 1 P.[Starting Date]as sales_promo
FROM [Purchase Line Discount] P
WHERE P.[Item No_] = :id
AND (P.[Line Discount _] > 0 OR P.[Line Discount _ 2] > 0)
AND (P.[Ending Date] >= getdate() OR P.[Ending Date] = '1753-01-01 00:00:00.000')
AND (P.[Starting Date] <= getdate() OR P.[Starting Date] = '1753-01-01 00:00:00.000')
ORDER BY P.[Starting Date] ASC
) salesPromo FULL OUTER JOIN
(
SELECT TOP 1 P.[Starting Date]as purchase_promo
FROM [Purchase Line Discount] P
WHERE P.[Item No_] = :id
AND (P.[Line Discount _] > 0 OR P.[Line Discount _ 2] > 0)
AND (P.[Ending Date] >= getdate() OR P.[Ending Date] = '1753-01-01 00:00:00.000')
AND (P.[Starting Date] <= getdate() OR P.[Starting Date] = '1753-01-01 00:00:00.000')
ORDER BY P.[Starting Date] ASC
) purchasePromo ON 1=1 FULL OUTER JOIN
(
SELECT TOP 1 P.[Lot Quantity]as lot, P.[Free Quantity]as free
FROM [Purchase Price] P
WHERE P.[Item No_] = :id
AND (P.[Ending Date] >= getdate() OR P.[Ending Date] = '1753-01-01 00:00:00.000')
AND (P.[Starting Date] <= getdate() OR P.[Starting Date] = '1753-01-01 00:00:00.000')
ORDER BY P.[Ending Date] ASC
) lotandfee ON 1=1 FULL OUTER JOIN
(
SELECT TOP 1 P.[Starting Date]as next_promo
FROM [Purchase Price] P
WHERE P.[Item No_] = :id
AND P.[Minimum Quantity] = 1
AND P.[Ending Date] > P.[Starting Date]
AND P.[Starting Date] > getdate()
ORDER BY P.[Starting Date] ASC
) nextPromo ON 1=1
Lorsque je lance la requête directement sur la base de données, j'ai les bons résultats attendus, c'est-à-dire une unique ligne avec un résultat ou null s'il n'y a pas de résultat. La seconde étape, mettre un BATCH pour avoir plusieurs IDs, est donc triviale.
Le problème :
En faisant cette requête avec PDO, puisque sur beaucoup de sous-requêtes je n'ai pas de résultat, il n'y a plus de lignes du tout. Par exemple, sur la seconde sous-requête, j'ai toujours un résultat. Mais si je n'en ai pas sur les trois autres, je n'ai aucune ligne en sortie. Alors qu'en exécutant la requête directement sur la base, j'ai mon résultat correct.
Ci-dessous une autre requête qui me donne le même résultat :
SELECT
sales_promo
purchase_promo,
lot, free,
next_promo
FROM
(
SELECT q.* FROM (SELECT NULL) AS d (dummy) LEFT JOIN (
SELECT TOP 1 P.[Starting Date]as sales_promo
FROM [Purchase Line Discount] P
WHERE P.[Item No_] = :id
AND (P.[Line Discount _] > 0 OR P.[Line Discount _ 2] > 0)
AND (P.[Ending Date] >= getdate() OR P.[Ending Date] = '1753-01-01 00:00:00.000')
AND (P.[Starting Date] <= getdate() OR P.[Starting Date] = '1753-01-01 00:00:00.000')
ORDER BY P.[Starting Date] ASC
) AS q ON 1=1) salesPromo,
(
SELECT q.* FROM (SELECT NULL) AS d (dummy) LEFT JOIN (
SELECT TOP 1 P.[Starting Date]as purchase_promo
FROM [Purchase Line Discount] P
WHERE P.[Item No_] = :id
AND (P.[Line Discount _] > 0 OR P.[Line Discount _ 2] > 0)
AND (P.[Ending Date] >= getdate() OR P.[Ending Date] = '1753-01-01 00:00:00.000')
AND (P.[Starting Date] <= getdate() OR P.[Starting Date] = '1753-01-01 00:00:00.000')
ORDER BY P.[Starting Date] ASC
) AS q ON 1=1) purchasePromo,
(
SELECT q.* FROM (SELECT NULL) AS d (dummy) LEFT JOIN (
SELECT TOP 1 P.[Lot Quantity]as lot, P.[Free Quantity]as free
FROM [Purchase Price] P
WHERE P.[Item No_] = :id
AND (P.[Ending Date] >= getdate() OR P.[Ending Date] = '1753-01-01 00:00:00.000')
AND (P.[Starting Date] <= getdate() OR P.[Starting Date] = '1753-01-01 00:00:00.000')
ORDER BY P.[Ending Date] ASC
) AS q ON 1=1) lotandfee,
(
SELECT q.* FROM (SELECT NULL) AS d (dummy) LEFT JOIN (
SELECT TOP 1 P.[Starting Date]as next_promo
FROM [Purchase Price] P
WHERE P.[Item No_] = :id
AND P.[Minimum Quantity] = 1
AND P.[Ending Date] > P.[Starting Date]
AND P.[Starting Date] > getdate()
ORDER BY P.[Starting Date] ASC
) AS q ON 1=1) nextPromo
J'ai cherché un peu partout d'où pourrait venir le problème. Je pense que c'est PDO qui enlève automatiquement les sous-requêtes avec NULL... Mais je suis un peu perdu, j'ai l'habitude de MySQL et c'est la première fois que je bosse sur MSSQL.