Próbuję zmodyfikować zapytanie użytkownika, aby zwrócić dodatkową kolumnę, INV1.WhsCode z następującego zapytania SQL:

SELECT  T0.CardCode,
        T2.CardName,
        T0.CodeBars,
        T0.ItemCode,
        T0.ItemName,
        T3.Price AS [POS Price],
        T1.AvgPrice,
        T1.OnHand,
        T1.MinStock,
        T1.MaxStock,
        T0.NumInBuy AS Packsize,
        T0.LstSalDate,
        (
            SELECT SUM(Quantity) AS Expr1
            FROM dbo.INV1
            INNER JOIN OINV
                    ON INV1.DocEntry = OINV.DocEntry
            WHERE INV1.ItemCode = T0.ItemCode
                    AND INV1.WhsCode = [%2]
                    AND Month(OINV.DocDate) = month(GetDate())
        ) AS [Current Period],
        (
            SELECT SUM(Quantity) AS Expr1
            FROM dbo.INV1
            INNER JOIN OINV
                    ON INV1.DocEntry = OINV.DocEntry
            WHERE INV1.ItemCode = T0.ItemCode
                    AND INV1.WhsCode = [%2]
                    AND Month(OINV.DocDate) = month(GetDate()) - 1
        ) AS [Previous Period],
        (
            SELECT SUM(Quantity) AS Expr1
            FROM dbo.INV1
            INNER JOIN OINV
                    ON INV1.DocEntry = OINV.DocEntry
            WHERE INV1.ItemCode = T0.ItemCode
                    AND INV1.WhsCode = [%2]
                    AND Month(OINV.DocDate) = month(GetDate()) - 2
        ) AS [60-90],
        (
            SELECT TOP 1 OPDN.DocDate AS Expr1
            FROM dbo.PDN1
            INNER JOIN OPDN
                    ON PDN1.DocEntry = OPDN.DocEntry
            WHERE PDN1.ItemCode = T0.ItemCode
            ORDER BY OPDN.DocDate DESC
        ) AS LastGRNDate
FROM    OITM T0
        INNER JOIN OITW T1
            ON T0.ItemCode = T1.ItemCode
        INNER JOIN OCRD T2
            ON T0.CardCode = T2.CardCode
        INNER JOIN ITM1 T3
            ON T0.ItemCode = T3.ItemCode
        INNER JOIN OWHS T4
            ON T1.WhsCode = T4.WhsCode
        INNER JOIN OITB T5
            ON T0.ItmsGrpCod = T5.ItmsGrpCod
WHERE   T3.PriceList = '3'
        AND T4.WhsName = [%0]
        AND T5.ItmsGrpNam = [%1]

Jak to osiągnąć? (MS SQL Server 2008)

0
Kinyanjui Kamau 18 październik 2012, 17:20

2 odpowiedzi

Najlepsza odpowiedź

Jest wiele rzeczy błędnych/nieefektywnych w twoim zapytaniu, o których wiem, że nie pytałeś, ale i tak odpowiem, ponieważ pomoże to odpowiedzieć na pytanie, które zadałeś.

Należy unikać skorelowanych podzapytań tam, gdzie to możliwe, są chwile, że są one nieuniknione i są najlepszym rozwiązaniem, jednak tak często widzę je w miejscu, w którym JOIN wykonałby tę samą pracę, a optymalizator poradziłby sobie z dołączeniem o wiele lepiej. Na przykład masz:

SELECT  (
            SELECT TOP 1 OPDN.DocDate AS Expr1
            FROM dbo.PDN1
            INNER JOIN OPDN
                    ON PDN1.DocEntry = OPDN.DocEntry
            WHERE PDN1.ItemCode = T0.ItemCode
            ORDER BY OPDN.DocDate DESC
        ) AS LastGRNDate
FROM    OITM T0

To ocenia podzapytanie dla każdego wiersza, podczas gdy w przypadku ponownego napisania w ten sposób:

SELECT  LastGRN.LastGRNDate
FROM    OITM TO
        LEFT JOIN
        (   SELECT  PDN1.ItemCode, [LastGRNDate] = MAX(OPDN.DocDate)
            FROM    dbo.PDN1
                    INNER JOIN OPDN
                        ON PDN1.DocEntry = OPDN.DocEntry
            GROUP BY PDN1.ItemCode
        ) LastGRN
            ON LastGRN.ItemCode = T0.ItemCode

Uzyskalibyście ten sam wynik, ale ocenilibyście w znacznie bardziej efektywny sposób.

Kolejną wadą jest Twoja metoda używania MONTH(GETDATE()) - 1, aby otrzymać 2 miesiące temu. W styczniu to oceni się na 0 i nie otrzyma żadnych dopasowań. Najlepszym sposobem na to jest przekonwertowanie każdej daty na pierwszy dzień każdego miesiąca, używając czegoś podobnego do jego:

SELECT  [FirstOfThisMonth] = DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()), 0),
        [FirstOfLastMonth] = DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE() - 1), 0)

Ta sama zasada sprzężeń, a nie skorelowanych podzapytań może być również zastosowana do twoich kolumn ilościowych, a to daje dostęp do kolumn WhsCode, nie jest to konieczne, ale użyłem wspólnego wyrażenia tabelowego do oczyszczenia zapytania ( używając logiki daty z góry)

WITH Quantities AS
(   SELECT  [DocMonth] = DATEADD(MONTH, DATEDIFF(MONTH, 0, IONV.DocDate),
            Inv1.WhsCode,
            ItemCode,
            [Quantity] = SUM(Quantity)
    FROM    dbo.Inv1
            INNER JOIN OINV
                ON Inv1.DocEntry = OINV.DocEntry
    GROUP BY DATEADD(MONTH, DATEDIFF(MONTH, 0, IONV.DocDate), WhsCode, itemCode
)
SELECT  T0.ItemCode,
        [Current Period] = COALESCE(Cur.Quantity, 0),
        [Previous Period] = COALESCE(prev.Quantity, 0),
        [60-90] = COALESCE(prev2.Quantity, 0)
FROM    OITM T0
        LEFT JOIN Quantities cur
            ON cur.ItemCode = T0.ItemCode
            AND cur.DocDate = DATEADD(MONTH, DATEDIFF(MONTH, 0, CURRENT_TIMESTAMP), 0)
            AND Cur.WhsCode = [%2]
        LEFT JOIN Quantities prev
            ON prev.ItemCode = T0.ItemCode
            AND prev.DocDate = DATEADD(MONTH, DATEDIFF(MONTH, 0, CURRENT_TIMESTAMP) - 1, 0)
            AND prev.WhsCode = [%2]
        LEFT JOIN Quantities prev2
            ON prev2.ItemCode = T0.ItemCode
            AND prev2.DocDate = DATEADD(MONTH, DATEDIFF(MONTH, 0, CURRENT_TIMESTAMP) - 2, 0)
            AND prev2.WhsCode = [%2]

Połączenie tego wszystkiego w ostateczne zapytanie daje:

SELECT  T0.CardCode,
        T2.CardName,
        T0.CodeBars,
        T0.ItemCode,
        T0.ItemName,
        T3.Price,
        T1.AvgPrice,
        T1.OnHand,
        T1.MinStock,
        T1.MaxStock,
        T0.NumInBuy AS Packsize,
        T0.LstSalDate
        [Current Period] = COALESCE(Cur.Quantity, 0),
        [Previous Period] = COALESCE(prev.Quantity, 0),
        [60-90] = COALESCE(prev2.Quantity, 0)
        LastGRN.LastGRNDate
FROM    OITM T0
        INNER JOIN OITW T1
            ON T0.ItemCode = T1.ItemCode
        INNER JOIN OCRD T2
            ON T0.CardCode = T2.CardCode
        INNER JOIN ITM1 T3
            ON T0.ItemCode = T3.ItemCode
        INNER JOIN OWHS T4
            ON T1.WhsCode = T4.WhsCode
        INNER JOIN OITB T5
            ON T0.ItmsGrpCod = T5.ItmsGrpCod
        LEFT JOIN Quantities cur
            ON cur.ItemCode = T0.ItemCode
            AND cur.DocDate = DATEADD(MONTH, DATEDIFF(MONTH, 0, CURRENT_TIMESTAMP), 0)
            AND Cur.WhsCode = [%2]
        LEFT JOIN Quantities prev
            ON prev.ItemCode = T0.ItemCode
            AND prev.DocDate = DATEADD(MONTH, DATEDIFF(MONTH, 0, CURRENT_TIMESTAMP) - 1, 0)
            AND prev.WhsCode = [%2]
        LEFT JOIN Quantities prev2
            ON prev2.ItemCode = T0.ItemCode
            AND prev2.DocDate = DATEADD(MONTH, DATEDIFF(MONTH, 0, CURRENT_TIMESTAMP) - 2, 0)
            AND prev2.WhsCode = [%2]
        LEFT JOIN
        (   SELECT  PDN1.ItemCode, [LastGRNDate] = MAX(OPDN.DocDate)
            FROM    dbo.PDN1
                    INNER JOIN OPDN
                        ON PDN1.DocEntry = OPDN.DocEntry
            GROUP BY PDN1.ItemCode
        ) LastGRN
            ON LastGRN.ItemCode = T0.ItemCode
WHERE   T3.PriceList = '3'
AND     T4.WhsName = [%0]
AND     T5.ItmsGrpNam = [%1]

To wszystko nie zostało przetestowane, więc mogą wystąpić pewne literówki/niewielkie błędy składniowe, ale te same zasady powinny nadal obowiązywać.

Jeśli nadal myślisz o skorelowanych podzapytaniach, możesz użyć APPLY, aby uzyskać z niego dostęp do wielu kolumn. np.

SELECT  T0.Code, cur.WhsCode, cur.Expr1 AS [Current Period]
FROM    OITM T0
        OUTER APPLY
        (   SELECT  INV1.WhsCode, SUM(Quantity) AS Expr1
            FROM    dbo.INV1
                    INNER JOIN OINV
                        ON INV1.DocEntry = OINV.DocEntry
            WHERE   INV1.ItemCode = T0.ItemCode
            AND     INV1.WhsCode = [%2]
            AND     MONTH(OINV.DocDate) = MONTH(GETDATE())
        ) cur
1
GarethD 18 październik 2012, 18:43

Wszystkie podzapytania zawierają w klauzuli WHERE następujące informacje: INV1.WhsCode = [%2]

Oznacza to, że zamiast INV1.WhsCode możesz po prostu zwrócić [%2] w ten sposób:

SELECT  [%2] as WhsCode,
        T0.CardCode,
        T2.CardName,
        T0.CodeBars,
......

Zakłada się, że [%2] i inne podobne tokeny zostaną zastąpione przez aplikację czymś, co rozumie SQL Server, zanim zostanie wysłany do wykonania.

1
Sebastian Meine 18 październik 2012, 17:38