SQL Server дает разные результаты с и без Order By - PullRequest
0 голосов
/ 03 июля 2019

Я пытаюсь включить ROW_NUMBER с ORDER BY, но он не работает должным образом. Я попытался использовать ORDER BY (без ROW_NUMBER), и результаты с и без ORDER BY отличаются (даже количество строк отличается).

Вот полный запрос (я знаю, что это не лучший запрос):

WITH cte1
AS
(   
    SELECT t1.OrderNo, t1.BlockID, t1.PcbID AS 'TopPcbID', t3.PcbID AS 'MountedOn', 
        t3.TimeDone AS TimeEnd, t9.TimeDone, t7.McID, t8.DeviceID, t8.Program, t6.CurMcID, t9.DeviceID AS D1, t9.Program AS P1,
        ROW_NUMBER() OVER(PARTITION BY t3.PcbID, t7.McID ORDER BY t9.TimeDone DESC) RN
    FROM PanelBlockTrace t1
        INNER JOIN (SELECT PcbID, MIN(BlockNo) AS 'MINIM' FROM PanelBlockTrace
            GROUP BY PcbID) t2 ON t1.PcbID = t2.PcbID
        INNER JOIN PcbTrace t3 ON t1.PcbID = t3.PcbID OR 
            (CASE WHEN t1.BlockNo = 0 THEN t1.BlockID ELSE t1.PcbID END) = t3.PcbID
        INNER JOIN (SELECT PcbID, MAX(McID) AS 'MAXIM' FROM PcbTrace
            WHERE Program NOT LIKE 'PANEL%' GROUP BY PcbID) t4 ON t3.PcbID = t4.PcbID
        INNER JOIN LineDesc t5 ON t3.McID = t5.McID
        INNER JOIN (SELECT LineID, MAX(McID) AS 'CurMcID' FROM LineDesc WHERE McID NOT LIKE '%9' GROUP BY LineID) t6 ON t5.LineID = t6.LineID
        INNER JOIN LineDesc t7 ON t5.LineID = t7.LineID
        LEFT JOIN PcbTrace t8 ON t3.PcbID = t8.PcbID AND t7.McID = t8.McID
        LEFT JOIN PcbTrace t9 ON t8.DeviceID IS NULL 
            AND t9.TimeDone BETWEEN DATEADD(DAY,-1,CONVERT(date, t3.TimeDone)) AND t3.TimeDone
            AND t9.McID = CurMcID AND t9.McID = t7.McID
    WHERE (t1.BlockID IN (...) OR t1.PcbID IN (...))
        AND t1.BlockNo = t2.MINIM
        AND t1.BlockID != t1.PcbID
        AND t1.PcbID != ''
        AND t3.Program NOT LIKE 'PANEL%'
        AND t3.McID = t4.MAXIM
),
cte11
AS
(
    SELECT * FROM cte1
    WHERE RN <= 3
),
cte12
AS
(
    SELECT t1.OrderNo, t1.BlockID, t1.TopPcbID, t1.MountedOn, t1.TimeEnd, LEAD(t1.TimeDone,2) OVER(ORDER BY t1.MountedOn) AS T1, 
        t1.McID, t1.DeviceID, t1.Program, t1.D1, t1.P1, t2.CurMcID, t1.RN
    FROM cte11 t1
        INNER JOIN
            (SELECT t1.MountedOn,t1.McID,t1.P1,t1.Program,MAX(RN) AS LastRec, t1.CurMcID-1 AS CurMcID 
            FROM cte11 t1
                LEFT JOIN (SELECT MountedOn,McID,Program,P1 FROM cte11 WHERE RN = 1) t2 ON
                    t1.MountedOn = t2.MountedOn AND t1.McID = t2.McID AND (t1.Program = t2.Program OR t1.P1 = t2.P1)
            WHERE RN <= 3
            GROUP BY t1.MountedOn, t1.McID, t1.P1, t1.Program, t1.CurMcID) t2 ON 
                t1.MountedOn = t2.MountedOn AND t1.McID = t2.McID 
                AND (t1.RN = t2.LastRec OR t1.RN = 1)

),
cte2
AS
(
    SELECT t1.*,t2.DeviceID AS D2, t2.Program AS P2, t2.TimeDone AS T2
    FROM cte12 t1
        LEFT JOIN PcbTrace t2 ON t1.DeviceID IS NULL AND t1.D1 IS NULL AND t1.McID = t1.CurMcID AND t1.McID = t2.McID
            AND t2.TimeDone BETWEEN DATEADD(DAY,-1,CONVERT(date, t1.T1)) AND t1.T1 

)
SELECT * FROM cte2

Во всяком случае, здесь все становится странным.

Конечная цель - включить ROW_NUMBER:

cte2
AS
(
    SELECT t1.*,t2.DeviceID AS D2, t2.Program AS P2, t2.TimeDone AS T2, 
        ROW_NUMBER() OVER (PARTITION BY MountedOn, t1.McID ORDER BY t2.TimeDone) RN2
    FROM cte12 t1
        LEFT JOIN PcbTrace t2 ON t1.DeviceID IS NULL AND t1.D1 IS NULL AND t1.McID = t1.CurMcID AND t1.McID = t2.McID
            AND t2.TimeDone BETWEEN DATEADD(DAY,-1,CONVERT(date, t1.T1)) AND t1.T1 

)
SELECT * FROM cte2

Поскольку добавление ROW_NUMBER приводит к совершенно другому результату (без ROW_NUMBER: 3762 строки, с ROW_NUMBER: 17 строк), я попытался упростить последнюю часть и понял, что причиной является ORDER BY.

cte2
AS
(
    SELECT t2.DeviceID AS Device2, t2.Program AS Program2, t2.TimeDone AS Time2
    FROM cte12 t1
        LEFT JOIN PcbTrace t2 ON t1.DeviceID IS NULL AND t1.D1 IS NULL AND t1.McID = t1.CurMcID AND t1.McID = t2.McID
            AND t2.TimeDone BETWEEN DATEADD(DAY,-1,CONVERT(date, t1.T1)) AND t1.T1 

)
SELECT * FROM cte2

Результат без ORDER BY (первые 10 из 3762 строк):

Device2 Program2    Time2
NULL    NULL    NULL
NULL    NULL    NULL
NULL    NULL    NULL
NULL    NULL    NULL
NULL    NULL    NULL
NULL    NULL    NULL
NULL    NULL    NULL
1557852877  G8542G004MPB_4M3_00 2019-05-15 00:01:59.777
1557852877  G8542G004MPB_4M3_00 2019-05-15 00:04:56.790
1557852877  G8542G004MPB_4M3_00 2019-05-15 00:05:42.843

Код с ЗАКАЗАТЬ ПО:

cte2
AS
(
    SELECT t2.DeviceID AS Device2, t2.Program AS Program2, t2.TimeDone AS Time2
    FROM cte12 t1
        LEFT JOIN PcbTrace t2 ON t1.DeviceID IS NULL AND t1.D1 IS NULL AND t1.McID = t1.CurMcID AND t1.McID = t2.McID
            AND t2.TimeDone BETWEEN DATEADD(DAY,-1,CONVERT(date, t1.T1)) AND t1.T1 

)
SELECT * FROM cte2
ORDER BY Time2

Результат (первые 10 из 17 строк):

Device2 Program2    Time2
NULL    NULL    NULL
NULL    NULL    NULL
NULL    NULL    NULL
NULL    NULL    NULL
NULL    NULL    NULL
NULL    NULL    NULL
NULL    NULL    NULL
NULL    NULL    NULL
NULL    NULL    NULL
NULL    NULL    NULL

Примечание:

  1. Этот cte является фактически четвертым cte и использует результат другого cte (cte12). Я не уверен, как это может повлиять на результат.
  2. Там нет ddl, потому что я извлекаю данные из существующей базы данных, и будет слишком сложно моделировать такое количество таблиц.
  3. И да, единственное различие между двумя последними запросами - это предложение ORDER BY, но количество возвращаемых строк отличается (3762 против 17)

Редактировать:

  1. Если я просто использую TOP (скажем, TOP 10000), результат будет, как и ожидалось, 3762 строк. Но если число TOP слишком велико (что-либо больше, чем TOP 27415), оно вернется к 17 строкам
  2. Каким-то образом это работает как ожидалось (3762 строки), если я изменяю временные ограничения с

    t2.TimeDone МЕЖДУ DATEADD (ДЕНЬ, -1, КОНВЕРТ (дата, t1.T1)) И t1.T1

    до

    t2.TimeDone МЕЖДУ DATEADD (ДЕНЬ, -1, t1.T1) И t1.T1

1 Ответ

0 голосов
/ 03 июля 2019

Если вы не укажете ORDER BY, SQL Server может возвращать результаты в любом удобном для вас порядке. Теоретически вы могли бы выполнять один и тот же запрос десятки раз, и каждый раз он мог бы создавать разные порядки.

Но он всегда будет возвращать одни и те же строки, если вы не используете TOP.


Подробнее после просмотра всего запроса:

В вашем первом CTE мы видим

ROW_NUMBER() OVER(PARTITION BY ... ORDER BY t9.TimeDone DESC) RN

и во втором CTE мы видим

   SELECT * FROM cte1
    WHERE RN <= 3

Теперь представьте, что SQL Server генерирует результаты для первого CTE. Сначала предлагается упорядочить эти значения по TimeDone desc, а затем присвоить номера строк результатам. Рассмотрим случай, когда есть несколько строк с одинаковым значением TimeDone. Он должен помещать значения с более высоким значением TimeDone перед более низкими значениями, но для строк с тем же значением он может размещать эти строки в любом удобном порядке. Затем вы отфильтровываете только первые 3 строки. (Это хитрый способ сделать то же самое, что и TOP!).

Допустим, у вас есть такая таблица:

ID  Time 
A   05:00
B   04:00
C   03:00
D   03:00
E   02:00
F   01:00

Вы просите SQL Server упорядочить их по Time desc и назначить каждому номер строки. Есть два возможных результата.

A  05:00  1
B  04:00  2
C  03:00  3
D  03:00  4
E  03:00  5
F  01:00  6

или

A  05:00  1
B  04:00  2
D  03:00  3        // <-- C and D
C  03:00  4        //     are swapped
E  03:00  5
F  01:00  6

Оба выполняют правило размещения более высоких значений времени перед более низкими значениями. Когда значения совпадают, выбор за SQL Server - это удобно.

Но если вы отфильтруете эти результаты на RN <= 3, вы получите два разных результата.

Если вы затем возьмете эти два результата и добавите их во все более и более сложные СТЕ, у вас могут получиться совершенно разные ответы.

Каждый раз, когда вы меняете общий запрос, добавляя TOP с различными значениями или ORDER BY, SQL Server может генерировать совершенно другой план выполнения для поиска результатов.

Даже если вы никогда не меняли запрос или данные, он мог генерировать разные результаты в зависимости от других факторов окружающей среды.

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