заменить значения NULL на последние значения, отличные от NULL, в серии результатов (SQL Server 2008 R2) - PullRequest
7 голосов
/ 12 августа 2011

для SQL Server 2008 R2

У меня есть набор результатов, который выглядит следующим образом (примечание [цена] является числовым, NULL ниже представляет собой Значение NULL, результирующий набор упорядочен по product_id и timestamp)

product timestamp          price 
------- ----------------   -----
   5678 2008-01-01 12:00   12.34
   5678 2008-01-01 12:01    NULL
   5678 2008-01-01 12:02    NULL
   5678 2008-01-01 12:03   23.45
   5678 2008-01-01 12:04    NULL

Я хочу преобразовать это в набор результатов, который (по существу) копирует ненулевое значение из последней предыдущей строки, чтобы получить набор результатов, который выглядит следующим образом:

product timestamp          price  
------- ----------------   -----
   5678 2008-01-01 12:00   12.34
   5678 2008-01-01 12:01   12.34
   5678 2008-01-01 12:02   12.34
   5678 2008-01-01 12:03   23.45
   5678 2008-01-01 12:04   23.45

Я не нашел ни одной агрегатной / оконной функции, которая позволила бы мне это сделать (опять же, это ТОЛЬКО необходимо для SQL Server 2008 R2.)

Я надеялся найти аналитическую агрегатную функцию, которая сделает это для меня, что-то вроде ...

LAST_VALUE(price) OVER (PARTITION BY product_id ORDER BY timestamp)

Но я, похоже, не нашел способа сделать "кумулятивное последнее ненулевое значение" в окне (чтобы связать окно с предыдущими строками, а не со всем разделом)

Помимо создания табличной пользовательской функции, есть ли встроенная функция, которая могла бы выполнить это?


UPDATE:

По-видимому, эта функциональность доступна в CTP «Денали», но не в SQL Server 2008 R2.

LAST_VALUE http://msdn.microsoft.com/en-us/library/hh231517%28v=SQL.110%29.aspx

Я только ожидал, что он будет доступен в SQL Server 2008. Он доступен в Oracle (по крайней мере, начиная с 10gR2), и я могу сделать нечто подобное в MySQL 5.1, используя локальную переменную.

http://download.oracle.com/docs/cd/E14072_01/server.112/e10592/functions083.htm

Ответы [ 3 ]

10 голосов
/ 12 августа 2011

Вы можете попробовать следующее:

* Обновлено **

-- Test Data
DECLARE @YourTable TABLE(Product INT, Timestamp DATETIME, Price NUMERIC(16,4))

INSERT INTO @YourTable
SELECT 5678, '20080101 12:00:00', 12.34
UNION ALL
SELECT 5678, '20080101 12:01:00', NULL
UNION ALL
SELECT 5678, '20080101 12:02:00', NULL
UNION ALL
SELECT 5678, '20080101 12:03:00', 23.45
UNION ALL
SELECT 5678, '20080101 12:04:00', NULL

;WITH CTE AS
(
    SELECT *
    FROM @YourTable
)

-- Query
SELECT A.Product, A.Timestamp, ISNULL(A.Price,B.Price) Price
FROM CTE A
OUTER APPLY (   SELECT TOP 1 *
                FROM CTE 
                WHERE Product = A.Product AND Timestamp < A.Timestamp
                AND Price IS NOT NULL
                ORDER BY Product, Timestamp DESC) B

--Results
Product Timestamp   Price
5678    2008-01-01 12:00:00.000 12.3400
5678    2008-01-01 12:01:00.000 12.3400
5678    2008-01-01 12:02:00.000 12.3400
5678    2008-01-01 12:03:00.000 23.4500
5678    2008-01-01 12:04:00.000 23.4500
2 голосов
/ 09 июня 2017

У меня есть таблица, содержащая следующие данные.Я хочу обновить все пустые значения в столбцах зарплаты с помощью предыдущего значения, не принимая нулевого значения.

Таблица:

id  name    salary
1   A       4000
2   B   
3   C   
4   C   
5   D       2000
6   E   
7   E   
8   F       1000
9   G       2000
10  G       3000
11  G       5000
12  G   

вот запрос, который работает для меня.

select a.*,first_value(a.salary)over(partition by a.value order by a.id) as abc from
(
     select *,sum(case when salary is null then 0 else 1 end)over(order by id) as value from test)a

вывод:

id  name    salary  Value   abc
1   A       4000    1     4000
2   B               1     4000
3   C               1     4000
4   C               1     4000
5   D       2000    2     2000
6   E               2     2000
7   E               2     2000
8   F       1000    3     1000
9   G       2000    4     2000
10  G       3000    5     3000
11  G       5000    6     5000
12  G               6     5000
2 голосов
/ 12 августа 2011

Попробуйте это:

;WITH SortedData AS
(
    SELECT
       ProductID, TimeStamp, Price,
       ROW_NUMBER() OVER(PARTITION BY ProductID ORDER BY TimeStamp DESC) AS 'RowNum'
    FROM dbo.YourTable
)
UPDATE SortedData
SET Price = (SELECT TOP 1 Price 
             FROM SortedData sd2
         WHERE sd2.RowNum > SortedData.RowNum 
           AND sd2.Price IS NOT NULL)
WHERE
    SortedData.Price IS NULL

По сути, CTE создает список, отсортированный по отметке времени (по убыванию) - сначала самый новый.При обнаружении значения NULL будет найдена следующая строка, содержащая цену NOT NULL, и это значение используется для обновления строки значением NULL.

...