【问题标题】:How to combine two SQL queries in MySQL with different columns without combining their resulting rows如何将 MySQL 中的两个 SQL 查询与不同的列组合在一起而不组合它们的结果行
【发布时间】:2020-07-07 04:21:10
【问题描述】:

上下文

我正在尝试在我的网站上创建一个“提要”系统,用户可以在其中访问他们的提要并查看他们在网站上可以执行的不同操作的所有新通知。例如,在“feed”部分,用户可以查看他们关注的用户是否创建了文章,以及用户是否对文章发表了评论。我当前的提要系统仅使用两个单独的查询来获取此信息。但是,我想将这两个查询合并为一个,以便用户可以按时间顺序查看他们关注的人的活动。我的系统现在的工作方式是,我从用户关注的每个人那里获取五篇文章并将其放在“提要”部分,然后获取五篇文章 cmets 并将其发布在“提要”部分的同一区域中。我不想将查询分开,而是将它们组合起来,这样,他们将看到按时间顺序发生的提要帖子,而不是连续看到五个文章帖子,然后连续看到五个文章 cmets,而不管其他用户是否创建首先是一篇文章,然后发表评论,然后创建另一篇文章,或者以任何顺序,而不是总是看到相同的通知顺序。

问题

首先,如果您想重新创建表格,让我向您展示我的表格创建代码。首先要做的是创建一个users 表,我的articlesarticlecomments 表引用了它:

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 表:

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 表:

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;

为了在本示例中充分填充表格,我们将使用以下语句:

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

我用来从articlesarticlecomments 表中获取数据的两个查询如下:

查询 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.datearticlecomments.date)对它们进行排序?我希望它们在不同的行中,而不是在同一行中。因此,应该就像我分别查询它们并将结果行简单地组合在一起一样。如果有 3 篇文章和 3 篇文章 cmets,我希望总共返回 6 行。

这就是我想要的样子。鉴于有三篇文章和三篇文章 cmets,并且 cmets 是在文章之后创建的,这就是结合上述查询后的结果应该是什么样子(我不确定在不同的列名下是否可以这样描述,但我'想知道是否可以完成类似的事情):

+-------------------------------+-------------------+---------------------+----------------------------------------------------------------+---------+-------------+
| 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 |
+-------------------------------+-------------------+---------------------+----------------------------------------------------------------+---------+-------------+

我尝试过的事情

我读到这可能涉及JOINUNION 运算符,但我不确定如何在这种情况下实现它们。我确实尝试通过简单地使用(Query 1) UNION (Query 2) 来组合这两个查询,它起初告诉我,我的两个查询中的列数不同,所以我不得不从我的articlecomments 查询中删除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

【问题讨论】:

  • 首先,您需要想象这种“组合操作”的最终结果应该是什么样子 - 并记录该愿景(只需准备一份规范)。您正在尝试组合两个查询,但您不太确定要获得什么。我投票结束这个问题。
  • 从您的大部分描述来看,UNION 应该是正确的答案,因此如果您可以在问题中添加一些示例数据和预期结果,这将有助于极大地很清楚你们都想以何种方式“组合查询”并拥有“单独的行”,这听起来像是相互矛盾的。
  • 请在代码问题中给出minimal reproducible example--cut & paste & runnable code,包括最小的代表性示例输入作为代码;期望和实际输出(包括逐字错误消息);标签和版本;明确的规范和解释。对于包含 DBMS 和 DDL(包括约束和索引)和输入为格式化为表的代码的 SQL。 How to Ask 在给出业务关系(船舶)/关联或表(基本或查询结果)时,说明其中的一行根据其列值说明了业务情况。当卡住时,您可以编写代码并说出他们的工作并解释卡住的情况。
  • 感谢您的反馈。我只是把它变成了一个可重复的答案。你能再看看吗?
  • “在给出业务关系(船舶)/关联或表(基本或查询结果)时,说明其中的一行根据其列值说明了业务情况。”我在这里有一行值,(id、tm、date、article_id、idUsers、uidUsers)& 我有一些表用户、文章和 articlecmets 的值。我的行在什么条件下进入结果? PS问1个问题。不要写关于你想知道的事情的其他问题,因为这样就不清楚你的帖子在问什么。如果您想问其他(特定研究的非重复)问题,请发布另一个帖子。

标签: sql join union mariadb-10.4


【解决方案1】:

这似乎是 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 |标题信息 |创建 | article_id | uid 用户 -----------------: | :------------- | :----------------- | ---------: | :---------- 1 |第一篇 | 2020-07-09 05:59:18 | 1 |通用用户 2 |第二篇 | 2020-07-09 05:59:18 | 1 |通用用户 3 |第三篇 | 2020-07-09 05:59:18 | 1 |通用用户 1 |第一条消息 | 2020-07-09 05:59:18 | 1 |通用用户 2 |第二条消息 | 2020-07-09 05:59:18 | 1 |通用用户 3 |第三条消息 | 2020-07-09 05:59:18 | 1 |通用用户

说明

由于我们要使用order bylimit,我们将从第一行创建一个子查询并从第一个子查询中选择所有列。我们将在输出中按照我们想要的方式命名每个字段。

我们对第二个查询做同样的事情,并在它们之间添加一个union all 子句。然后,我们根据创建日期(这是第一个查询中的别名)应用排序,以按照您想要的顺序获得您想要的结果。

如果您使用union,则会从结果中消除重复行。如果您使用union all,将保留重复的行(如果存在)。 union all 更快,因为它结合了 2 个数据集(只要查询中的列相同。union 还必须查找重复的行并将它们从查询中删除。

【讨论】:

  • 问题并没有说是 MySQL。您不应该编辑它来添加该标签。您应该对问题发表评论以进行澄清。
  • 感谢您的回复。我今晚去看看。
  • 我对您的回复进行了一些编辑,以使其适合我正在寻找的内容。我所做的其中一件事是添加一个名为“contenttype”的行,并指定内容类型是文章还是文章评论。这是在使用 UNION 语句时区分内容类型的有效方法吗?
  • 嗨@CarsonD,我需要看一些你对 contenttype 的想法的例子来回答你的问题。根据您的问题,您所说的似乎是区分文章和评论之间内容类型的有效方法。
【解决方案2】:

您没有提及您使用的 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 中的运行示例。

【讨论】:

  • 感谢您的回复。使用ROW_NUMBER() 会做什么?它只是作为我可能想要的特定行的标识符的东西吗?另外,我想要的顺序只是文章/cmets 创建日期的降序,如果这就是你的意思的话。 article_id 列在那里是因为我需要我网站的“提要”部分中的信息,这样当用户点击包含文章评论的帖子时,它会将用户带到相应的文章,我需要 @ 987654329@定位。
  • @CarsonD 查看更新后的查询。是的,根据文章/评论日期,人为生成行号以在联合之后以正确的顺序对行进行排序。我添加了 article_id。
【解决方案3】:

你可以像这样交叉加入=

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'

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-01-09
    • 1970-01-01
    • 2021-10-09
    • 2022-01-16
    • 2021-09-08
    • 2020-03-25
    • 2021-07-02
    • 1970-01-01
    相关资源
    最近更新 更多