【问题标题】:MySQL - condition on the joined row from from right tableMySQL - 来自右表的连接行的条件
【发布时间】:2013-03-11 14:48:31
【问题描述】:

我有两张桌子:

mysql> select * from orders;
+------+---------------------+------------+---------+
| id   | created_at          | foreign_id | data    |
+------+---------------------+------------+---------+
|    1 | 2010-10-10 10:10:10 |          3 | order 1 |
|    4 | 2010-10-10 00:00:00 |          6 | order 4 |
|    5 | 2010-10-10 00:00:00 |          7 | order 5 |
+------+---------------------+------------+---------+

mysql> select * from activities;
+------+---------------------+------------+------+
| id   | created_at          | foreign_id | verb |
+------+---------------------+------------+------+
|    1 | 2010-10-10 10:10:10 |          3 | get  |
|    2 | 2010-10-10 10:10:15 |          3 | set  |
|    3 | 2010-10-10 10:10:20 |          3 | put  |
|    4 | 2010-10-10 00:00:00 |          6 | get  |
|    5 | 2010-10-11 00:00:00 |          6 | set  |
|    6 | 2010-10-12 00:00:00 |          6 | put  |
+------+---------------------+------------+------+

现在我需要在foreign_id 列上加入activitiesorders:为每个订单选择一个活动(如果存在),使ABS(TIMESTAMPDIFF(SECOND, orders.created_at, activities.created_at)) 最少。例如。订单和活动几乎是同时创建的。

+----------+---------+---------------------+-------------+------+---------------------+
| order_id | data    | order_created_at    | activity_id | verb | activity_created_at |
+----------+---------+---------------------+-------------+------+---------------------+
|        1 | order 1 | 2010-10-10 10:10:10 |           1 | get  | 2010-10-10 10:10:10 |
|        4 | order 4 | 2010-10-10 00:00:00 |           4 | get  | 2010-10-10 00:00:00 |
|        5 | order 5 | 2010-10-10 00:00:00 |        NULL | NULL | NULL                |
+----------+---------+---------------------+-------------+------+---------------------+

以下查询生成包含所需行的行集。如果包含GROUP BY 语句,则无法控制加入activities 中的哪一行。

SELECT o.id AS order_id
     , o.data AS data
     , o.created_at AS order_created_at
     , a.id AS activity_id
     , a.verb AS verb
     , a.created_at AS activity_created_at 
FROM orders AS o 
LEFT JOIN activities AS a ON a.foreign_id = o.foreign_id;

是否可以编写这样的查询?理想情况下,我想避免使用 group by,因为这部分是更大的报告查询的一部分。

【问题讨论】:

  • 你想用这条线做什么:ABS(TIMESTAMPDIFF(SECOND, orders.created_at, activities.created_at))?
  • 我想最小化这个值。例如。如果订单是在 11:00 创建的,并且有三个活动 (1, 09:00), (2, 10:00), (3, 11:00), (3, 12:00) 我想要第三次活动。该行将返回创建或订单与活动之间的秒数

标签: mysql join group-by


【解决方案1】:

因为两个表都引用了一些神秘的外键,所以下面的查询可能会出错,但它可能会给你一个原则,你可以根据自己的目的进行调整...

DROP TABLE IF EXISTS orders;

CREATE TABLE orders
(id INT NOT NULL PRIMARY KEY
,created_at DATETIME NOT NULL
,foreign_id INT NOT NULL
,data    VARCHAR(20) NOT NULL
);

INSERT INTO orders VALUES
(1 ,'2010-10-10 10:10:10',3 ,'order 1'),
(4 ,'2010-10-10 00:00:00',6 ,'order 4'),
(5 ,'2010-10-10 00:00:00',7 ,'order 5');

DROP TABLE IF EXISTS activities;

CREATE TABLE activities
(id   INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,created_at          DATETIME NOT NULL
,foreign_id INT NOT NULL
,verb VARCHAR(20) NOT NULL
);

INSERT INTO activities VALUES
(1,'2010-10-10 10:10:10',3,'get'),
(2,'2010-10-10 10:10:15',3,'set'),
(3,'2010-10-10 10:10:20',3,'put'),
(4,'2010-10-10 00:00:00',6,'get'),
(5,'2010-10-11 00:00:00',6,'set'),
(6,'2010-10-12 00:00:00',6,'put');

SELECT o.id order_id
     , o.data
     , o.created_at order_created_at    
     , a.id activity_id 
     , a.verb 
     , a.created_at activity_created_at 
  FROM activities a 
  JOIN orders o 
    ON o.foreign_id = a.foreign_id 
  JOIN 
     ( SELECT a.foreign_id
            , MIN(ABS(TIMEDIFF(a.created_at,o.created_at))) x 
         FROM activities a 
         JOIN orders o 
           ON o.foreign_id = a.foreign_id 
        GROUP 
           BY a.foreign_id
     ) m 
    ON m.foreign_id = a.foreign_id
   AND m.x = ABS(TIMEDIFF(a.created_at,o.created_at))
 UNION DISTINCT
SELECT o.id 
     , o.data
     , o.created_at
     , a.id
     , a.verb
     , a.created_at
  FROM orders o
  LEFT
  JOIN activities a
    ON a.foreign_id = o.foreign_id 
 WHERE a.foreign_id IS NULL;
;

+----------+---------+---------------------+-------------+------+---------------------+
| order_id | data    | order_created_at    | activity_id | verb | activity_created_at |
+----------+---------+---------------------+-------------+------+---------------------+
|        1 | order 1 | 2010-10-10 10:10:10 |           1 | get  | 2010-10-10 10:10:10 |
|        4 | order 4 | 2010-10-10 00:00:00 |           4 | get  | 2010-10-10 00:00:00 |
|        5 | order 5 | 2010-10-10 00:00:00 |        NULL | NULL | NULL                |
+----------+---------+---------------------+-------------+------+---------------------+

【讨论】:

  • 谢谢,这很有帮助。我最终稍微修改了您的查询:SELECT o.id, o.data, a.verb, m.x FROM orders o LEFT JOIN ( SELECT o2.id o_id , a2.id a_id , MIN(ABS(TIMESTAMPDIFF(SECOND, a2.created_at, o2.created_at))) x FROM orders o2 JOIN activities a2 ON o2.foreign_id = a2.foreign_id GROUP BY o2.foreign_id ) m ON m.o_id = o.id LEFT JOIN activities a ON m.a_id = a.id
  • 操作,我没有注意到您的编辑。我修改后的查询似乎达到了相同的结果,而且比你的要简单一些。
  • 鉴于 o2.foreign_id = a2.foreign,我看不出选择 a2.id 的意义。我不认为我们的查询在逻辑上是相同的,所以它们会在更大的数据集上产生不同的结果。但只有你能说哪个是正确的。此外,两者都将面临较大的时差。最好转换为秒,做数学,然后再转换回来。在性能方面,我偷偷怀疑你的实际上比我的快 - 但我不确定。
猜你喜欢
  • 2013-08-23
  • 2011-04-29
  • 2015-10-28
  • 1970-01-01
  • 1970-01-01
  • 2017-11-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多