【问题标题】:为什么以及如何找到拍卖获胜者查询?
【发布时间】:2022-01-05 16:22:52
【问题描述】:

我的一个朋友想出了以下查询来查找获胜赌注

SELECT b1.*
FROM bids as b1
LEFT JOIN bids AS b2 ON b1.item_id = b2.item_id AND b1.bid_price < b2.bid_price
WHERE b2.item_id IS NULL

它似乎工作正常,但我不明白它是如何工作的,以及它是偶然的还是结果总是一样的。有人可以解释一下,它是如何工作的,尤其是 b1.bid_price &lt; b2.bid_price 如何与显然不存在的 b2.bid_price 一起工作?

这里是转储:

CREATE TABLE `bids` (
  `bid_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `bid_price` decimal(10,2) unsigned NOT NULL,
  `user_id` int(11) unsigned NOT NULL,
  `item_id` int(11) unsigned NOT NULL,
  `bid_date_created` datetime NOT NULL DEFAULT current_timestamp(),
  PRIMARY KEY (`bid_id`)
) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8mb4

INSERT INTO bids VALUES (1, 14000.33, 12, 1, '2021-10-27 19:07:21');
INSERT INTO bids VALUES (2, 13000.60, 6, 1, '2021-10-27 18:07:21');
INSERT INTO bids VALUES (3, 21000.00, 7, 4, '2021-10-29 18:07:21');
INSERT INTO bids VALUES (4, 17000.25, 6, 4, '2021-10-27 18:07:21');
INSERT INTO bids VALUES (6, 5500.00, 6, 7, '2021-11-21 11:46:17');
INSERT INTO bids VALUES (7, 1000.00, 6, 29, '2021-11-22 11:21:41');
INSERT INTO bids VALUES (8, 18000.00, 6, 1, '2021-11-23 11:21:11');
INSERT INTO bids VALUES (9, 110.00, 14, 30, '2021-11-28 15:24:56');
INSERT INTO bids VALUES (10, 120.00, 13, 30, '2021-11-28 15:25:11');
INSERT INTO bids VALUES (11, 159.00, 14, 30, '2021-11-28 15:25:19');
INSERT INTO bids VALUES (12, 170.00, 13, 30, '2021-11-28 15:25:34');
INSERT INTO bids VALUES (13, 200.00, 14, 30, '2021-11-28 15:25:57');
INSERT INTO bids VALUES (14, 250.00, 13, 30, '2021-11-28 15:26:02');
INSERT INTO bids VALUES (15, 6000.00, 14, 6, '2021-11-28 15:26:30');
INSERT INTO bids VALUES (16, 7300.00, 13, 6, '2021-11-28 15:26:44');
INSERT INTO bids VALUES (17, 10000.00, 14, 6, '2021-11-28 15:29:14');

【问题讨论】:

标签: mysql sql join greatest-n-per-group


【解决方案1】:

查询使用 LEFT OUTER JOIN 来尝试 找到与 b1 相同的 item_id 匹配的行 b2,但 bid_price 大于 b1 中的值.

左外连接的工作方式是,如果没有匹配,则无论如何都会返回左表 (b1) 中的列,b2 的列为 NULL。如果我们在没有 WHERE 子句的情况下测试查询,您可以在示例数据中看到这一点。

mysql> SELECT b1.bid_id, b1.item_id, b2.bid_id, b2.item_id 
FROM bids as b1 LEFT JOIN bids AS b2 ON b1.item_id = b2.item_id 
  AND b1.bid_price < b2.bid_price;
+--------+---------+--------+---------+
| bid_id | item_id | bid_id | item_id |
+--------+---------+--------+---------+
|      2 |       1 |      1 |       1 |
|      4 |       4 |      3 |       4 |
|      1 |       1 |      8 |       1 |
|      2 |       1 |      8 |       1 |
|      9 |      30 |     10 |      30 |
|      9 |      30 |     11 |      30 |
|     10 |      30 |     11 |      30 |
|      9 |      30 |     12 |      30 |
|     10 |      30 |     12 |      30 |
|     11 |      30 |     12 |      30 |
|      9 |      30 |     13 |      30 |
|     10 |      30 |     13 |      30 |
|     11 |      30 |     13 |      30 |
|     12 |      30 |     13 |      30 |
|      9 |      30 |     14 |      30 |
|     10 |      30 |     14 |      30 |
|     11 |      30 |     14 |      30 |
|     12 |      30 |     14 |      30 |
|     13 |      30 |     14 |      30 |
|     15 |       6 |     16 |       6 |
|     15 |       6 |     17 |       6 |
|     16 |       6 |     17 |       6 |
|      3 |       4 |   NULL |    NULL |
|      6 |       7 |   NULL |    NULL |
|      7 |      29 |   NULL |    NULL |
|      8 |       1 |   NULL |    NULL |
|     14 |      30 |   NULL |    NULL |
|     17 |       6 |   NULL |    NULL |
+--------+---------+--------+---------+

大多数行将b1b2 的某些行匹配,item_id 相同,但bid_price 更大。

在最后六行中,出价b1 没有其他出价更高的bid_price,因此这六个是每个item_id 的最高出价。这些是您想要的结果。

一种简单的过滤方法是使用您看到的 WHERE 子句:

WHERE b2.item_id IS NULL

确实,您可以使用b2 中已知为非 NULL 的任何列,因为这意味着它为 NULL 的唯一方法是由于 LEFT OUTER JOIN 而没有匹配。

【讨论】:

  • 非常感谢!就像通常发生的那样,在发布问题后,我自己设法删除了 where 子句,并看到了这种赌注的进展情况。然后用你的“如果没有匹配,那么...... b2 为NULL”它点击了。而且我相信它解释了为什么这个查询比在 WHERE 中使用子查询慢 5 倍 - 只是因为这个连接返回/检查了这么多行。
  • 性能取决于项目数和匹配行数。在某些情况下,使用左外连接方法比使用相关子查询快得多。另请参阅我对stackoverflow.com/a/1313293/20860 的回答
  • 通常是的,但在这种情况下,查询 SELECT b.* FROM items i JOIN bids b ON i.item_id = b.item_id and b.bid_price = (SELECT MAX(b1.bid_price) FROM bids b1 WHERE b1.item_id = i.item_id) 的执行速度比上面的查询快 3 到 5 倍。我还没有找到原因。但似乎 explain format="json" 支持我的主张。不仅查询成本与子查询相比减少了 10 倍,而且对于连接它说“rows_produced_per_join”:2138128(在有 100k 手的表上)
【解决方案2】:

以下是正在发生的事情的简要说明:

让我们从连接本身开始。 LEFT JOIN 表示它将从左侧 (b1) 获取所有结果,如果存在则从右侧 (b2) 获取任何相应的结果。如果不是,b2 中的所有列都将为空(请记住这一点,因为它在此查询的工作方式中起着重要作用)。

接下来,它加入item_id,因此它只获取该特定商品的价格。然后,它有另一个连接条件b1.bid_price &lt; b2.bid_price。与NULL 的任何比较最终都将被评估为假。请记住,必须满足这两个条件,否则b2 中的所有行都为空,并且b2 代表bets 表中的每个条目。这意味着如果b2 为空,那么它要么是该项目的唯一赌注(因为b1.item_id = b2.item_id 约束),要么是最高赌注(因为b1.bid_price &lt; b2.bid_price 约束)。

最后,WHERE 子句。如上所述,JOIN 子句设置得非常完美,因此最高出价都将在b2 中具有NULL 值。这使得这部分变得简单,因为现在WHERE 子句可以简单地获取连接的b2 具有NULL 值的每个实例。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-09-13
    • 2015-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多