从聚合查询中的第一个记录中获取值

时间:2018-05-16 10:44:34

标签: sql sql-server aggregate-functions

考虑以下可笑的简化费用数据库表:

|------------------|--------------|--------|
|       Name       | Description  | Amount |
|------------------|--------------|--------|
|    John Smith    |    Hotel     |  £100  |
|    John Smith    | Evening meal |   £30  |
|   Claire Jones   |    Lunch     |   £20  |
|    John Smith    |    Travel    |   £80  |
|   Claire Jones   |    Hotel     |  £150  |
|------------------|--------------|--------|

使用SQL

SELECT [Name], SUM([Amount])
FROM [dbo].[Expenses]
GROUP BY [Name]

我可以得到结果

|------------------|--------|
|    John Smith    |  £210  |
|   Claire Jones   |  £170  |
|------------------|--------|

但是,我想知道如何获得相同的结果,但是还有一个 Description 列,它只显示基表的 Description 列中的值聚合组中的第一条记录。例如:

|------------------|--------------|--------|
|       Name       | Description  | Amount |
|------------------|--------------|--------|
|    John Smith    |    Hotel     |  £210  |
|   Claire Jones   |    Lunch     |  £170  |
|------------------|--------------|--------|

这显然不是我正在使用的实际数据,但我想知道是否可以这样做?

3 个答案:

答案 0 :(得分:3)

没有第一个记录,因为SQL表代表无序数据。

因此,您可以使用列中的排序。在您的情况下,可能是:

SELECT Name, MIN(Description), SUM(Amount)
FROM dbo.Expenses
GROUP BY Name;

如果您有订购列,则可以使用条件聚合:

SELECT Name, 
       MAX(CASE WHEN seqnum = 1 THEN Description END),
       SUM(Amount)
FROM (SELECT e.*,
             ROW_NUMBER() OVER (PARTITION BY Name ORDER BY ?) as seqnum
      FROM dbo.Expenses e
     ) e
GROUP BY Name;

?是订购列的占位符。

使用FIRST_VALUE()还有另一种相当神秘的方式:

SELECT DISTINCT Name,
       FIRST_VALUE(Description) OVER (PARTITION BY name ORDER BY ?), 
       SUM(Amount) OVER (PARTITION BY name)
FROM dbo.Expenses;

这使用FIRST_VALUE()作为分析函数,但不是窗口函数。

答案 1 :(得分:2)

使用subquery

SELECT [Name], SUM([Amount]) Amount,
       (SELECT TOP 1 Description  
        FROM [dbo].[Expenses] 
        WHERE Name = e.Name 
        ORDER BY Name) Description
FROM [dbo].[Expenses] e
GROUP BY [Name]

答案 2 :(得分:0)

您还可以使用窗口功能 -

SELECT [Name],
    SUM([Amount]) OVER (PARTITION BY [Name]) as total_amount,
    ROW_NUMBER() OVER (PARTITION BY [Name] ORDER BY some_column) as row_num
  FROM [dbo].[Expenses]
  WHERE row_num = 1