如何在MySQL中对同一表的多列进行INNER JOIN

How to do a INNER JOIN on multiple columns of the same table in MySQL

提问人:Alexander Chereji 提问时间:8/24/2023 最后编辑:Alexander Chereji 更新时间:8/24/2023 访问量:59

问:

我有以下问题:我有三个表:日志、媒体和用户。

CREATE TABLE `users` (
  `id` INTEGER NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(20) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB
ROW_FORMAT=DEFAULT;
CREATE TABLE `media` (
  `id` INTEGER NOT NULL AUTO_INCREMENT,
  `title` VARCHAR(20) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB
ROW_FORMAT=DEFAULT;
CREATE TABLE `log` (
  `id` INTEGER NOT NULL AUTO_INCREMENT,
  `action` VARCHAR(20) NOT NULL,
  `action_time` DATETIME NOT NULL,
  `user_id` INTEGER NOT NULL,
  `affected_user_id` INTEGER DEFAULT NULL,
  `media_id` INTEGER DEFAULT NULL,
  `comment` TEXT DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB
ROW_FORMAT=DEFAULT;

日志表的要点是,所做的每个操作都是由用户完成的,但有些操作会影响用户,有些操作会影响媒体,但并非总是如此。某些操作会影响用户和媒体。 列 user_id, affected_user_id 是要 users.id
的外键 列 media_id 是要 media.id 的外键 如果该列没有值,则该值为 NULL。

我想做的是对日志进行 SELECT 查询,并对其他两个表进行 INNER JOIN。

首先我尝试了这个查询,不知道有什么问题,所以这将是我第一次尝试做这样的事情:

SELECT log.action, log.action_time, log.user_id, user_name_table.name AS user_name, 
log.affected_user_id, affected_user_name_table.name AS affected_user_name, 
log.media_id, media.title AS media_title, log.comment 
FROM log 
INNER JOIN users AS user_name_table ON user_name_table.id = log.user_id 
INNER JOIN users AS affected_user_name_table ON affected_user_name_table.id = log.affected_user_id 
INNER JOIN media ON media.id = log.media_id 
WHERE log.user_id=1 OR log.affected_user_id=1

我在互联网上查找了如何在两列上内部连接同一个表,我真的不知道它是否正确,也许这也是问题所在。我希望此查询将返回日志表的所有列以及与user_id和affected_user_id关联的用户的名称。

在日志表中,有足够多的行具有 user_id=1 或 affected_user_id=1。但不知何故,我的结果集是空的。我没有收到错误消息,所以我的查询语法应该没问题。

我还尝试了其他几个版本,例如:

SELECT log.action, log.action_time, log.user_id, users.name AS user_name, log.affected_user_id, 
log.media_id, media.title AS media_title, log.comment 
FROM log 
INNER JOIN users ON users.id = log.user_id OR users.id=log.affected_user_id
INNER JOIN media ON media.id = log.media_id 
WHERE log.user_id=1 OR log.affected_user_id=1
SELECT log.action, log.action_time, log.user_id, users.name AS user_name, 
log.affected_user_id, 
log.media_id, media.title AS media_title, log.comment 
FROM log 
INNER JOIN users ON users.id = log.user_id = affected_user_id
INNER JOIN media ON media.id = log.media_id 
WHERE log.user_id=1 OR log.affected_user_id=1

我已经在互联网上搜索了解决方案,但我似乎没有找到。我什至问过 ChatGPT :),但它说我的查询完全没问题,应该返回我的结果集。

SQL MySQL Select 内部联接 结果集

评论

0赞 GarethD 8/24/2023
“但有些操作会影响用户,有些操作会影响媒体,但并非总是如此” - 听起来您可能想要在这些列上而不是 .如果没有同时影响媒体和用户的日志记录,则具有两个内部联接的查询不会返回任何内容。例如 dbfiddle.uk/0O0U7lR2LEFT JOININNER
0赞 Alexander Chereji 8/24/2023
也许我解释得有点复杂,但每个动作都是由用户执行的。有些操作会影响用户,有些操作会影响媒体,但也有一些操作会影响用户和媒体。在这种情况下,所有三列(user_id、affected_user_id、media_id)都将有一个值
0赞 GarethD 8/24/2023
是否有日志记录,其中填充了所有 3 个值,并且两个用户列中的任何一个的user_id为 1?例如,这返回了什么:SELECT COUNT(*) FROM logs WHERE media_id IS NOT NULL AND user_id IS NOT NULL AND affected_user_id IS NOT NULL AND (user_id = 1 OR affected_user_id = 1)

答:

0赞 Littlefoot 8/24/2023 #1

如果我理解正确,这会有所帮助吗?

SELECT log.action,
       log.action_time,
       log.user_id,
       users.name AS user_name,
       log.affected_user_id,
       log.media_id,
       media.title AS media_title,
       log.comment
  FROM log
       INNER JOIN users
          ON    (    users.id = log.user_id
                 AND log.user_id = 1)
             OR (    users.id = log.affected_user_id
                 AND log.affected_user_id = 1)
       INNER JOIN media ON media.id = log.media_id

[编辑],在您发布示例数据后:

SQL> SELECT * FROM users;

        ID NAME
---------- -----
         1 Alex
         2 Heinz
         3 Frank

SQL> SELECT * FROM media;

        ID TITLE
---------- ---------------
         1 Harry Potter
         2 Some other book
         3 No idea

SQL> SELECT * FROM log;

        ID ACTION       ACTION_TIME            USER_ID AFFECTED_USER_ID   MEDIA_ID
---------- ------------ ------------------- ---------- ---------------- ----------
         1 MEDIA_EDIT   2023-08-23 17:56:04          1                           5
         2 USER_EDIT    2023-08-23 17:56:04          1                3
         3 MEDIA_BORROW 2023-08-23 17:56:04          1                3          1

查询,将表联接到表两次:一次为(内部联接),另一次为(外部联接)。loguserslog.user_idlog.affected_user_id

SQL> SELECT l.action,
  2         l.action_time,
  3         u.name AS user_name,
  4         au.name AS affected_user_name,
  5         m.title media_title
  6    FROM LOG  l
  7         JOIN users u ON u.id = l.user_id
  8         LEFT JOIN users au ON au.id = l.affected_user_id
  9         LEFT JOIN media m ON m.id = l.media_id;

ACTION       ACTION_TIME         USER_NAME AFFECTED_USER_NAME      MEDIA_TITLE
------------ ------------------- --------- ----------------------- ---------------
MEDIA_BORROW 2023-08-23 17:56:04 Alex      Frank                   Harry Potter
USER_EDIT    2023-08-23 17:56:04 Alex      Frank
MEDIA_EDIT   2023-08-23 17:56:04 Alex

SQL>

评论

0赞 Alexander Chereji 8/24/2023
我试过了,但似乎没有用。只是为了让我理解,INNER JOIN 和 WHERE 同时是吗?(users.id = log.user_id AND log.user_id = 1)
0赞 Littlefoot 8/24/2023
你理解正确。如果它不起作用,请考虑发布一些示例数据和所需的输出(基于此类数据) - 这可能会帮助我们(社区)帮助您
0赞 Alexander Chereji 8/24/2023
对于用户来说,这些结果集是可能的: 对于媒体,它将是这些: 对于日志,有几种可能性: 列注释并不重要,所以我把它设为 NULL(1, 'Alex')(2, 'Heinz')(3, 'Frank')(1, 'Harry Potter')(2, 'Some other book')(3, 'no idea')(1, 'MEDIA_EDIT', '2023-08-23 17:56:04', 1, Null, 5, Null)(2, 'USER_EDIT', '2023-08-23 17:56:04', 1, 3, Null, Null)(3, 'MEDIA_BORROW', '2023-08-23 17:56:04', 1, 3, 5, Null)
0赞 Littlefoot 8/24/2023
我编辑了答案;请看一看。
1赞 Alexander Chereji 8/24/2023
我试了几次,它似乎工作得很好。我将学习左连接和外部连接以完全理解您的查询,但非常感谢您,您真的在棘手的情况下帮助了我!