mysql 是否有一种[直接]的方法来使用SQL对结果进行排序 * 首先 *,然后 * 按另一列分组?

blmhpbnm  于 2023-03-28  发布在  Mysql
关注(0)|答案(5)|浏览(114)

我看到在SQL查询中,GROUP BY必须在ORDER BY表达式之前。这是否意味着在分组后进行排序会丢弃相同的行?
因为我似乎需要先按时间戳对行进行排序,* 然后 * 丢弃具有相同时间戳的行。
我用的是MySQL 5.1.41。
下面是用create table表示的表的定义:

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”中的每个值只有一行(考虑DISTINCTGROUP 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)
)

这些表可能包括其他列,但我忽略了这些无关的。实施支付方案,我必须通过Hive网络向用户发送SMS,根据付款是否到期而定期间隔。当SMS发送时,付款被实现,因为接收者为此征税。我使用payment_receipts表来记录所有已完成的付款,这是为了模拟一个真实的商店,买卖双方都得到一份购买收据的副本,以供参考。这个表存储了我的(卖方的)副本[每个收据]。客户的收据是收到的短信本身。每次发送短信(并且因此完成了支付)时,该表被插入收据记录,该收据记录陈述了谁支付、何时以及“直到何时”。为了解释后者,设想订阅服务,但是该订阅服务无限地跨越直到用户明确地选择退出,此时相应的用户记录被删除。付款提前一个月进行,因此作为规则,payed_tspayed_until_ts之间的差异是30天的时间价值。
我有一个每天执行的批处理作业,需要选择一个按月付款的用户列表,作为上述自动订阅续订的一部分。为了将其链接到前面的虚拟示例,电话号码列phone_nr将是列“A”,payed_until_ts将是列“B”,但实际上有两个表,这与以下行为有关:删除用户记录时,必须保留收据以进行簿记。因此,我不仅需要按日期对付款进行分组并丢弃除最新付款收据日期以外的所有付款,还需要注意不要选择不再有匹配用户记录的收据。
为了解决选择所需记录(即到期付款的记录)的问题,我需要为每个phone_nr查找具有最新payed_until_ts时间戳的收据(可以有几个,显然),并且在这些记录中,我还需要仅选择payed_until_ts早于批处理作业执行时间的那些电话号码。然后,我将向这些号码中的每一个发送SMS,为每个发送的SMS插入一个收据记录,其中payed_tsnow()payed_until_tsnow() + interval 30 days
但我似乎想不出所需的查询。

gudnpqoy

gudnpqoy1#

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

bvjveswy2#

是的,首先进行分组,它会影响单个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中进行排序,而是简单地从合适的索引中选择最大值(如果可用))。

xnifntxz

xnifntxz3#

SELECT DISTINCT a,b
FROM tbl t
WHERE b = (SELECT MAX(b) FROM tbl WHERE tbl.a = t.a);
y0u0uwnf

y0u0uwnf4#

根据您的新规则(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的值。

fsi0uk1n

fsi0uk1n5#

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获得一个值。

相关问题