Sql 从一对多表中获取多个最大值

Sql 从一对多表中获取多个最大值,sql,sql-server,tsql,Sql,Sql Server,Tsql,我有两张桌子,我想把MAXdate从一张桌子变成多张桌子。如果没有值,则应为NULL。我知道如何做的唯一方法是进行子查询,但如果有大约20种不同的类型,那么20个子查询听起来效率不够。有没有更好的办法 Table A: UserId | Name 1 | John 2 | Jane Table B: UserId | Type | Date 1 | A | 2015-01-01 1 | A |

我有两张桌子,我想把MAXdate从一张桌子变成多张桌子。如果没有值,则应为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
谢谢你的快速回答!它们工作得很好。我稍微修改了我的问题,因为我注意到我需要在某些类型上添加一些条件。例如只有当类型C大于类型B时,才应显示它。

要获得您甚至不需要加入的预期结果,只需执行group by,用例表达式执行条件聚合:

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

您可以执行条件聚合,而不是选择中的子查询:

但是,只有当您知道类型的值时,上述操作才会起作用。如果不这样做,则需要使用动态查询

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);

参考资料:

使用动态透视,无论有多少类型,都不需要更改查询

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
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
编辑:对于类型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

用户名为1的类型C为空?@Kason是的,它应该为空,因为我只想在日期大于类型B的情况下显示。为什么2015-09-09->2016-09-09?@Kason我的错,这是一个输入错误。应该是2015-09-09。谢谢你的关注!请参阅我的更新,但是有一些特殊的句柄,因此类型以c结尾。只有两个提示:要创建列列表,您应该更喜欢使用STUFF和FOR XML PATH的方法,值得一提的是,带有条件聚合的老式pivot GROUP BY为您提供了更多的控制。。。最后但并非最不重要的一点是,您可以在特设SQL中使用它。您的动态方法必须在EXEC无视图、无函数的情况下调用。。。
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