2011-08-31 110 views
3

这是一个简化的交易表。聚合列的更优雅的方式

每一行是一个具有独特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分组的最近一次交易,包括该日期的金额。

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

我的方法:

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 

回答

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 
+0

很好的答案。我认为你的看起来更精致,总体上更容易理解。我使用的是2005年,所以CTE绝对是一种选择。 – deutschZuid

+0

如果我希望将来包含更多依赖列(例如transac_description),它也更加灵活。 – deutschZuid

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小马通过使用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) 
    ) 
+0

嗨8kb。感谢您的解决方案。你是绝对正确的。在实际的表中,日期字段是完整的,因为它具有整个日期/时间范围,直到毫秒,因此对于同一个帐户,两个事务在同一毫秒内发生的可能性极小。尽管如此,仍然有可能。 :)。 – deutschZuid

-2

这可以通过简单的查询来完成

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

结果是:

trans_id amount 
3   -3 
6   -2 
2   3 
+0

没有冒犯,但这个查询甚至不会运行,因为trans_id和金额不是按语句组的部分。其逻辑实际上毫无意义。查询引擎如何知道为每个account_pointer选择哪个trans_id?对不起,但我必须标记你的伴侣。 – deutschZuid

+0

请尝试运行此查询..这当然有效..我已测试此查询与上面的表结构相同,然后只发布它.. –

+0

注意..使用sybase 15 .. –

1

另一种方法使用CROSS/OUTER应用可用运营商从SQL Server 2005开始: 1)如果你只想显示acco与交易UNTS然后用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

+0

由于需要访问两个表,这种性能如何明智?但它完美的工作。没有分组,也没有排名。 – deutschZuid

+0

请使用'SET STATISTICS IO ON'(查看逻辑读取)来比较不同方法的性能。在这种情况下,由于TOP(1),如果'Transactionts'上有适当的索引(例如:覆盖索引:acc_ptr),那么来自'Transactionts'表的逻辑读取次数可能少于来自其他解决方案的逻辑读取次数+ trans_date + trans_id或不覆盖索引:acc_ptr)。非覆盖索引(仅限acc_ptr字段)将需要额外的步骤(密钥查找)来查找缺失值:trans_date。 –