2

У меня есть следующие данные:Неожиданные результаты с LAST_VALUE, CURRENT ROW, нулям

CREATE TABLE #Transfers (
    AddedOn DATETIME2 NOT NULL, 
    EmpID INT NOT NULL, 
    NewDeptID INT NULL 
) 

INSERT INTO #Transfers 
VALUES 
    ('2013-12-17 17:18:54.3499987', 19, 36), 
    ('2013-12-18 13:02:34.1168087', 19, NULL), 
    ('2014-01-28 11:41:55.8755928', 22, 100), 
    ('2014-02-05 10:36:36.3645703', 22, NULL), 
    ('2014-02-16 00:00:00.0000000', 22, 37), 
    ('2014-02-17 00:00:00.0000000', 22, NULL) 

Для каждой строки, я стараюсь, чтобы получить самую последнюю непустой NewDeptID (до этой строки):

SELECT *, 
    LAST_VALUE(NewDeptID) OVER (
     PARTITION BY EmpID 
     ORDER BY IIF(NewDeptID IS NULL,0,1), AddedOn 
     ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW 
    ) AS CurrentDeptID 
FROM #Transfers 
ORDER BY EmpID, AddedOn 

Если я правильно понимаю, нулевые значения должны быть исключены, поскольку они являются первыми значениями в окне - IIF(NewDeptID IS NULL,0,1).

Я хотел бы ожидать следующее:

AddedOn      EmpID NewDeptID CurrentDeptID 
2013-12-17 17:18:54.3499987 19  36   36 
2013-12-18 13:02:34.1168087 19  NULL  36 
2014-01-28 11:41:55.8755928 22  100  100 
2014-02-05 10:36:36.3645703 22  NULL  100 
2014-02-16 00:00:00.0000000 22  37   37 
2014-02-17 00:00:00.0000000 22  NULL  37 

Вместо этого предложения ORDER BY внутри LAST_VALUE игнорируется, и NULL возвращается, если текущая строка содержит NULL:

AddedOn      EmpID NewDeptID CurrentDeptID 
2013-12-17 17:18:54.3499987 19  36   36 
2013-12-18 13:02:34.1168087 19  NULL  NULL -- 
2014-01-28 11:41:55.8755928 22  100  100 
2014-02-05 10:36:36.3645703 22  NULL  NULL -- 
2014-02-16 00:00:00.0000000 22  37   37 
2014-02-17 00:00:00.0000000 22  NULL  NULL -- 

Я получаю те же результаты в SQL Server 2012 и 2014.

Является ли это ошибкой в ​​SQL Server, или я что-то пропускаю в синтаксисе функции окна?


Примечание: Если я развернуть окно, чтобы включить весь раздел, нули игнорируются:

SELECT *, 
    LAST_VALUE(NewDeptID) OVER (
     PARTITION BY EmpID 
     ORDER BY IIF(NewDeptID IS NULL,0,1), AddedOn 
     ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING 
    ) AS CurrentDeptID 
FROM #Transfers 
ORDER BY EmpID, AddedOn 

Результаты:

AddedOn      EmpID NewDeptID CurrentDeptID 
2013-12-17 17:18:54.3499987 19  36   36 
2013-12-18 13:02:34.1168087 19  NULL  36 
2014-01-28 11:41:55.8755928 22  100  37 
2014-02-05 10:36:36.3645703 22  NULL  37 
2014-02-16 00:00:00.0000000 22  37   37 
2014-02-17 00:00:00.0000000 22  NULL  37 

ответ

3

Нет, вы не поняли хорошо, как работают окна. ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW применяется послеORDER BY IIF..., поэтому сначала упорядочиваются строки (сначала те, которые имеют NULL, затем все остальное), а затем применяется ограничение ROWS .... Таким образом, это никогда не будет работать для вашей проблемы.

Подробно, статья OVER создает эти «окна», согласно PARTITION и ORDER BY.
Так, например, для строки с AddedOn = '2014-02-17 00:00:00.0000000', то ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW являются строки сверху и сама по себе (две строки, помеченные <------):

AddedOn      EmpID IIF() NewDeptID 

-- partition EmpID = 19 
2013-12-18 13:02:34.1168087 19  0  NULL  
2013-12-17 17:18:54.3499987 19  1  36   

-- partition EmpID = 22 
2014-02-05 10:36:36.3645703 22  0  NULL <--- 
2014-02-17 00:00:00.0000000 22  0  NULL <--- this is the LAST_VALUE 
2014-01-28 11:41:55.8755928 22  1  100  
2014-02-16 00:00:00.0000000 22  1  37   

Таким образом, CurrentDeptID колонка получает эти значения:

AddedOn      EmpID IIF() NewDeptID CurrentDeptID 

-- partition EmpID = 19 
2013-12-18 13:02:34.1168087 19  0  NULL  NULL 
2013-12-17 17:18:54.3499987 19  1  36  36 

-- partition EmpID = 22 
2014-02-05 10:36:36.3645703 22  0  NULL  NULL 
2014-02-17 00:00:00.0000000 22  0  NULL  NULL 
2014-01-28 11:41:55.8755928 22  1  100  100 
2014-02-16 00:00:00.0000000 22  1  37  37 

, которые затем повторно заказаны для конечного результата, в соответствии с внешним ORDER BY


Чтобы обойти эту проблему, можно использовать коррелировала подзапрос:

SELECT *, 
    (SELECT TOP (1) NewDeptID 
     FROM #Transfers AS ti 
     WHERE ti.EmpID = t.EmpID 
     AND ti.NewDeptID IS NOT NULL 
     AND ti.AddedOn <= t.AddedOn 
     ORDER BY AddedOn DESC 
    ) AS CurrentDeptID 
FROM #Transfers AS t 
ORDER BY EmpID, AddedOn ; 

Тест на SQL-Fiddle


То, что вы пытаетесь сделать бы на самом деле имеет смысл, если обнуляет может быть проигнорировано LAST_VALUE() функция.Это можно сделать с IGNORE NULLS, но, к сожалению, эта функция еще не реализована в SQL-Server. Вы можете увидеть, как он будет работать, в Oracle (fiddle-2):

SELECT AddedOn, EmpID, NewDeptID, 
    LAST_VALUE(NewDeptID) 
     IGNORE NULLS        -- check this 
     OVER (
     PARTITION BY EmpID 
     ORDER BY AddedOn 
     ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW 
    ) AS CurrentDeptID 
FROM Transfers 
ORDER BY EmpID, AddedOn ; 

Другой способ, с оконными функциями, который работает в SQL-сервере, является подсчет не-нуль NewDeptID до текущей строки первой , Вы можете проверить на fiddle-3:

WITH cte AS 
    (SELECT AddedOn, EmpID, NewDeptID, 
     COUNT(NewDeptID) OVER (
      PARTITION BY EmpID 
      ORDER BY AddedOn 
      ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW 
     ) AS cnt 
    FROM Transfers 
) 
SELECT AddedOn, EmpID, NewDeptID, 
    MIN(NewDeptID) OVER (PARTITION BY EmpID, cnt) AS CurrentDeptID 
FROM cte 
ORDER BY EmpID, AddedOn ; 
+0

Я не понимаю. Если сначала применяется «ORDER BY», тогда записи с нулевыми значениями сначала входят в раздел. При вычислении последнего значения в пределах раздела до текущей строки результат никогда не должен быть нулевым, если значение для всех предыдущих записей в разделе также не равно нулю. –

+0

Но вы сказали так: * «записи с нулевыми значениями сначала входят в раздел» *. Если строка имеет нуль, все строки перед ней также имеют нуль. И когда я написал «ORDER BY», я имел в виду «ORDER BY IIF ...», а не внешний порядок. –

+0

Ваш 'IIF()' не позволяет этого (все строки с нулевым размещением после всех строк с нулевым). Я отредактировал свой ответ с подробностями о том, как работают окна. –

Смежные вопросы