Bonjour,

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 :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
 
                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 :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
 
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 :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
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.

Si quelqu'un a une piste, je suis preneur !