我正在使用SSMS 2008,尝试只选择一行/客户端。我需要选择以下列:client_name
,end_date
和program
。有些客户只有一个客户端行。但其他人有多重。
对于具有多行的客户,它们通常具有不同的end_date
和program
。例如:
CLIENT PROGRAM END_DATE
a b c
a d e
a f g
h d e
h f NULL
这是实际数据的真实简化版本。正如您将看到的,不同的客户端可以在同一个程序中(“d”)。但是同一个客户端不能同时在同一个程序中。
同样棘手的是end_date
可以为NULL,所以当我尝试使用>选择那些客户端时1行,我添加了一个HAVING语句>但是这消除了我的所有NULL End_date
行。
总而言之,我希望每个客户端有一行。因此,那些只有一行的客户+上面列出的客户具有以下标准:
End_date
最大或NULL的行。 (在大多数情况下,end_date
对于这些客户端为空)。 如何以尽可能少的逻辑实现这一目标?
答案 0 :(得分:4)
在SQL Server 2005及更高版本上,您可以将公用表表达式(CTE)与ROW_NUMBER()
和PARTITION BY
函数结合使用。此CTE将按一个标准“划分”您的数据 - 在您的情况下为Client
,为每个单独的客户端创建“分区”。然后,ROW_NUMBER()
将按照另一个条件对每个分区进行编号 - 这里我创建了一个DATETIME
- 并为每个分区分别设置了从1开始的数字。
因此,在这种情况下,按DATETIME DESC
排序,最新的行编号为1 - 这是我从CTE中选择时使用的事实。我在这里使用ISNULL()
函数将具有NULL end_date
某些任意值的行分配给“按顺序获取它们”。我不太确定我是否正确理解了您的问题:您是否想要选择具有给定end_Date
的NULL行,或者您是否希望优先于NULL上的现有end_Date
值?
这将为每个客户选择最新的行(对于数据的每个“分区”):
DECLARE @clients TABLE (Client CHAR(1), Program CHAR(1), END_DATE DATETIME)
INSERT INTO @clients
VALUES('a', 'b', '20090505'),
('a', 'd', '20100808'),
('a', 'f', '20110303'),
('h', 'd', '20090909'),
('h', 'f', NULL)
;WITH LatestData AS
(
SELECT Client, Program, End_Date,
ROW_NUMBER() OVER(PARTITION BY CLient ORDER BY ISNULL(End_Date, '99991231') DESC) AS 'RowNum'
FROM @clients
)
SELECT Client, Program, End_Date
FROM LatestData
WHERE RowNum = 1
输出结果为:
Client Program End_Date
a f 2011-03-03
h f (NULL)