聚合列的更优雅方式

时间:2011-08-31 02:37:35

标签: sql sql-server sql-server-2005 tsql

这是一个简化的交易表。

每一行都是一个具有唯一transac id的事务(如果你愿意,还有身份字段),一个accountpointer(账户表的外键,这里没有显示),transac日期和美元金额。

以下示例数据:

trans_id acc_ptr trans_date amount
1        12      2011-08-24 2.0
2        12      2011-08-25 3.0
3        14      2011-07-28 -3.0
4        16      2011-06-14 -1.0
5        12      2011-05-15 0.5
6        16      2011-07-30 -2

我想要的很简单。显示按acc_ptr分组的最新transac,包括该日期的金额。

我的工作完美,但我想知道的是,是否有一种更优雅的方式(就编程而言)或更有效的方法来处理这个问题,特别是我对子查询的处理量?想看看你会如何接近它。

我的方法:

select acc_ptr
, max(trans_date) as [most rec transac date]
, (select amount from transactions t2 
where t2.trans_date = max(t1.trans_date) 
and t2.acc_ptr = t1.acc_ptr) as amount
from transactions t1
group by acc_ptr

4 个答案:

答案 0 :(得分:3)

首先想到的选择是使用分析(IE:ROW_NUMBER),但这是SQL Server 2005+的功能。

WITH example AS (
  SELECT t.acc_ptr,
         t.trans_date AS [most rec transac date],
         t.amount,
         ROW_NUMBER() OVER (PARTITION BY t.acc_ptr 
                                ORDER BY t.trans_date DESC) AS rnk
    FROM transactions t)
SELECT e.acc_tpr,
       e.trans_date,
       e.amount
  FROM example e
 WHERE e.rnk = 1

此示例中没有使用CTE(WITH语法)的性能值 - 这是等效的:

SELECT e.acc_tpr,
       e.trans_date,
       e.amount
  FROM (SELECT t.acc_ptr,
               t.trans_date AS [most rec transac date],
               t.amount,
               ROW_NUMBER() OVER (PARTITION BY t.acc_ptr 
                                      ORDER BY t.trans_date DESC) AS rnk
          FROM transactions t) e
 WHERE e.rnk = 1

答案 1 :(得分:2)

要注意,如果同一天有两笔交易,您的子查询可能会失败:

DECLARE @transactions TABLE (
  trans_id INT, 
  acc_ptr INT, 
  trans_date datetime, 
  amount money
) 

INSERT @transactions VALUES 
(1,12,'2011-08-24',2.0),
(2,12,'2011-08-25',3.0),
(3,14,'2011-07-28', -3.0),
(4,16,'2011-06-14', -1.0),
(5,12,'2011-05-15', 0.5),
(6,16,'2011-07-30', -2),
(7,16,'2011-07-30', -1) -- New transaction

select acc_ptr,
  max(trans_date) as [most rec transac date],
 (select amount 
  from @transactions t2 
  where t2.trans_date = max(t1.trans_date) 
  and t2.acc_ptr = t1.acc_ptr) as amount
  from @transactions t1
  group by acc_ptr

Result: 

Msg 512, Level 16, State 1, Line 17
Subquery returned more than 1 value. 
This is not permitted when the subquery follows =, !=, <, <= , >, >= 
or when the subquery is used as an expression.

OMG Ponies使用ROW_NUMBER解决了决胜局的问题。

另一个需要考虑的选择:

SELECT
  acc_ptr,
  amount,
  trans_date
FROM transactions t1
WHERE trans_id = 
  (SELECT
     MAX(trans_id)
   FROM transactions t2
   WHERE acc_ptr = t1.acc_ptr
   AND trans_date = 
     (SELECT
        MAX(trans_date) 
      FROM transactions
      WHERE acc_ptr = t2.acc_ptr)
   ) 

答案 2 :(得分:1)

另一种方法使用从SQL Server 2005开始提供的CROSS / OUTER APPLY运算符: 1)如果您只想显示包含交易的帐户,请使用CROSS APPLY

SELECT a.account_id, b.trans_date, b.amount
FROM Account a CROSS APPLY 
(
SELECT TOP(1) t.trans_date, t.amount
FROM Transactionts t
WHERE t.acc_ptr = a.account_id
ORDER BY t.trans_date DESC, t.trans_id DESC
) b

根据AdventureWorks数据库看一下这个例子(它只显示每个客户的最后一个订单):

SELECT  c.CustomerID, c.AccountNumber
        ,ca.OrderDate, ca.SubTotal
FROM    Sales.Customer c
CROSS APPLY
(
SELECT  TOP(1)
        soh.OrderDate, soh.SubTotal 
FROM    Sales.SalesOrderHeader soh
WHERE   soh.CustomerID = c.CustomerID 
ORDER BY soh.OrderDate DESC, soh.SalesOrderID DESC
) ca

2)如果您想要显示所有帐户,请使用OUTTER APPLY

答案 3 :(得分:-2)

这可以通过简单的查询

来完成
select tans_id, amount from tablename 
group by acc_ptr
having date =max(date)

结果是:

trans_id    amount
3           -3
6           -2
2            3