我的桌子有很多状态,比如
Id | Date | IsEnabled | IsUpdated | IsDuplicate | IsSuspended | ...
状态(IsEnabled,IsUpdated,IsDuplicate,IsSuspended ......)是可以为空的位。
我需要从此表中选择最新(但不大于某些输入日期)不可为空的状态。如果某些状态具有NULL
值,则选择以前不可为空的值。
我创建了select以仅选择最新值,并且无法理解如何获取以前不可为空的值。
;WITH CTE AS (
SELECT cbs.*, rn = ROW_NUMBER() OVER (PARTITION BY cbs.Id ORDER BY cbs.[Date] DESC)
FROM [dbo].CompanyBusinessStatus cbs
WHERE cbs.[Date] <= @inputDate
)
SELECT *
FROM CTE
WHERE rn = 1
我正在使用MS SQL 2016
数据示例:
1 | 2017-01-01 | 1 | 0 | 0 | 0
_______________________________________
1 | 2017-01-03 | 1 | NULL | NULL | 1
_______________________________________
2 | 2017-01-03 | 1 | 1 | NULL | 0
_______________________________________
1 | 2017-01-05 | 0 | 1 | 0 | NULL
如果@inputDate是&#39; 2017-01-04&#39;我需要选择
Id | IsEnabled | IsUpdated | IsDuplicate | IsSuspended
_________________________________________________________
1 | 1 | 0 | 0 | 1
_________________________________________________________
2 | 1 | 1 | NULL | 0
答案 0 :(得分:1)
单向(demo)将是
SELECT Id,
IsEnabled = CAST(RIGHT(MAX(yyyymmdd + CAST(IsEnabled AS CHAR(1))), 1) AS BIT),
IsUpdated = CAST(RIGHT(MAX(yyyymmdd + CAST(IsUpdated AS CHAR(1))), 1) AS BIT),
IsDuplicate = CAST(RIGHT(MAX(yyyymmdd + CAST(IsDuplicate AS CHAR(1))), 1) AS BIT),
IsSuspended = CAST(RIGHT(MAX(yyyymmdd + CAST(IsSuspended AS CHAR(1))), 1) AS BIT)
FROM dbo.CompanyBusinessStatus cbs
CROSS APPLY (SELECT FORMAT(Date, 'yyyyMMdd')) CA(yyyymmdd)
WHERE cbs.[Date] <= @inputDate
GROUP BY Id
如果您在id
上有覆盖索引(或者即使您没有获得哈希聚合),这可能会产生一个根本没有排序操作的计划,并且可能比Gordon便宜得多#&# 39;答案。
答案 1 :(得分:0)
对于下面的查询,我认为Order by
中的ROW_NUMBER
会将最少NULL
的记录作为输出的第一个记录。
WITH CTE AS (
SELECT cbs.*, rn = ROW_NUMBER() OVER (PARTITION BY cbs.Id ORDER BY cbs.[Date] DESC, IsEnabled DESC,IsUpdated DESC,IsDuplicate DESC,IsSuspended DESC)
FROM [dbo].CompanyBusinessStatus cbs
WHERE cbs.[Date] <= @inputDate
)
SELECT *
FROM CTE
WHERE rn = 1
答案 2 :(得分:0)
我的另一个答案明显错误地解释了这个问题。不幸的是,SQL Server仅提供FIRST_VALUE()
作为窗口函数。所以,这是一种方法:
SELECT DISTINCT cbs.id,
MAX(cbs.date) OVER (PARTITION BY cbs.id) as date,
FIRST_VALUE(IsEnabled) OVER (PARTITION BY cbs.id ORDER BY (CASE WHEN IsEnabled IS NULL THEN 2 ELSE 1 END), cbs.date DESC) as isEnabled,
FIRST_VALUE(IsUpdated) OVER (PARTITION BY cbs.id ORDER BY (CASE WHEN IsUpdated IS NULL THEN 2 ELSE 1 END), cbs.date DESC) as IsUpdated,
. . .
FROM [dbo].CompanyBusinessStatus cbs
WHERE cbs.[Date] <= @inputDate ;
为此目的,我不是SELECT DISTINCT
的粉丝,但它似乎是表达逻辑的最简单方法。
ANSI SQL为IGNORE NULL
(以及其他一些窗口函数)提供FIRST_VALUE()
选项。但是,SQL Server(尚未)支持此选项。
答案 3 :(得分:0)
我知道做你想做的唯一方法是为每个“状态”列做一个相关的子查询。编写的SQL很多,并且看起来不是很优雅,但它肯定适用于任何版本的SQL Server。
可能有一个更优雅的解决方案,包括UNPIVOTing然后RE-PIVOTing,但除非我有超过20个不同的“状态”列,否则我不会去那条路。