繁体   English   中英

SQL:任何直接的方式来订购结果FIRST,然后按另一列排序?

[英]SQL: Any straightforward way to order results FIRST, THEN group by another column?

我在SQL中看到,GROUP BY必须在ORDER BY表达式之前。 这是否意味着在分组后完成排序会丢弃相同的行/列?

因为我似乎需要首先按时间戳列A排序行,然后丢弃列A中具有相同值的行。不确定如何完成此操作...

我正在使用MySQL 5.1.41

create table
(
    A int,
    B timestamp
)

数据可能是:

+-----+-----------------------+
|  A  |  B                    |
+-----+-----------------------+
|  1  |  today                |
|  1  |  yesterday            |
|  2  |  yesterday            |
|  2  |  tomorrow             |
+-----+-----------------------+

我的目标是:

+-----+-----------------------+
|  A  |  B                    |
+-----+-----------------------+
|  1  |  today                |
|  2  |  tomorrow             |
+-----+-----------------------+

基本上,我希望列B中具有最新时间戳的行(想想ORDER BY),并且列A中的每个值只有一行(想想DISTINCT或GROUP BY)。

我的实际项目详情,如果您需要这些:

在现实生活中,我有两个表 - userspayment_receipts

create table users
(
    phone_nr int(10) unsigned not null,
    primary key (phone_nr)
)

create table payment_receipts
(
    phone_nr int(10) unsigned not null,
    payed_ts timestamp default current_timestamp not null,
    payed_until_ts timestamp not null,
    primary key (phone_nr, payed_ts, payed_until_ts)
)

这些表可能包含其他列,我省略了IMO与此无关的所有内容。 作为移动支付方案的一部分,我必须定期向移动蜂窝网络中的用户发送短信,具体取决于付款是否到期。 发送短信时实现付款,这是高级税。 我保留了使用payment_receipts表进行的所有付款的记录,用于记账,模拟真实的商店,买方和卖方都获得购买收据的副本,以供参考。 此表存储每张收据的(卖家)副本。 客户收据是收到的SMS本身。 每次发送短信(从而完成付款)时,表格都会插入一张收据记录,说明付款的人,何时以及“直到何时”。 为了解释后者,想象一下订阅服务,但是它会无限期地跨越直到用户明确选择退出,此时用户记录被删除。 付款是提前一个月支付的,因此, payed_tspayed_until_ts之间的payed_until_ts是30天的时间。

当然,我有一个每天执行的批处理作业,需要选择每月到期的用户列表,作为自动续订的一部分。 要将此链接与之前的虚拟示例相关联,电话号码列phone_nrapayed_until_tsb ,但在实际代码中有两个表,这使我产生以下行为及其含义:删除用户记录时,收据留下来,用于簿记。 因此,我不仅需要按日期对付款进行分组并丢弃除最新付款收据日期以外的所有付款,我还需要注意不要选择不再有匹配用户记录的收据。

我解决通过寻找与最新的收据选择是由于缴费记录的问题payed_until_ts值(在大多数情况下都会有每个电话号码几个收据)每个phone_nr进出这些行的我还只需要离开那些phone_number,其中payed_until_ts早于批处理作业执行的时间。 我循环遍历这些号码列表并发送付款,为每个发送的短信存储新收据,其中payed_ts now()now()payed_until_ts now() + interval 30 daysnow() + interval 30 days

Select a,b from (select a,b from table order by b) as c group by a;

是的,首先完成分组,它会影响单个select而排序会影响union所有select语句的所有结果,例如:

select a, 'max', max(b) from tbl group by a
union all select a, 'min', min(b) from tbl group by a
order by 1, 2

(按order by使用字段编号,因为我无法为列命名而烦恼)。 每个group by仅影响其selectorder by影响组合结果集。

您似乎可以通过以下方式实现目标:

select A, max(B) from tbl group by A

这使用max聚合函数来基本上进行组前排序(它实际上并没有在任何合适的DBMS中对其进行排序,而是只选择合适的索引中的最大值(如果可用))。

根据你的新规则(用PostgreSQL测试)


您想要的查询:

SELECT    pr.phone_nr, pr.payed_ts, pr.payed_until_ts 
FROM      payment_receipts pr
JOIN      users
          ON (pr.phone_nr = users.phone_nr)
   JOIN      (select phone_nr, max(payed_until_ts) as payed_until_ts 
              from payment_receipts 
              group by phone_nr
             ) sub
             ON (    pr.phone_nr       = sub.phone_nr 
                 AND pr.payed_until_ts = sub.payed_until_ts)
ORDER BY  pr.phone_nr, pr.payed_ts, pr.payed_until_ts;


原始答案(有更新):

CREATE TABLE foo (a NUMERIC, b TEXT, DATE);

INSERT INTO foo VALUES 
   (1,'a','2010-07-30'),
   (1,'b','2010-07-30'),
   (1,'c','2010-07-31'),
   (1,'d','2010-07-31'),
   (1,'a','2010-07-29'),
   (1,'c','2010-07-29'),
   (2,'a','2010-07-29'),
   (2,'a','2010-08-01');

-- table contents
SELECT * FROM foo ORDER BY c,a,b;
 a | b |     c      
---+---+------------
 1 | a | 2010-07-29
 1 | c | 2010-07-29
 2 | a | 2010-07-29
 1 | a | 2010-07-30
 1 | b | 2010-07-30
 1 | c | 2010-07-31
 1 | d | 2010-07-31
 2 | a | 2010-08-01

-- The following solutions both retrieve records based on the latest date
--    they both return the same result set, solution 1 is faster, solution 2
--    is easier to read

-- Solution 1: 
SELECT    foo.a, foo.b, foo.c 
FROM      foo
JOIN      (select a, max(c) as c from foo group by a) bar
  ON      (foo.a=bar.a and foo.c=bar.c)
ORDER BY  foo.a, foo.b, foo.c;

-- Solution 2: 
SELECT    a, b, MAX(c) AS c 
FROM      foo main
GROUP BY  a, b
HAVING    MAX(c) = (select max(c) from foo sub where main.a=sub.a group by a)
ORDER BY  a, b;

 a | b |     c      
---+---+------------
 1 | c | 2010-07-31
 1 | d | 2010-07-31
 2 | a | 2010-08-01
(3 rows)  


评论:
1返回两次,因为它们是多个b值。 这是可以接受的(并建议)。 您的数据永远不会出现此问题,因为c基于b的值。

SELECT DISTINCT a,b
FROM tbl t
WHERE b = (SELECT MAX(b) FROM tbl WHERE tbl.a = t.a);
create table user_payments
(
    phone_nr int NOT NULL,
    payed_until_ts datetime NOT NULL
)

insert into user_payments
(phone_nr, payed_until_ts)
values
(1, '2016-01-28'), -- today
(1, '2016-01-27'), -- yesterday  
(2, '2016-01-27'), -- yesterday 
(2, '2016-01-29')  -- tomorrow

select phone_nr, MAX(payed_until_ts) as latest_payment
from user_payments
group by phone_nr

-- OUTPUT:
-- phone_nr latest_payment
-- 1        2016-01-28 00:00:00.000
-- 2        2016-01-29 00:00:00.000

在上面的示例中,我使用了datetime列,但类似的查询应该适用于timestamp列。

MAX函数基本上会执行“ORDER BY”payed_until_ts列,并为每个phone_nr选择最新值。 此外,由于“GROUP BY”子句,每个phone_nr只能获得一个值。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM