alx*_*ndr 5 mysql sql database rdbms join
我几乎在寻找一种方法来SELECT根据另一个表的行中的某些值来过滤掉一个表中的行.
我正在尝试下面的示例结构.我有一个博客文章内容表(每篇博文一行),另一个关于帖子的元数据表(每个键值对一行;每行有一个列与博客文章关联;每行多行博客文章).我想posts只在没有行的metadata地方拉一行metadata.pid=posts.pid AND metadata.k='optout'.也就是说,对于下面的示例结构,我只想回到posts.id=1行.
(基于我已经尝试过的)JOINs不会最终删除具有一些元数据的帖子metadata.k='optout',因为另一行元数据pid意味着它将其纳入结果.
mysql> select * from posts;
+-----+-------+--------------+
| pid | title | content |
+-----+-------+--------------+
| 1 | Foo | Some content |
| 2 | Bar | More content |
| 3 | Baz | Something |
+-----+-------+--------------+
3 rows in set (0.00 sec)
mysql> select * from metadata;
+------+-----+--------+-----------+
| mdid | pid | k | v |
+------+-----+--------+-----------+
| 1 | 1 | date | yesterday |
| 2 | 1 | thumb | img.jpg |
| 3 | 2 | date | today |
| 4 | 2 | optout | true |
| 5 | 3 | date | tomorrow |
| 6 | 3 | optout | true |
+------+-----+--------+-----------+
6 rows in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
子查询可以给我与我想要的相反:
mysql> select posts.* from posts where pid = any (select pid from metadata where k = 'optout');
+-----+-------+--------------+
| pid | title | content |
+-----+-------+--------------+
| 2 | Bar | More content |
| 3 | Baz | Something |
+-----+-------+--------------+
2 rows in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
...但是使用pid != any (...)给了我帖子中的所有3行,因为每个人pid都有一个元数据行k!='optout'.
听起来你想要做一个LEFT JOIN然后检查连接表的值的结果NULL,表明没有这样的连接记录存在.
例如:
SELECT * FROM posts
LEFT JOIN metadata ON (posts.pid = metadata.pid AND metadata.k = 'optout')
WHERE metadata.mdid IS NULL;
Run Code Online (Sandbox Code Playgroud)
这将从表中选择任何posts没有对应metadata行且值为的行k = 'optout'.
编辑:值得注意的是,这是左连接的关键属性,不适用于常规连接; 即使连接表中不存在匹配值,左连接也将始终返回第一个表中的值,从而允许您根据缺少这些行执行选择.
编辑2:让我们澄清一下这里发生了什么相LEFT JOIN对于JOIN(我称之为INNER JOIN为了清晰但在MySQL中可互换).
假设您运行以下两个查询之一:
SELECT posts.*, metadata.mdid, metadata.k, metadata.v
FROM posts
INNER JOIN metadata ON posts.pid = metadata.pid;
Run Code Online (Sandbox Code Playgroud)
要么
SELECT posts.*, metadata.mdid, metadata.k, metadata.v
FROM posts
LEFT JOIN metadata ON posts.pid = metadata.pid;
Run Code Online (Sandbox Code Playgroud)
两个查询都生成以下结果集:
+-----+-------+--------------+------+-------+-----------+
| pid | title | content | mdid | k | v |
+-----+-------+--------------+------+-------+-----------+
| 1 | Foo | Some content | 1 | date | yesterday |
| 1 | Foo | Some content | 2 | thumb | img.jpg |
+-----+-------+--------------+------+-------+-----------+
Run Code Online (Sandbox Code Playgroud)
现在,让我们假设我们修改查询以添加提到的"optout"的额外条件.一,INNER JOIN:
SELECT posts.*, metadata.mdid, metadata.k, metadata.v
FROM posts
INNER JOIN metadata ON (posts.pid = metadata.pid AND metadata.k = "optout");
Run Code Online (Sandbox Code Playgroud)
正如所料,这不会返回任何结果:
Empty set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
现在,将其更改为LEFT JOIN:
SELECT posts.*, metadata.mdid, metadata.k, metadata.v
FROM posts
LEFT JOIN metadata ON (posts.pid = metadata.pid AND metadata.k = "optout");
Run Code Online (Sandbox Code Playgroud)
这会产生一个结果集:
+-----+-------+--------------+------+------+------+
| pid | title | content | mdid | k | v |
+-----+-------+--------------+------+------+------+
| 1 | Foo | Some content | NULL | NULL | NULL |
+-----+-------+--------------+------+------+------+
Run Code Online (Sandbox Code Playgroud)
a INNER JOIN和a 之间的区别在于LEFT JOIN,INNER JOIN只有来自BOTH连接表的行匹配才会返回结果.在a中LEFT JOIN,无论是否找到任何连接,都将始终返回第一个表中的匹配行.在很多情况下,使用哪一个并不重要,但选择正确的一个非常重要,这样才能获得意想不到的结果.
所以在这种情况下,建议查询:
SELECT posts.*, metadata.mdid, metadata.k, metadata.v
LEFT JOIN metadata ON (posts.pid = metadata.pid AND metadata.k = 'optout')
WHERE metadata.mdid IS NULL;
Run Code Online (Sandbox Code Playgroud)
将返回与上面相同的结果集:
+-----+-------+--------------+------+------+------+
| pid | title | content | mdid | k | v |
+-----+-------+--------------+------+------+------+
| 1 | Foo | Some content | NULL | NULL | NULL |
+-----+-------+--------------+------+------+------+
Run Code Online (Sandbox Code Playgroud)
希望清除它!加入是一个很好的东西要学习,完全理解何时使用哪一个是一件非常好的事情.