0

这是我们正在运行的查询的简化版本,我们需要在主父表中查找子行匹配的所有行。当子表之一为空时,以下查询不返回任何结果。

主表有两个子表:

CREATE TABLE main (id INT PRIMARY KEY, name VARCHAR(8));

CREATE TABLE child1(id INT PRIMARY KEY, main_id int, name VARCHAR(8));
ALTER TABLE child1 add constraint fk_child1_main foreign key (main_id) references main (id);

CREATE TABLE child2(id INT PRIMARY KEY, main_id int, name VARCHAR(8));
ALTER TABLE child2 add constraint fk_child2_main foreign key (main_id) references main (id);

INSERT INTO main (id, name) VALUES (1, 'main');
INSERT INTO child1 (id, main_id, name) VALUES (2, 1, 'child1');

child2 中没有行,当它为空时,以下查询不返回任何行:

SELECT
  main.*
FROM
  main
INNER JOIN
  child1
ON
  main.id = child1.main_id
INNER JOIN
  child2
ON
  main.id = child2.main_id
WHERE
  child1.name = 'child1' OR
  child2.name = 'DOES NOT EXIST';

如果将一行添加到 child2,即使它与 WHERE 子句不匹配,SELECT 也会返回主表中的行。

INSERT INTO child2 (id, main_id, name) VALUES (4, 1, 'child2');

我已经在 Derby 和 SQLite 上对此进行了测试,所以这看起来与数据库通用。

为什么会这样?

我能做些什么来修复它?

我可以更改为 UNION 单独的 SELECT,但这更冗长,而且,我们正在动态生成 SQL,我宁愿不必更改我们的代码。

另一个解决方法是向数据库添加一个愚蠢的行,但这很混乱。

PS 主表是资产管理系统中的一个会话表,记录客户查找的资产。有不同类型的查找,每种类型都有一个单独的子表,此外还有一个属性子表,用于可以搜索的会话的键/值对。

4

3 回答 3

4

当 child2 没有行时,由于对 child2 表的内部连接,查询不会返回任何行。如果您对没有行的表进行内部联接,则永远不会得到任何结果 - 如果您想在 child2 为空时获得结果,则必须外部联接到 child2。

当 child2 确实有一行时,查询返回结果的原因是由于 where 子句:

在哪里
  child1.name = 'child1' 或
  child2.name = '不存在';

内连接表示 child2 中必须有具有匹配 ID 的内容,但 where 子句中有一个 OR,因此您将得到结果,因为 child1.name = 'child1'。之后,数据库就不必费心查看 child2 表了。

要解决这个问题:

我有预感,您只想在满足某些条件时返回子行。您应该对它们都进行外连接,并且也许还将您的额外条件从 where 子句移动到 join 子句,如下所示:

选择
  主要的。*
从
  主要的
左外连接
  孩子1
在
  main.id = child1.main_id
  AND child1.name = 'child1'
左外连接
  孩子2
在
  main.id = child2.main_id
  AND child2.name = '随便'
  • 外连接意味着即使一张表是空的,您也有机会获得结果。

  • 将额外条件 (child1.name = ...) 从 WHERE 子句移到外连接意味着您只有在条件为真时才能获得表信息。(我认为这可能是您想要做的,但可能不是,在这种情况下,将条件留在您最初拥有它们的 WHERE 子句中。)

于 2009-05-09T08:15:19.550 回答
2

它没有返回任何内容,因为您使用的是内部连接。

将内连接更改为左连接

于 2009-05-09T08:18:00.370 回答
2

当您说 INNER JOIN 时,您是在要求查询返回在连接两侧都有结果的行。这意味着任何没有匹配子行的行都将被删除。

听起来您正在寻找的是 LEFT JOIN ,它将包括连接(主)左侧的所有行,即使它们在右侧(child1,child2)没有匹配的条目。

这是标准行为,对于不熟悉 SQL 的人来说是一个非常常见的问题。 维基百科有所有的细节,否则一个快速的谷歌搜索会带来很多结果。

于 2009-05-09T08:18:00.853 回答