MySQL离开了连接过滤器

时间:2013-04-08 21:55:29

标签: mysql

我有两个相关的表,如下所示,我需要JOIN来获取所有新的和更新的记录。这些是具有简化表结构的代表性示例。

第一个表是一个项目表,例如'注释'包含所有现有注释以及“新”注释和现有注释的更新。

第二个表包含注释的挂起更改/更新的详细信息。 例如,如果已修改注释,则使用修改克隆该行并且a row被添加到pendingUpdates表中,其中包含原始注释的id,包含更新的行的id以及进行更新的用户。如果添加了新注释,则工作流程类似,期望原始id和修改后的id引用同一行。

Notes:
+----+---------------------+----------------------------+
| id | title               | text                       | 
+----+---------------------+----------------------------+
|  1 | this is a test note | blah blah blah             | 
|  2 | note 2              | sdfsadfasdf                | 
|  3 | note 3              | jklhjklhjklhjk             |
|  4 | note 3              | This is an update to note3 |
|  5 | note 3              | another update             | 
|  6 | note 4              | new note                   | 
+----+---------------------+----------------------------+

pendingUpdates:
+----+------------+---------+------+
| id | originalId | cloneId | user |
+----+------------+---------+------+
|  1 |          3 |       4 |    1 |
|  2 |          3 |       5 |    1 |
|  3 |          6 |       6 |    2 |
+----+------------+---------+------+

我想要做的是运行一个SELECT来获取所有Notes并包括该笔记是否有更改(包含更新列表)和任何新笔记。

因此,上面的示例将返回第1,2,3行和第6行。第3行也会将4,5列为更新。

期望结果的示例:

+----+------------+-------------+------+--------+---------------------+
| id | hasChanges | isNewRecord | p_id | clones | title               |
+----+------------+-------------+------+--------+---------------------+
|  1 | no         | no          | NULL |  NULL  | this is a test note |
|  2 | no         | no          | NULL |  NULL  | note 2              |
|  3 | yes        | no          |    1 |  4,5   | note 3              |
|  6 | yes        | yes         |    3 |  6     | note 4              |
+----+------------+-------------+------+--------+---------------------+

我尝试了很多不同的查询组合,但还没有得到我需要的内容。 这是我所拥有的非常接近,但包括第4行和第4行。我不想要的结果集中有5个。

SELECT 
    o.id,
    if(o.id = p.originalId, 'yes', 'no') AS hasChanges,
    if(p.cloneId = p.originalId,
        'yes',
        'no') AS isNewRecord,
    p.id AS p_id,
    group_concat(p.cloneId
        separator ',') AS clones,
    o.title
FROM
    `notes` AS o
        LEFT JOIN
    `pendingUpdates` AS p ON (o.id = p.originalId)
group by id;

返回:

   +----+------------+-------------+------+--------+---------------------+
   | id | hasChanges | isNewRecord | p_id | clones | title               |
   +----+------------+-------------+------+--------+---------------------+
   |  1 | no         | no          | NULL |  NULL  | this is a test note |
   |  2 | no         | no          | NULL |  NULL  | note 2              |
   |  3 | yes        | no          |    1 |  4,5   | note 3              |
 ->|  4 | no         | no          | NULL |  NULL  | note 3              |
 ->|  5 | no         | no          | NULL |  NULL  | note 3              |
   |  6 | yes        | yes         |    3 |  6     | note 4              |
   +----+------------+-------------+------+--------+---------------------+

解决方案:

这是我最终使用的最终解决方案,它与fthiella的最相似。

再次感谢所有为此提供帮助的人。

SELECT 
    m.id,
    if(m.max_pending IS NOT NULL, 'yes', 'no') hasChanges,
    if(m.id = pendingUpdates.cloneId, 'yes', 'no') isNew,
    m.clones,
    m.pendingIds,
    m.title
FROM (SELECT 
           Notes.id,
           MAX(pendingUpdates.id) max_pending,
          GROUP_CONCAT(pendingUpdates.id) pendingIds,
          GROUP_CONCAT(pendingUpdates.cloneId) clones,
          MAX(title) title
      FROM  Notes
      LEFT JOIN pendingUpdates ON Notes.id = pendingUpdates.originalId
      GROUP BY Notes.id
      ) m
          LEFT JOIN pendingUpdates ON m.max_pending = pendingUpdates.id
WHERE
    m.id NOT IN (SELECT 
                     cloneId
                 FROM pendingUpdates
                 WHERE cloneId NOT IN (SELECT 
                                          originalId
                                         FROM pendingUpdates)
                )
order by id;

http://sqlfiddle.com/#!2/8566e/1/0

2 个答案:

答案 0 :(得分:1)

你可以寻找这个

   SELECT 
o.id,
if(o.id = p.originalId , 'yes', 'no') AS hasChanges,
if(p.cloneId = p.originalId,
    'yes',
    'no') AS isNewRecord,
p.id AS p_id,
group_concat(p.cloneId
    separator ',') AS clones,
o.title
FROM
`notes` AS o
    LEFT JOIN
`pendingUpdates` AS p ON (o.id = p.originalId)
where o.id in (select originalId from pendingUpdates)
or o.id in (select id from pendingUpdates)
group by id;

DEMO HERE

答案 1 :(得分:0)

我已经以这种方式重写了您的查询(您的原始查询使用了一些非聚合列,并且在某些情况下这些列的值可能未确定):

SELECT
  m.id,
  CASE WHEN m.max_pending IS NOT NULL
       THEN 'yes' ELSE 'no' END hasChanges,
  CASE WHEN m.id=pendingUpdates.cloneId
       THEN 'yes' ELSE 'no' END isNew,
  m.min_pending pid,
  m.clones,
  m.title
FROM (
  SELECT
    Notes.id,
    MAX(pendingUpdates.id) max_pending,
    MIN(pendingUpdates.id) min_pending,
    GROUP_CONCAT(pendingUpdates.cloneId) clones,
    MAX(title) title
  FROM
    Notes LEFT JOIN pendingUpdates
    ON Notes.id = pendingUpdates.originalId
  GROUP BY
    Notes.id) m LEFT JOIN pendingUpdates
  ON m.max_pending = pendingUpdates.id
WHERE
  m.id NOT IN (SELECT cloneId FROM pendingUpdates
               WHERE cloneId NOT IN (SELECT originalId FROM pendingUpdates))

请参阅小提琴here