使用 table 作为队列在 SQL Server 2017 中查找以前的条件值
Lookup previous values on condition in SQL Server 2017 using a table as a queue
在 table 上查找一行时称为原因:
machine_id reason start_time
001234 moving 10:00:00
001234 parked 10:10:00
001234 moving 10:15:00
001234 NULL 10:20:00
001234 NULL 10:25:00
001234 dumping 10:30:00
009876 parked 10:00:00
009876 NULL 10:10:00
009876 NULL 10:15:00
009876 moving 10:20:00
009876 dumping 10:25:00
无论出于何种原因,我都需要获取非 NULL 的最新值,因此 001234 NULL 值都将变为 'moving',而 009876 NULL 值将变为 'parked'.
我通常会通过交叉应用来解决这个问题,例如:
SELECT
r1.machine_id
,ISNULL(r1.reason,r2.reason) AS reason
,r1.start_time
FROM #reason r1
CROSS APPLY(
SELECT TOP 1
r2.reason
FROM #reason r2
WHERE r2.machine_oid = r1.machine_oid
AND r2.start_time < r1.start_time
AND r2.reason IS NOT NULL
ORDER BY start_time DESC
) r2
但是我查询的这个table是几十万行(无法修改源数据库)而且查询的复杂度似乎接近n^2。
在 C++ 中,我会通过使用优先级队列并丢弃对象列表中的项目来检查不符合条件的项目来解决,因此复杂度接近 nlogn。
我尝试在此处了解 post 关于使用 table 作为队列的内容:http://rusanu.com/2010/03/26/using-tables-as-queues 但它超出了我的技能水平。
因为这是对我的数据集的一个非常普遍的要求,所以我希望有一个可以应用的优雅解决方案?
你可以这样做:
它会给你每个 machine_id
的 NULL 值
select a.* from reason a
inner join reason b on a.machine_id = b.machine_id
and a.reason is not null and b.reason is null and a.start_time < b.start_time
where
not exists(select 1 from reason c where a.machine_id = c.machine_id
and a.start_time < c.start_time and c.start_time < b.start_time)
像这样:
DECLARE @DataSource TABLE
(
[machine_id] VARCHAR(6)
,[reason] VARCHAR(12)
,[start_time] TIME
);
INSERT INTO @DataSource([machine_id], [reason], [start_time])
VALUES ('001234', 'moving', '10:00:00')
,('001234', 'parked', '10:10:00')
,('001234', 'moving', '10:15:00')
,('001234', NULL, '10:20:00')
,('001234', NULL, '10:25:00')
,('001234', 'dumping', '10:30:00')
,('009876', 'parked', '10:00:00')
,('009876', NULL, '10:10:00')
,('009876', NULL, '10:15:00')
,('009876', 'moving', '10:20:00')
,('009876', 'dumping', '10:25:00');
SELECT [machine_id]
,[reason] AS [reason_old]
,ISNULL([reason], MAX([Reason]) OVER (PARTITION BY [machine_id], [RowID])) AS [reason]
,[start_time]
FROM
(
SELECT *
,SUM(IIF([reason] IS NULL, 0, 1)) OVER (PARTITION BY [machine_id] ORDER BY [start_time] ASC) AS [RowID]
FROM @DataSource
) DS
ORDER BY [machine_id]
,[start_time];
想法是使用 SUM
将具有 NULL
值的记录与第一条具有 NOT NULL
值的记录分组。
SELECT *
,SUM(IIF([reason] IS NULL, 0, 1)) OVER (PARTITION BY [machine_id] ORDER BY [start_time] ASC) AS [RowID]
FROM @DataSource;
然后,我们可以简单地获取此类组的 MAX/MIN
值,因为这些聚合忽略 NULL
s 并将 return NOT NULL
值。
在 table 上查找一行时称为原因:
machine_id reason start_time
001234 moving 10:00:00
001234 parked 10:10:00
001234 moving 10:15:00
001234 NULL 10:20:00
001234 NULL 10:25:00
001234 dumping 10:30:00
009876 parked 10:00:00
009876 NULL 10:10:00
009876 NULL 10:15:00
009876 moving 10:20:00
009876 dumping 10:25:00
无论出于何种原因,我都需要获取非 NULL 的最新值,因此 001234 NULL 值都将变为 'moving',而 009876 NULL 值将变为 'parked'.
我通常会通过交叉应用来解决这个问题,例如:
SELECT
r1.machine_id
,ISNULL(r1.reason,r2.reason) AS reason
,r1.start_time
FROM #reason r1
CROSS APPLY(
SELECT TOP 1
r2.reason
FROM #reason r2
WHERE r2.machine_oid = r1.machine_oid
AND r2.start_time < r1.start_time
AND r2.reason IS NOT NULL
ORDER BY start_time DESC
) r2
但是我查询的这个table是几十万行(无法修改源数据库)而且查询的复杂度似乎接近n^2。
在 C++ 中,我会通过使用优先级队列并丢弃对象列表中的项目来检查不符合条件的项目来解决,因此复杂度接近 nlogn。
我尝试在此处了解 post 关于使用 table 作为队列的内容:http://rusanu.com/2010/03/26/using-tables-as-queues 但它超出了我的技能水平。
因为这是对我的数据集的一个非常普遍的要求,所以我希望有一个可以应用的优雅解决方案?
你可以这样做: 它会给你每个 machine_id
的 NULL 值select a.* from reason a
inner join reason b on a.machine_id = b.machine_id
and a.reason is not null and b.reason is null and a.start_time < b.start_time
where
not exists(select 1 from reason c where a.machine_id = c.machine_id
and a.start_time < c.start_time and c.start_time < b.start_time)
像这样:
DECLARE @DataSource TABLE
(
[machine_id] VARCHAR(6)
,[reason] VARCHAR(12)
,[start_time] TIME
);
INSERT INTO @DataSource([machine_id], [reason], [start_time])
VALUES ('001234', 'moving', '10:00:00')
,('001234', 'parked', '10:10:00')
,('001234', 'moving', '10:15:00')
,('001234', NULL, '10:20:00')
,('001234', NULL, '10:25:00')
,('001234', 'dumping', '10:30:00')
,('009876', 'parked', '10:00:00')
,('009876', NULL, '10:10:00')
,('009876', NULL, '10:15:00')
,('009876', 'moving', '10:20:00')
,('009876', 'dumping', '10:25:00');
SELECT [machine_id]
,[reason] AS [reason_old]
,ISNULL([reason], MAX([Reason]) OVER (PARTITION BY [machine_id], [RowID])) AS [reason]
,[start_time]
FROM
(
SELECT *
,SUM(IIF([reason] IS NULL, 0, 1)) OVER (PARTITION BY [machine_id] ORDER BY [start_time] ASC) AS [RowID]
FROM @DataSource
) DS
ORDER BY [machine_id]
,[start_time];
想法是使用 SUM
将具有 NULL
值的记录与第一条具有 NOT NULL
值的记录分组。
SELECT *
,SUM(IIF([reason] IS NULL, 0, 1)) OVER (PARTITION BY [machine_id] ORDER BY [start_time] ASC) AS [RowID]
FROM @DataSource;
然后,我们可以简单地获取此类组的 MAX/MIN
值,因为这些聚合忽略 NULL
s 并将 return NOT NULL
值。