为什么以及如何找到拍卖获胜者查询?

Why and how does this finding the auction winner query work?

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

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 < 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');

查询使用 LEFT OUTER JOIN 来 try 来查找匹配 item_idb1 相同的行 b2,但是bid_price 大于 b1.

中的值

左外连接的工作方式是,如果没有匹配项,则无论如何都会返回左侧 table (b1) 的列,而 b2 的列是空的。如果我们在没有 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 |
+--------+---------+--------+---------+

大多数行 b1 与具有相同 item_id 但更大 bid_price.

的某些行 b2 匹配

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

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

WHERE b2.item_id IS NULL

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

下面是对正在发生的事情的快速细分:

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

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

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