【问题标题】:Create a left join using a minimum column value?使用最小列值创建左连接?
【发布时间】:2015-01-13 18:21:10
【问题描述】:

我有两个表,我想从 review 表中选择 author 为 usr1 的所有行,并将其加入到帖子表中,其中 threadid 和 tid 列匹配,但使用 MIN(positionID)

这是我的评论表:

cid     tID         author     
 1   |    1      |  usr1     |    
 2   |    2      |  usr2     |    
 3   |    3      |  usr1     |   
 4   |    3      |  usr1     |    

还有帖子表:

 id   threadID    rating        positionID
 1   |    1      |  99.99      |   1
 2   |    1      |  150.00     |   2
 3   |    2      |  33.10      |   1
 4   |    2      |  10.00      |   2
 5   |    3      |  16.00      |   1
 6   |    3      |  45.00      |   2
 7   |    3      |  75.00      |   3

预期结果:

cid     tID         author    rating 
 1   |    1      |  usr1     |    99.99
 3   |    3      |  usr1     |    16.00
 4   |    3      |  usr1     |    16.00

阅读后我尝试了几个不同的查询,如下面的这个,但我在几行中得到了 NULL:

SELECT * FROM Reviews AS R
LEFT JOIN (SELECT * from posts GROUP BY positionID) AS P on P.threadID=R.tID
WHERE c.author_name='usr1'

也许左连接不是我需要的?

【问题讨论】:

  • 为什么你的结果集有重复的行?
  • 结果集没有重复行。 cid 不同
  • 但是其他的都一样吗?
  • 是的。其他列也很少有不同。为清楚起见省略。只要能拿到min(positionID),剩下的我都可以管理
  • 只是另一个问题 - 是否会出现最小 positionid 不是 1 的情况?

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


【解决方案1】:

左连接不适合在这里使用。如果您想从一个表中获取所有行,并且仅在它们匹配时从另一个表中获取行,则使用外连接。在这里您似乎只想获取两个表中都存在相应行的行。

你可以从写这个开始,得到你想要的行:

SELECT r.*
FROM reviews r
JOIN posts p ON p.threadid = r.tid
WHERE r.author = 'usr1';

就获得最小位置而言 - 如果它从 1 开始,它可能总是 1,因此您可以对其进行过滤。那应该给你留下这个查询:

SELECT r.cid, r.tid, r.author, p.rating
FROM reviews r
JOIN posts p ON p.threadid = r.tid
WHERE r.author = 'usr1' AND p.positionid = 1;

但是,如果您想获得最小位置 id,我会执行以下操作,虽然这看起来有点棘手,但可能有更好的方法:

这是获取组中第一项的小技巧。在这种情况下,您需要线程 ID 组中最早的 positionid:

SELECT *
FROM posts p
WHERE(
  SELECT COUNT(*)
  FROM posts pt
  WHERE pt.threadid = p.threadid AND pt.positionid <= p.positionid
  ) <= 1;

您可以使用该子查询来加入您的评论表,如下所示:

SELECT r.cid, r.tid, r.author, t.rating
FROM reviews r
JOIN(SELECT *
  FROM posts p
  WHERE(
    SELECT COUNT(*)
    FROM posts pt
    WHERE pt.threadid = p.threadid AND pt.positionid <= p.positionid
  ) <= 1) t
ON t.threadid = r.tid AND r.author = 'usr1';

这适用于SQL Fiddle,不过我建议先在整个数据集上对其进行测试。

注意以上看起来与您的结果集略有不同,因为我使用了您的想法并删除了 positionID 为 1 的行以确保将使用下一行。它按预期工作。

【讨论】:

  • 是的。但另一个似乎有点短,我也用我自己的试用查询得到了非常接近的东西。我希望我可以将两者都标记为已接受。 :) 非常感谢您的帮助!
【解决方案2】:
DROP TABLE IF EXISTS posts;

CREATE TABLE posts(
  id INT NOT NULL AUTO_INCREMENT,
  threadID INT NOT NULL,
  rating DECIMAL(5,2) NOT NULL,
  positionID INT NOT NULL,
  PRIMARY KEY(id));

INSERT INTO posts(threadid, rating, positionid) VALUES
(1, 99.99, 1),
(1, 150.00, 2),
(2, 33.10, 1),
(2, 10.00, 2),
(3, 45.00, 2),
(3, 75.00, 3);

DROP TABLE IF EXISTS reviews;

CREATE TABLE reviews(
  cid INT NOT NULL AUTO_INCREMENT,
  tid INT NOT NULL,
  author VARCHAR(10) NOT NULL,
  PRIMARY KEY(cid));

INSERT INTO reviews(tid, author) VALUES
(1, 'usr1'),
(2, 'usr2'),
(3, 'usr1'),
(3, 'usr1');

SELECT cid,tid,author,rating
  FROM posts p
  JOIN
     ( SELECT threadid,MIN(positionid) min_positionid FROM posts GROUP BY threadid) x
    ON x.threadid = p.threadid
   AND x.min_positionid = p.positionid
  JOIN reviews r ON r.tid = p.threadid
 WHERE author = 'usr1'
;
+-----+-----+--------+--------+
| cid | tid | author | rating |
+-----+-----+--------+--------+
|   1 |   1 | usr1   |  99.99 |
|   3 |   3 | usr1   |  45.00 |
|   4 |   3 | usr1   |  45.00 |
+-----+-----+--------+--------+

http://sqlfiddle.com/#!2/b2669/7

【讨论】:

  • 我认为在 SQL 中添加一点解释而不是一大段代码会更好,这对已经困惑的人没有帮助。
猜你喜欢
  • 2011-11-27
  • 2013-11-03
  • 1970-01-01
  • 1970-01-01
  • 2021-11-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多