【问题标题】:A more graceful way to aggregate columns一种更优雅的聚合列的方式
【发布时间】:2011-09-05 14:11:58
【问题描述】:

这是一个简化的交易表。

每一行都是一个交易,它有一个唯一的交易 id(如果你愿意,可以是身份字段)、一个帐户指针(帐户表的外键,这里没有显示)、一个交易日期和一个美元金额。

下面的示例数据:

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

【问题讨论】:

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


    【解决方案1】:

    想到的第一个替代方法是使用分析(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
    

    【讨论】:

    • 好答案。我认为你的看起来更精致,整体更容易理解。我使用的是 2005,所以 CTE 绝对是一个选择。
    • 如果我希望将来包含更多依赖列(例如 transac_description),它也会更加灵活。
    【解决方案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)
       ) 
    

    【讨论】:

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

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

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

    结果是:

    trans_id    amount
    3           -3
    6           -2
    2            3
    

    【讨论】:

    • 没有冒犯,但是这个查询甚至不会运行,因为 trans_id 和 amount 不是 group by 语句的一部分。它的逻辑实际上毫无意义。查询引擎如何知道为每个 account_pointer 选择哪个 trans_id?抱歉,伙计,我不得不记下你。
    • 请尝试运行这个查询..这肯定有效..我已经用相同的表结构测试了这个查询然后只发布了它..
    • 我知道您正在尝试提供帮助,但我不使用 sybase,也从未使用过它。我在我的问题中明确指出我正在使用 Sql Server/tsql。无论您如何在 tsql 中测试,此查询都不起作用,它会告诉您“date”列在 Have 子句中无效,而 trans_id 和 amount 列在 select 语句中无效,因为它们没有出现在通过...分组。所以请在TSQL中测试一下,在sybase中给我一个答案几乎没用。真的很抱歉。
    猜你喜欢
    • 2015-08-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多