如何在不合并结果行的情况下将 MySQL 中的两个 SQL 查询与不同的列合并
How to combine two SQL queries in MySQL with different columns without combining their resulting rows
上下文
我正在尝试在我的网站上创建一个“提要”系统,用户可以在其中转到他们的提要并查看他们在网站上可以执行的不同操作的所有新通知。例如,在“feed”部分,用户可以查看他们关注的用户是否创建了文章以及用户是否对文章发表了评论。我当前的提要系统只是使用两个单独的查询来获取此信息。但是,我想将这两个查询合并为一个,以便用户可以按时间顺序查看他们关注的 activity。我的系统现在的工作方式是,我从用户关注的每个人那里获得五篇文章并将其放入“提要”部分,然后获得五篇文章评论和 post 它位于“提要”部分的同一区域。我不想将查询分开,而是将它们组合起来,这样他们就不会看到连续五篇文章 post 和连续五篇文章评论,而是看到提要 post按时间顺序发生,其他用户是否先创建了一篇文章,然后评论,然后创建了另一篇文章,或者任何顺序,而不是总是看到相同的通知顺序。
问题
首先,让我向您展示我的 table 创建代码,如果您想重新创建它。首先要做的是创建一个 users
table,我的 articles
和 articlecomments
table 参考:
CREATE TABLE users (
idUsers int(11) AUTO_INCREMENT PRIMARY KEY NOT NULL,
uidUsers TINYTEXT NOT NULL,
emailUsers VARCHAR(100) NOT NULL,
pwdUsers LONGTEXT NOT NULL,
created DATETIME NOT NULL,
UNIQUE (emailUsers),
FULLTEXT(uidUsers)
) ENGINE=InnoDB;
接下来,让我们创建 articles
table:
CREATE TABLE articles (
article_id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
title TEXT NOT NULL,
article TEXT NOT NULL,
date DATETIME NOT NULL,
idUsers int(11) NOT NULL,
topic VARCHAR(50) NOT NULL,
published VARCHAR(50) NOT NULL,
PRIMARY KEY (article_id),
FULLTEXT(title, article),
FOREIGN KEY (idUsers) REFERENCES users (idUsers) ON DELETE CASCADE ON UPDATE
CASCADE
) ENGINE=InnoDB;
最后,我们需要 articlecomments
table:
CREATE TABLE articlecomments (
comment_id INT(11) AUTO_INCREMENT PRIMARY KEY NOT NULL,
message TEXT NOT NULL,
date DATETIME NOT NULL,
article_id INT(11) UNSIGNED NOT NULL,
idUsers INT(11) NOT NULL,
seen TINYTEXT NOT NULL,
FOREIGN KEY (article_id) REFERENCES articles (article_id) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY (idUsers) REFERENCES users (idUsers) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB;
为了充分填充此示例的 table,我们将使用这些语句:
INSERT INTO users (uidUsers, emailUsers, pwdUsers, created) VALUES ('genericUser', 'genericUser@hotmail.com', 'password', NOW());
INSERT INTO articles (title, article, date, idUsers, topic, published) VALUES ('first article', 'first article contents', NOW(), '1', 'other', 'yes');
INSERT INTO articles (title, article, date, idUsers, topic, published) VALUES ('second article', 'second article contents', NOW(), '1', 'other', 'yes');
INSERT INTO articles (title, article, date, idUsers, topic, published) VALUES ('third article', 'third article contents', NOW(), '1', 'other', 'yes');
INSERT INTO articlecomments (message, date, article_id, idUsers, seen) VALUES ('first message', NOW(), '1', '1', 'false');
INSERT INTO articlecomments (message, date, article_id, idUsers, seen) VALUES ('second message', NOW(), '1', '1', 'false');
INSERT INTO articlecomments (message, date, article_id, idUsers, seen) VALUES ('third message', NOW(), '1', '1', 'false');
我用来从 articles
和 articlecomments
table 获取数据的两个查询如下:
查询 1:
SELECT
articles.article_id, articles.title, articles.date,
articles.idUsers, users.uidUsers
FROM articles
JOIN users ON articles.idUsers = users.idUsers
WHERE articles.idUsers = '1' AND articles.published = 'yes'
ORDER BY articles.date DESC
LIMIT 5
查询 2:
SELECT
articlecomments.comment_id, articlecomments.message,
articlecomments.date, articlecomments.article_id, users.uidUsers
FROM articlecomments
JOIN users ON articlecomments.idUsers = users.idUsers
WHERE articlecomments.idUsers = '1'
ORDER BY articlecomments.date DESC
LIMIT 5
我如何组合这两个包含不同信息和列的查询,以便它们根据创建日期(分别为 articles.date
和 articlecomments.date
)排序?我希望它们在不同的行中,而不是在同一行中。所以,应该就像我分别查询它们并将结果行简单地组合在一起一样。如果有三篇文章和三篇文章评论,我希望总共返回六行。
这就是我想要的样子。鉴于有三篇文章和三篇文章评论,并且评论是在文章之后创建的,这就是结合上面的查询后的结果应该是这样的(我不确定这种描述是否可能给定不同的列名,但我我想知道是否可以完成类似的事情):
+-------------------------------+-------------------+---------------------+----------------------------------------------------------------+---------+-------------+
| id (article_id or comment_id) | title/message | date | article_id (because it is referenced in articlecomments table) | idUsers | uidUsers |
+-------------------------------+-------------------+---------------------+----------------------------------------------------------------+---------+-------------+
| 1 | first message | 2020-07-07 11:27:15 | 1 | 1 | genericUser |
| 2 | second message | 2020-07-07 11:27:15 | 1 | 1 | genericUser |
| 3 | third message | 2020-07-07 11:27:15 | 1 | 1 | genericUser |
| 2 | second article | 2020-07-07 10:47:35 | 2 | 1 | genericUser |
| 3 | third article | 2020-07-07 10:47:35 | 3 | 1 | genericUser |
| 1 | first article | 2020-07-07 10:46:51 | 1 | 1 | genericUser |
+-------------------------------+-------------------+---------------------+----------------------------------------------------------------+---------+-------------+
我试过的东西
我了解到这可能涉及 JOIN
或 UNION
运算符,但我不确定在这种情况下如何实现它们。我确实尝试通过简单地使用 (Query 1) UNION (Query 2)
来组合两个查询,这首先告诉我两个查询中的列数不同,所以我不得不从我的 [=22] 中删除 idUsers
列=]查询。这实际上让我有点接近,但它的格式不正确:
+------------+-------------------+---------------------+---------+-------------+
| article_id | title | date | idUsers | uidUsers |
+------------+-------------------+---------------------+---------+-------------+
| 2 | first message | 2020-07-07 10:47:35 | 1 | genericUser |
| 3 | third article | 2020-07-07 10:47:35 | 1 | genericUser |
| 1 | first article | 2020-07-07 10:46:51 | 1 | genericUser |
| 1 | second article | 2020-07-07 11:27:15 | 1 | genericUser |
| 2 | third article | 2020-07-07 11:27:15 | 1 | genericUser |
| 3 | first article | 2020-07-07 11:27:15 | 1 | genericUser |
+------------+-------------------+---------------------+---------+-------------+
有什么想法吗?让我知道是否有任何混淆。谢谢
Server type: MariaDB
Server version: 10.4.8-MariaDB - mariadb.org binary distribution
你可以这样交叉连接=
select select(1) from FROM [job] WITH (NOLOCK)
WHERE MemberCode = 'pay'
AND CampaignID = '2'
cross join
select(1)
FROM [product] WITH (NOLOCK)
WHERE MemberCode = 'pay'
AND CampaignID = '2'
这好像是MySQL。你可以这样做:
select * from (SELECT articles.article_id as id_article_comment, articles.title as title_message, articles.date as created, 'article' AS contenttype, articles.article_id as article_id, articles.idUsers, users.uidUsers FROM articles JOIN users ON articles.idUsers = users.idUsers WHERE articles.idUsers = '1' AND articles.published = 'yes' ORDER BY articles.date DESC LIMIT 5) a
union all
select * from (SELECT articlecomments.comment_id, articlecomments.message, articlecomments.date, 'article comment' AS contenttype, articlecomments.article_id, articlecomments.idUsers, users.uidUsers FROM articlecomments JOIN users ON articlecomments.idUsers = users.idUsers WHERE articlecomments.idUsers = '1' ORDER BY articlecomments.date DESC LIMIT 5) b
order by created DESC
参见此处示例:https://dbfiddle.uk/?rdbms=mysql_5.7&fiddle=26280a9c1c5f62fc33d00d93ab84adf3
结果如下:
id_article_comment | title_message | created | article_id | uidUsers
-----------------: | :------------- | :------------------ | ---------: | :----------
1 | first article | 2020-07-09 05:59:18 | 1 | genericUser
2 | second article | 2020-07-09 05:59:18 | 1 | genericUser
3 | third article | 2020-07-09 05:59:18 | 1 | genericUser
1 | first message | 2020-07-09 05:59:18 | 1 | genericUser
2 | second message | 2020-07-09 05:59:18 | 1 | genericUser
3 | third message | 2020-07-09 05:59:18 | 1 | genericUser
说明
因为我们要使用 order by
和 limit
,我们将从第一行创建一个子查询,并从第一个子查询创建 select 所有列。我们将在输出中按照我们希望的方式命名每个字段。
我们对第二个查询做同样的事情,并在它们之间添加一个 union all
子句。然后,我们根据创建日期(这是第一个查询中的别名)应用排序,以按照您想要的顺序获得您想要的结果。
如果您使用union
,重复的行将从结果中消除。如果您使用 union all
,重复行(如果存在)将被保留。 union all
更快,因为它结合了 2 个数据集(只要查询中的列相同。union
还必须查找重复的行并将它们从查询中删除。
您没有提及您使用的 MySQL 版本,所以我假设它是现代版本 (MySQL 8.x)。您可以使用 ROW_NUMBER()
在每个子集上生成一个行号,然后一个普通的 UNION ALL
就可以了。
我无法理解您想要的确切顺序,以及第四列 article_id (because it is referenced in articlecomments table)
的含义。如果您详细说明,我可以相应地调整这个答案。
生成您想要的结果集的查询是:
select *
from ( (
SELECT
a.article_id as id, a.title, a.date,
a.article_id, u.uidUsers,
row_number() over(ORDER BY a.date DESC) as rn
FROM articles a
JOIN users u ON a.idUsers = u.idUsers
WHERE a.idUsers = '1' AND a.published = 'yes'
ORDER BY a.date DESC
LIMIT 5
) union all (
SELECT
c.comment_id, c.message, c.date,
c.article_id, u.uidUsers,
5 + row_number() over(ORDER BY c.date DESC) as rn
FROM articlecomments c
JOIN users u ON c.idUsers = u.idUsers
WHERE c.idUsers = '1'
ORDER BY c.date DESC
LIMIT 5
)
) x
order by rn
结果:
id title date article_id uidUsers rn
-- -------------- ------------------- ---------- ----------- --
1 first article 2020-07-10 10:37:00 1 genericUser 1
2 second article 2020-07-10 10:37:00 2 genericUser 2
3 third article 2020-07-10 10:37:00 3 genericUser 3
1 first message 2020-07-10 10:37:00 1 genericUser 6
2 second message 2020-07-10 10:37:00 1 genericUser 7
3 third message 2020-07-10 10:37:00 1 genericUser 8
参见 db<>fiddle 中的 运行 示例。
上下文
我正在尝试在我的网站上创建一个“提要”系统,用户可以在其中转到他们的提要并查看他们在网站上可以执行的不同操作的所有新通知。例如,在“feed”部分,用户可以查看他们关注的用户是否创建了文章以及用户是否对文章发表了评论。我当前的提要系统只是使用两个单独的查询来获取此信息。但是,我想将这两个查询合并为一个,以便用户可以按时间顺序查看他们关注的 activity。我的系统现在的工作方式是,我从用户关注的每个人那里获得五篇文章并将其放入“提要”部分,然后获得五篇文章评论和 post 它位于“提要”部分的同一区域。我不想将查询分开,而是将它们组合起来,这样他们就不会看到连续五篇文章 post 和连续五篇文章评论,而是看到提要 post按时间顺序发生,其他用户是否先创建了一篇文章,然后评论,然后创建了另一篇文章,或者任何顺序,而不是总是看到相同的通知顺序。
问题
首先,让我向您展示我的 table 创建代码,如果您想重新创建它。首先要做的是创建一个 users
table,我的 articles
和 articlecomments
table 参考:
CREATE TABLE users (
idUsers int(11) AUTO_INCREMENT PRIMARY KEY NOT NULL,
uidUsers TINYTEXT NOT NULL,
emailUsers VARCHAR(100) NOT NULL,
pwdUsers LONGTEXT NOT NULL,
created DATETIME NOT NULL,
UNIQUE (emailUsers),
FULLTEXT(uidUsers)
) ENGINE=InnoDB;
接下来,让我们创建 articles
table:
CREATE TABLE articles (
article_id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
title TEXT NOT NULL,
article TEXT NOT NULL,
date DATETIME NOT NULL,
idUsers int(11) NOT NULL,
topic VARCHAR(50) NOT NULL,
published VARCHAR(50) NOT NULL,
PRIMARY KEY (article_id),
FULLTEXT(title, article),
FOREIGN KEY (idUsers) REFERENCES users (idUsers) ON DELETE CASCADE ON UPDATE
CASCADE
) ENGINE=InnoDB;
最后,我们需要 articlecomments
table:
CREATE TABLE articlecomments (
comment_id INT(11) AUTO_INCREMENT PRIMARY KEY NOT NULL,
message TEXT NOT NULL,
date DATETIME NOT NULL,
article_id INT(11) UNSIGNED NOT NULL,
idUsers INT(11) NOT NULL,
seen TINYTEXT NOT NULL,
FOREIGN KEY (article_id) REFERENCES articles (article_id) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY (idUsers) REFERENCES users (idUsers) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB;
为了充分填充此示例的 table,我们将使用这些语句:
INSERT INTO users (uidUsers, emailUsers, pwdUsers, created) VALUES ('genericUser', 'genericUser@hotmail.com', 'password', NOW());
INSERT INTO articles (title, article, date, idUsers, topic, published) VALUES ('first article', 'first article contents', NOW(), '1', 'other', 'yes');
INSERT INTO articles (title, article, date, idUsers, topic, published) VALUES ('second article', 'second article contents', NOW(), '1', 'other', 'yes');
INSERT INTO articles (title, article, date, idUsers, topic, published) VALUES ('third article', 'third article contents', NOW(), '1', 'other', 'yes');
INSERT INTO articlecomments (message, date, article_id, idUsers, seen) VALUES ('first message', NOW(), '1', '1', 'false');
INSERT INTO articlecomments (message, date, article_id, idUsers, seen) VALUES ('second message', NOW(), '1', '1', 'false');
INSERT INTO articlecomments (message, date, article_id, idUsers, seen) VALUES ('third message', NOW(), '1', '1', 'false');
我用来从 articles
和 articlecomments
table 获取数据的两个查询如下:
查询 1:
SELECT
articles.article_id, articles.title, articles.date,
articles.idUsers, users.uidUsers
FROM articles
JOIN users ON articles.idUsers = users.idUsers
WHERE articles.idUsers = '1' AND articles.published = 'yes'
ORDER BY articles.date DESC
LIMIT 5
查询 2:
SELECT
articlecomments.comment_id, articlecomments.message,
articlecomments.date, articlecomments.article_id, users.uidUsers
FROM articlecomments
JOIN users ON articlecomments.idUsers = users.idUsers
WHERE articlecomments.idUsers = '1'
ORDER BY articlecomments.date DESC
LIMIT 5
我如何组合这两个包含不同信息和列的查询,以便它们根据创建日期(分别为 articles.date
和 articlecomments.date
)排序?我希望它们在不同的行中,而不是在同一行中。所以,应该就像我分别查询它们并将结果行简单地组合在一起一样。如果有三篇文章和三篇文章评论,我希望总共返回六行。
这就是我想要的样子。鉴于有三篇文章和三篇文章评论,并且评论是在文章之后创建的,这就是结合上面的查询后的结果应该是这样的(我不确定这种描述是否可能给定不同的列名,但我我想知道是否可以完成类似的事情):
+-------------------------------+-------------------+---------------------+----------------------------------------------------------------+---------+-------------+
| id (article_id or comment_id) | title/message | date | article_id (because it is referenced in articlecomments table) | idUsers | uidUsers |
+-------------------------------+-------------------+---------------------+----------------------------------------------------------------+---------+-------------+
| 1 | first message | 2020-07-07 11:27:15 | 1 | 1 | genericUser |
| 2 | second message | 2020-07-07 11:27:15 | 1 | 1 | genericUser |
| 3 | third message | 2020-07-07 11:27:15 | 1 | 1 | genericUser |
| 2 | second article | 2020-07-07 10:47:35 | 2 | 1 | genericUser |
| 3 | third article | 2020-07-07 10:47:35 | 3 | 1 | genericUser |
| 1 | first article | 2020-07-07 10:46:51 | 1 | 1 | genericUser |
+-------------------------------+-------------------+---------------------+----------------------------------------------------------------+---------+-------------+
我试过的东西
我了解到这可能涉及 JOIN
或 UNION
运算符,但我不确定在这种情况下如何实现它们。我确实尝试通过简单地使用 (Query 1) UNION (Query 2)
来组合两个查询,这首先告诉我两个查询中的列数不同,所以我不得不从我的 [=22] 中删除 idUsers
列=]查询。这实际上让我有点接近,但它的格式不正确:
+------------+-------------------+---------------------+---------+-------------+
| article_id | title | date | idUsers | uidUsers |
+------------+-------------------+---------------------+---------+-------------+
| 2 | first message | 2020-07-07 10:47:35 | 1 | genericUser |
| 3 | third article | 2020-07-07 10:47:35 | 1 | genericUser |
| 1 | first article | 2020-07-07 10:46:51 | 1 | genericUser |
| 1 | second article | 2020-07-07 11:27:15 | 1 | genericUser |
| 2 | third article | 2020-07-07 11:27:15 | 1 | genericUser |
| 3 | first article | 2020-07-07 11:27:15 | 1 | genericUser |
+------------+-------------------+---------------------+---------+-------------+
有什么想法吗?让我知道是否有任何混淆。谢谢
Server type: MariaDB
Server version: 10.4.8-MariaDB - mariadb.org binary distribution
你可以这样交叉连接=
select select(1) from FROM [job] WITH (NOLOCK)
WHERE MemberCode = 'pay'
AND CampaignID = '2'
cross join
select(1)
FROM [product] WITH (NOLOCK)
WHERE MemberCode = 'pay'
AND CampaignID = '2'
这好像是MySQL。你可以这样做:
select * from (SELECT articles.article_id as id_article_comment, articles.title as title_message, articles.date as created, 'article' AS contenttype, articles.article_id as article_id, articles.idUsers, users.uidUsers FROM articles JOIN users ON articles.idUsers = users.idUsers WHERE articles.idUsers = '1' AND articles.published = 'yes' ORDER BY articles.date DESC LIMIT 5) a
union all
select * from (SELECT articlecomments.comment_id, articlecomments.message, articlecomments.date, 'article comment' AS contenttype, articlecomments.article_id, articlecomments.idUsers, users.uidUsers FROM articlecomments JOIN users ON articlecomments.idUsers = users.idUsers WHERE articlecomments.idUsers = '1' ORDER BY articlecomments.date DESC LIMIT 5) b
order by created DESC
参见此处示例:https://dbfiddle.uk/?rdbms=mysql_5.7&fiddle=26280a9c1c5f62fc33d00d93ab84adf3
结果如下:
id_article_comment | title_message | created | article_id | uidUsers -----------------: | :------------- | :------------------ | ---------: | :---------- 1 | first article | 2020-07-09 05:59:18 | 1 | genericUser 2 | second article | 2020-07-09 05:59:18 | 1 | genericUser 3 | third article | 2020-07-09 05:59:18 | 1 | genericUser 1 | first message | 2020-07-09 05:59:18 | 1 | genericUser 2 | second message | 2020-07-09 05:59:18 | 1 | genericUser 3 | third message | 2020-07-09 05:59:18 | 1 | genericUser
说明
因为我们要使用 order by
和 limit
,我们将从第一行创建一个子查询,并从第一个子查询创建 select 所有列。我们将在输出中按照我们希望的方式命名每个字段。
我们对第二个查询做同样的事情,并在它们之间添加一个 union all
子句。然后,我们根据创建日期(这是第一个查询中的别名)应用排序,以按照您想要的顺序获得您想要的结果。
如果您使用union
,重复的行将从结果中消除。如果您使用 union all
,重复行(如果存在)将被保留。 union all
更快,因为它结合了 2 个数据集(只要查询中的列相同。union
还必须查找重复的行并将它们从查询中删除。
您没有提及您使用的 MySQL 版本,所以我假设它是现代版本 (MySQL 8.x)。您可以使用 ROW_NUMBER()
在每个子集上生成一个行号,然后一个普通的 UNION ALL
就可以了。
我无法理解您想要的确切顺序,以及第四列 article_id (because it is referenced in articlecomments table)
的含义。如果您详细说明,我可以相应地调整这个答案。
生成您想要的结果集的查询是:
select *
from ( (
SELECT
a.article_id as id, a.title, a.date,
a.article_id, u.uidUsers,
row_number() over(ORDER BY a.date DESC) as rn
FROM articles a
JOIN users u ON a.idUsers = u.idUsers
WHERE a.idUsers = '1' AND a.published = 'yes'
ORDER BY a.date DESC
LIMIT 5
) union all (
SELECT
c.comment_id, c.message, c.date,
c.article_id, u.uidUsers,
5 + row_number() over(ORDER BY c.date DESC) as rn
FROM articlecomments c
JOIN users u ON c.idUsers = u.idUsers
WHERE c.idUsers = '1'
ORDER BY c.date DESC
LIMIT 5
)
) x
order by rn
结果:
id title date article_id uidUsers rn
-- -------------- ------------------- ---------- ----------- --
1 first article 2020-07-10 10:37:00 1 genericUser 1
2 second article 2020-07-10 10:37:00 2 genericUser 2
3 third article 2020-07-10 10:37:00 3 genericUser 3
1 first message 2020-07-10 10:37:00 1 genericUser 6
2 second message 2020-07-10 10:37:00 1 genericUser 7
3 third message 2020-07-10 10:37:00 1 genericUser 8
参见 db<>fiddle 中的 运行 示例。