从一对多表中获取多个MAX值

时间:2016-06-10 06:53:35

标签: sql sql-server tsql

我有两张桌子,我想从一对多的桌子获得MAX(日期)。如果没有值,则应为NULL。我只知道如何通过子查询来实现它,但如果有大约20种不同类型,那么20个子查询听起来效率不够。有没有更好的方法呢?

Table A:
UserId    | Name
1         | John
2         | Jane

Table B:
UserId   | Type    | Date
1        | A       | 2015-01-01
1        | A       | 2015-12-31
1        | B       | 2015-01-01
1        | B       | 2015-12-31
2        | B       | 2015-06-06
1        | C       | 2015-01-01
2        | C       | 2015-09-09

Result:
UserId | Type A date | Type B date | Type C date
1      | 2015-12-31  | 2015-12-31  | NULL
2      | NULL        | 2015-06-06  | 2015-09-09
Current solution:

 SELECT UserId,
        (SELECT MAX(Date) FROM B WHERE Type = 'A' AND B.UserId= A.UserId),
        (SELECT MAX(Date) FROM B WHERE Type = 'B' AND B.UserId= A.UserId),
        (SELECT MAX(Date) FROM B WHERE Type = 'C' AND B.UserId= A.UserId
        AND Date > (SELECT MAX(Date) FROM B WHERE Type = 'B' AND B.UserId =   A.UserId))
    FROM A

感谢您的快速解答!他们工作得很好。我修改了我的问题,因为我注意到我需要在某些类型上添加一些条件。例如。如果类型C大于类型B,则应仅显示类型。

3 个答案:

答案 0 :(得分:4)

要获得预期结果,您甚至不需要join,只需执行group by并使用case表达式执行条件聚合:

select userid,
       max(case when type = 'A' then date end) Adate,
       max(case when type = 'B' then date end) Bdate
from tableB
group by userid

如果您还想要用户名,可以使用以上查询加入tableA:

select a.name, b.Adate, b.Bdate
from tableA a
join (select userid,
             max(case when type = 'A' then date end) Adate,
             max(case when type = 'B' then date end) Bdate
      from tableB
      group by userid) b
    on a.userid = b.userid

但是,如果类型的数量未知,我也会按列类型进行分组。即返回不同行中的类型。

select a.name, b.type, b.date
from tableA a
left join (select userid, type, max(date) date,
           from tableB
           group by userid, type) b
    on a.userid = b.userid

答案 1 :(得分:2)

您可以在SELECT

中执行条件聚合而不是子查询
SELECT
    a.UserId,
    MAX(CASE WHEN b.Type = 'A' THEN b.date END) AS ADate,
    MAX(CASE WHEN b.Type = 'B' THEN b.date END) AS BDate
FROM TableA a
LEFT JOIN TableB b
    ON b.UserId = a.UserId
GROUP BY a.UserId;

但是,只有了解Type的值,上述内容才有效。如果不这样做,则需要使用动态查询。

DECLARE @sql NVARCHAR(MAX) = N'';

SELECT @sql = 
'SELECT
    a.UserId' + CHAR(10) +
(SELECT DISTINCT 
'   , MAX(CASE WHEN b.Type = ''' + Type + ''' THEN b.Date END) AS ' + QUOTENAME(Type + 'Date') + CHAR(10)
FROM TableB
FOR XML PATH('')
) +
'FROM TableA a
LEFT JOIN TableB b
    ON b.UserId = a.UserId
GROUP BY a.UserId;';

EXEC (@sql);

ONLINE DEMO

参考:Cross Tabs and Pivots by Jeff Moden

答案 2 :(得分:2)

使用Dynamic Pivot,无论您拥有多少类型,都不需要更改查询。

DECLARE @DynamicPivotQuery AS NVARCHAR(MAX)
DECLARE @ColumnName AS NVARCHAR(MAX)

SELECT @ColumnName = ISNULL(@ColumnName + ',','') + QUOTENAME([TYPE])
FROM (SELECT DISTINCT [TYPE] as [TYPE] FROM [master].[dbo].[YourTable]) AS TypeTable

SET @DynamicPivotQuery = 
'SELECT USERID,' + @ColumnName+'
FROM [master].[dbo].[YourTable]
PIVOT(MAX([Date]) 
      FOR [TYPE] IN (' + @ColumnName+')) AS PVTTable'

EXEC sp_executesql @DynamicPivotQuery

结果如下:

USERID  A           B
1       2015-12-31  2015-12-31
2       NULL        2015-06-06

编辑:对于类型C,有一些条件:

DECLARE @DynamicPivotQuery AS NVARCHAR(MAX)
DECLARE @ColumnName AS NVARCHAR(MAX)
DECLARE @ColumnNameForDisplay AS NVARCHAR(MAX)
DECLARE @otherCondition AS NVARCHAR(MAX)

SELECT @ColumnName = ISNULL(@ColumnName + ',','') + QUOTENAME([TYPE])
FROM (SELECT DISTINCT [TYPE] as [TYPE] FROM [master].[dbo].[YourTable]) AS TypeTable

SELECT @ColumnNameForDisplay = ISNULL(@ColumnNameForDisplay + ',','') + [TYPE2]
FROM (SELECT DISTINCT [TYPE]+' as [Type '+[TYPE]+' date]' as [TYPE2] FROM [master].[dbo].[YourTable] where type <> 'c') AS TypeTable

SELECT @otherCondition = ' ,Case when c <= b then null else c END as [Type C date] '


SET @DynamicPivotQuery = '
SELECT USERID,'+@ColumnNameForDisplay+@otherCondition+' from(
SELECT USERID,' + @ColumnName+'
FROM [master].[dbo].[YourTable]
PIVOT(MAX([Date]) 
FOR [TYPE] IN (' + @ColumnName+')) AS PVTTable) as t'

EXEC sp_executesql @DynamicPivotQuery

结果如下:

USERID  Type A date Type B date Type C date
1       2015-12-31  2015-12-31  NULL
2       NULL        2015-06-06  2015-09-09