在UNION语句之后对分组行使用WHERE

时间:2014-07-04 10:49:17

标签: mysql

我有一个包含两个表,song和edited_song的数据库模式。除了edit_song中名为deleted的一个额外列之外,这些表是相同的。 edited_song-table包含对歌曲表中id的引用。我想找到所有没有被删除的歌曲。

我有一个UNION语句,其中I GROUP是两个SELECT语句的结果的id。我想排除已删除列的值为1的结果。可以在此处看到设置示例。

CREATE TABLE if not exists song
(
id int(11) NOT NULL auto_increment ,
title varchar(255),
  PRIMARY KEY  (id)
);
CREATE TABLE if not exists editedsong
(
id int(11) NOT NULL auto_increment ,
  title varchar(255),
  deleted tinyint(1),
  PRIMARY KEY  (id)
);

INSERT INTO song (id, title) VALUES
(1, 'Born in the USA');


INSERT INTO editedsong (id, title, deleted) VALUES
(1, 'Born in the USA', 1);

查询在这里:

SELECT * FROM
  ((SELECT *, 0 AS deleted FROM song WHERE id=1)
UNION
  (SELECT * FROM editedsong WHERE id=1)) AS song
WHERE song.deleted!=1
GROUP BY song.id

使用UNION语句而不是连接,因为这两个表中有大量文本,并且连接导致写入磁盘。这是真实查询的简化形式,但它再现了我遇到的问题。我希望查询不会产生任何结果,因为GROUP BY应保留第一行并丢弃所有后续内容。为什么不这样做?是因为WHERE在GROUP BY之前执行了吗?如果是,那么克服这个问题的好方法是什么?

http://sqlfiddle.com/#!2/5cdb6c/3

1 个答案:

答案 0 :(得分:0)

SQLFiddle中的代码不起作用的原因是WHERE子句在GROUP BY执行之前从editsong中排除了已删除的记录。

您可以使用HAVINGGROUP BY子句后应用条件。

这似乎有效:

SELECT *, max(deleted) as md FROM
  ((SELECT *, 0 AS deleted FROM song)
UNION
  (SELECT * FROM editedsong)) AS song
-- WHERE song.deleted!=1
GROUP BY song.id
HAVING md != 1

这将返回song的记录,而不是editedsong的记录,用于尚未删除的记录。如果您想要另一个,请颠倒UNION子句中项目的顺序。

GROUP BY的这种语法很不寻常,我很惊讶它得到了支持。我使用的大多数数据库系统都要求输出中的每个字段指定一些处理(MAXCOUNTGROUP BY等)。因此SELECT *GROUP BY不兼容。 MySQL必须在这里做出一些假设或者有一些默认行为,但我认为大多数服务器都不喜欢它(我也是)。