MySQL JOIN 查询产生错误的结果

Saj*_*v C 5 mysql join

我有两个表complaintscomplaints_reply在我的MySQl数据库中。用户可以添加存储在complaints投诉回复中的投诉存储在complaints_reply表中。我试图在特定条件下加入这两个表内容。在我提到我想要得到什么以及我面临的问题之前,我将首先解释这两个表的结构。


注意添加投诉的人是投诉所有者,添加投诉回复的人是投诉回复者。投诉所有者还可以添加回复。因此,他既可以是投诉所有者,也可以是投诉回复者。这两个表是一对多的关系。一个投诉可以有多个投诉回复。 member_idcomplaint表代表投诉所有者mem_idcomplaints_reply代表投诉回答者


期望的输出

连接两个表并获取值并将投诉和投诉的回复显示为单个结果集。但条件有点棘手。表中最后添加的投诉回复complaints_reply应以complaints投诉所有者不应成为投诉回复者的方式为表格中的投诉获取。我使用posted_date& posted_timefrom complaints_replytable 来获取最后添加的投诉回复,并且投诉回复必须显示在结果集中。

因此,从表现在包含的示例数据中,我应该得到的输出是:

+------+---------+----------+-------------+-------------------+
| id   | title   |member_id |last_replier |last_posted_dt     |
+------+---------+----------+-------------+-------------------+
|    1 | x       | 1000     |2002         | 2015-05-2610:11:17|
|    2 | y       | 1001     |1000         | 2015-05-2710:06:16|
+------+---------+----------+-------------+-------------------+
Run Code Online (Sandbox Code Playgroud)

但我得到的是:

+------+---------+----------+-------------+-------------------+
| id   | title   |member_id |last_replier |last_posted_dt     |
+------+---------+----------+-------------+-------------------+
|    1 | x       | 1000     |1001         | 2015-05-2610:11:17|
|    2 | y       | 1001     |2000         | 2015-05-2710:06:16|
+------+---------+----------+-------------+-------------------+
Run Code Online (Sandbox Code Playgroud)

日期是正确的,但是返回的投诉回复last_replier是错误的。

这是我的查询

SELECT com.id,
       com.title,
       com.member_id,
       last_comp_reply.last_replier,
       last_comp_reply.last_posted_dt
FROM complaints com
LEFT JOIN
  (SELECT c.id AS complaint_id,
          c.member_id AS parent_mem_id,
          cr.mem_id AS last_replier,
          max(cr.posted_dt) AS last_posted_dt
   FROM
     (SELECT cr.complaint_id,cr.mem_id,c.id,c.member_id,(CONCAT(cr.posted_date,cr.posted_time)) AS posted_dt
      FROM complaints_reply cr,
           complaints c
      WHERE cr.complaint_id=c.id
        AND cr.mem_id!=c.member_id
      GROUP BY cr.complaint_id,
               cr.mem_id,
               posted_dt)cr,
        complaints c
   WHERE cr.complaint_id=c.id
   GROUP BY cr.complaint_id,
            c.id,
            c.member_id) AS last_comp_reply ON com.id=last_comp_reply.complaint_id
Run Code Online (Sandbox Code Playgroud)

表的表结构 complaints

CREATE TABLE IF NOT EXISTS `complaints` (
  `id` int(11) NOT NULL,
  `title` varchar(500) NOT NULL,
  `member_id` int(11) NOT NULL,
  `posted_date` date NOT NULL,
  `posted_time` time NOT NULL 
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
Run Code Online (Sandbox Code Playgroud)

表的索引 complaints

ALTER TABLE `complaints`
 ADD PRIMARY KEY (`id`);
Run Code Online (Sandbox Code Playgroud)

表的 AUTO_INCREMENT complaints

ALTER TABLE `complaints`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=3;
Run Code Online (Sandbox Code Playgroud)

为表转储数据 complaints

INSERT INTO `complaints` (`id`, `title`, `member_id`, `posted_date`, `posted_time`) VALUES
(1, 'x', 1000, '2015-05-05', '02:06:15'),
(2, 'y', 1001, '2015-05-14', '02:08:10');
Run Code Online (Sandbox Code Playgroud)

表的表结构 complaints_reply

CREATE TABLE IF NOT EXISTS `complaints_reply` (
`id` int(11) NOT NULL,
  `complaint_id` int(11) NOT NULL,
  `comments` text NOT NULL,
  `mem_id` int(11) NOT NULL,
  `posted_date` date NOT NULL,
  `posted_time` time NOT NULL
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=10 ;
Run Code Online (Sandbox Code Playgroud)

表的索引 complaints_reply

ALTER TABLE `complaints_reply`
 ADD PRIMARY KEY (`id`);
Run Code Online (Sandbox Code Playgroud)

表的 AUTO_INCREMENT complaints_reply

ALTER TABLE `complaints_reply`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=10;
Run Code Online (Sandbox Code Playgroud)

为表转储数据 complaints_reply

INSERT INTO `complaints_reply` (`id`, `complaint_id`, `comments`, `mem_id`, `posted_date`, `posted_time`) VALUES
(1, 1, 'reply1', 2000, '2015-05-08', '02:07:08'),
(2, 1, 'reply2', 2001, '2015-05-06', '06:05:08'),
(3, 1, 'reply3', 1000, '2015-05-14', '02:12:13'),
(4, 2, 'hola', 1000, '2015-05-27', '10:06:16'),
(5, 2, 'hello', 2000, '2015-05-04', '03:09:09'),
(6, 2, 'gracias', 1001, '2015-05-31', '06:12:18'),
(7, 1, 'reply4', 1001, '2015-01-04', '04:08:12'),
(8, 2, 'puta', 1001, '2015-06-13', '06:12:18'),
(9, 1, 'reply5', 1000, '2015-06-01', '04:08:12'),
(10, 1, 'reply next', 2002, '2015-05-26', '10:11:17');
P.S.
Run Code Online (Sandbox Code Playgroud)

为了了解我的查询的全部内容,我将解释用于组合表并根据条件给出结果的子查询:投诉所有者不应是投诉回复者是:

SELECT cr.complaint_id,
       cr.mem_id,
       c.id,
       c.member_id,
       (CONCAT(cr.posted_date,cr.posted_time)) AS posted_dt
FROM complaints_reply cr,
     complaints c
WHERE cr.complaint_id=c.id
  AND cr.mem_id!=c.member_id
GROUP BY cr.complaint_id,
         cr.mem_id,
         posted_dt
Run Code Online (Sandbox Code Playgroud)

结果是:

+--------------+---------+----------+-------------+-------------------+
| complaint_id | mem_id  | id       |member_id    |     posted_dt     |
+--------------+---------+-------   +-------------+-------------------+
|    1         | 1001    | 1        |1000         | 2015-01-0404:08:12|
|    1         | 2000    | 1        |1000         | 2015-05-0802:07:08|
|    1         | 2001    | 1        |1000         | 2015-05-0606:05:08|
|    1         | 2002    | 1        |1000         | 2015-05-2610:11:17|
|    2         | 1000    | 2        |1001         | 2015-05-2710:06:16|
|    2         | 2000    | 2        |1001         | 2015-05-0403:09:09|
+--------------+---------+----------+-------------+-------------------+
Run Code Online (Sandbox Code Playgroud)

member_id这里代表投诉所有者,mem_id代表投诉回复者

内部查询根据条件给出结果,然后在此之后的一切都变得乱七八糟。我不知道我哪里出错了。此表中未提取投诉所有者添加的投诉回复。到现在为止还挺好。有没有其他方法可以从这里得到结果?

ype*_*eᵀᴹ 5

问题在于子查询,您在选择列表中组合了表中的聚合 ( max(cr.posted_dt)) 和非聚合表达式/列。

MySQL 允许您运行这种不一致的查询 - 使用默认设置 - 这基本上意味着它取决于开发人员编写一致的查询,而不是自己动手。您可以将 sql 模式更改为ONLY_FULL_GROUP_BY并查看尝试运行查询时会发生什么。

现在要解决这个问题,您似乎想要一种[greatest-n-per-group]查询。有几种方法可以做到这一点,在 MySQL 中都非常复杂(因为它缺少窗口函数)。检查此站点SO 主站点中的相关标签。

这是一种方法:

SELECT c.id,
       c.title,
       c.member_id,
       cr.mem_id    AS last_replier,
       CONCAT(cr.posted_date, 'T', cr.posted_time) AS last_posted_dt
FROM complaints AS c
  LEFT JOIN complaints_reply AS cr
    ON  cr.id = 
        ( SELECT crl.id
          FROM complaints_reply AS crl
          WHERE crl.complaint_id = c.id
            AND crl.mem_id <> c.member_id
          ORDER BY posted_date DESC, posted_time DESC
          LIMIT 1
        ) ;
Run Code Online (Sandbox Code Playgroud)

如果您在上添加索引,它将为您提供一致的结果并且非常有效 (complaint_id, posted_date, posted_time, mem_id)

SQLfiddle测试。

  • mysql 确实需要在这方面发挥作用。 (3认同)