Когда оператор UPDATE имеет доступ к нескольким значениям, какое из них выбрать? - PullRequest
0 голосов
/ 09 мая 2018

Я не думал, что этот код будет работать, и теперь, похоже, он работает, я беспокоюсь, что он не всегда может работать:

IF OBJECT_ID('tempdb..#Dates') IS NOT NULL DROP TABLE #Dates

; WITH Dates
    AS (
        SELECT    CAST(DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0) AS DATE) AS CDate
        UNION ALL
        SELECT    DATEADD(dd, 1, CDate) AS CDate
            FROM Dates
                WHERE DATEADD(dd, 1, CDate) < CAST(GETDATE() AS DATE)
        )

SELECT    d.CDate
        , d.CDate AS BDate
    INTO #Dates
        FROM Dates d
            OPTION (MAXRECURSION 400)

; WITH BDate
    AS (
        SELECT    CDate
            FROM #Dates d
                WHERE CDate NOT IN ('2018-01-01', '2018-01-15', '2018-02-19') -- New Years, MLK Day, Presidents Day
                        AND DATEPART(dw, d.CDate) NOT IN (1,7)

        )

UPDATE d
    SET d.BDate = b.CDate
        FROM #Dates d
            JOIN BDate b
                ON d.CDate <= b.CDate

SELECT * FROM #Dates

Что я не понимаю, так это как он знает, какое значение выбрать для оператора UPDATE. Если мы выберем случайную дату, например, 24 января, оператор SELECT даст нам несколько значений (поскольку это среда, и мы избегаем выходных, этот список будет включать 24, 25 и 26 января, а затем перейти к 29, 30, 31-му , так далее.). Так почему же он выбирает тот, который я на самом деле хочу (минимальное значение, которое соответствует критериям)? Конечно, мне не удастся использовать ORDER BY для принудительного оформления ордера.

Обычно я бы написал свой код, например, так:

    IF OBJECT_ID('tempdb..#Dates') IS NOT NULL DROP TABLE #Dates

    ; WITH Dates
        AS (
            SELECT    CAST(DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0) AS DATE) AS CDate
            UNION ALL
            SELECT    DATEADD(dd, 1, CDate) AS CDate
                FROM Dates
                    WHERE DATEADD(dd, 1, CDate) < CAST(GETDATE() AS DATE)
            )

    SELECT    d.CDate
            , d.CDate AS BDate
        INTO #Dates
            FROM Dates d
                OPTION (MAXRECURSION 400)

    ; WITH BDate
        AS (
            SELECT    CDate
                FROM #Dates d
                    WHERE CDate NOT IN ('2018-01-01', '2018-01-15', '2018-02-19') -- New Years, MLK Day, Presidents Day
                            AND DATEPART(dw, d.CDate) NOT IN (1,7)
            )

    , BDMin
        AS (
            SELECT    d.CDate
                    , MIN(b.CDate) AS BDate
                FROM #Dates d
                    JOIN BDate b
                        ON d.CDate <= b.CDate
                    GROUP BY d.CDate
            )

    UPDATE d
        SET d.BDate = b.BDate
            FROM #Dates d
                JOIN BDMin b
                    ON d.CDate = b.CDate

    SELECT * FROM #Dates

Это, конечно, кажется безопаснее, но теперь мне интересно, если это необходимо.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...