2011-09-17 30 views
1

我正在使用SSMS 2008,试图只选择一行/客户端。我需要选择以下列:client_name,end_dateprogram。有些客户端只有一个客户端行。但其他人有多个。如何开发t-sql子查询,每个只选择一条记录?

对于那些有多行的客户,他们通常有不同的end_dateprogram。例如:

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语句> 1.但是这消除了我的所有NULL End_date行。总结一下,我想每个客户端都有一行。因此,那些只有一行总数的客户+上面列出的客户使用以下标准:

  • 只选择End_date最大或NULL的行。 (在大多数情况下,这些客户端的end_date为空)。

我该如何用尽可能少的逻辑来实现?

+0

我假设其中end_date为NULL的行优先于max(end_date)。但是,如果客户端对于两个不同的程序具有NULL end_date,应该返回什么,因为您只希望每个客户端返回一行? – user937146

回答

4

在SQL Server 2005及更高版本上,可以使用与ROW_NUMBER()PARTITION BY函数结合使用的通用表达式(CTE)。此CTE将按照一个标准“划分”您的数据 - 您的情况为Client,为每个单独的客户端创建一个“分区”。然后,ROW_NUMBER()将按每个分区的顺序对每个分区进行编号 - 在这里我创建了一个DATETIME - 并分别为每个分区从1开始分配数字。

所以在这种情况下,按DATETIME DESC排序,最新的一行编号为1 - 这就是我从CTE中选择时使用的事实。我在这里使用ISNULL()函数来分配那些具有NULL值的行,以便“按顺序获取它们”。我不太清楚,如果我正确理解您的问题:您是否想要选择那些具有给定的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) 
+0

感谢Marc,我想选择具有给定结束日期的NULL行。 – salvationishere

+0

@salvantionsishere:OK,在这种情况下,只需用99991231' - 9999年12月31日的ISNULL调用中的日期替换 - 那么带NULL的行将首先出现。 –

+0

超级!谢谢Marc;这正是我需要的! – salvationishere