为什么这个查询只返回非空子表的结果?

发布于 2024-07-19 11:26:44 字数 1316 浏览 5 评论 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 主表是资产管理系统中的一个会话表,记录了客户端查找的资产。 有不同类型的查找,每种类型都有一个单独的子表,此外还有一个用于可搜索会话的键/值对的属性子表。

This is a simplified version of a query we are running where we need to find all rows in the main parent table where the child rows match. The query below returns no results when one of the child tables is empty.

The main table has two child tables:

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');

There are no rows in child2 and the following query returns no rows when it is empty:

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';

If a row is added to child2, even if it doesn't match the WHERE clause, then the SELECT does return the row in the main table.

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

I've tested this on Derby and SQLite, so this looks to be something general with databases.

Why is this behaving this way?

What can I do to fix it?

I could change to UNION separate SELECTs, but that's much more verbose, and plus, we're generating the SQL dynamically and I'd rather not have to change our code.

Another fix is just to add a dumb row to the database, but that's messy.

PS The main table is a session table in an asset management system that records the assets that clients look up. There are different types of lookups and each kind gets a separate child table, plus there is an attributes child table for key/value pairs for the session that can be searched on.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

半透明的墙 2024-07-26 11:26:44

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

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

WHERE
  child1.name = 'child1' OR
  child2.name = 'DOES NOT EXIST';

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

解决这个问题:

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

SELECT
  main.*
FROM
  main
LEFT OUTER JOIN
  child1
ON
  main.id = child1.main_id
  AND child1.name = 'child1'
LEFT OUTER JOIN
  child2
ON
  main.id = child2.main_id
  AND child2.name = 'whatever'
  • 外连接意味着即使一个表为空,您也有机会获得结果。

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

When child2 has no rows, the query returns no rows because of the inner join to the child2 table. If you inner join to a table that has no rows, you will never get any results - you would have to outer join to child2 instead if you want to get results when child2 is empty.

When child2 does have a row, the reason your query returns results is because of the where clause:

WHERE
  child1.name = 'child1' OR
  child2.name = 'DOES NOT EXIST';

The inner join says there has to be something in child2 with a matching ID, but the where clause has an OR in it, so you will get results just because child1.name = 'child1'. After that, the database doesn't have to bother looking at the child2 tables.

To fix it:

I have hunch that you only want to return the child rows when some condition is met. You should outer-join to both of them, and perhaps also move your extra conditions from the where clause to the join clause, like this:

SELECT
  main.*
FROM
  main
LEFT OUTER JOIN
  child1
ON
  main.id = child1.main_id
  AND child1.name = 'child1'
LEFT OUTER JOIN
  child2
ON
  main.id = child2.main_id
  AND child2.name = 'whatever'
  • The outer joins mean you have the chance of getting results even if one table is empty.

  • Moving the extra conditions (child1.name = ...) from the WHERE clause to the outer join means you only get the tables info if the condition is true. (I think this might be what you are trying to do, but maybe not, in which case leave the conditions in the WHERE clause where you originally had them.)

木緿 2024-07-26 11:26:44

它不返回任何内容,因为您正在使用内部联接。

将内连接更改为左连接

It's returning nothing because you are using inner joins.

Change your inner joins to left joins

浅笑轻吟梦一曲 2024-07-26 11:26:44

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

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

这是标准行为,对于不熟悉 SQL 的人来说也是一个非常常见的问题。 Wikipedia 包含所有详细信息,否则快速 Google 搜索 会带来大量结果。

When you say INNER JOIN you are asking the query to return rows that have results on both sides of the join. This means any rows that do not have matching child rows will be removed.

It sounds like what you are looking for is LEFT JOIN which will include all rows on the left hand side of the join (main) even if they do not have a matching entry on the right hand side (child1, child2).

This is standard behaviour and a very common problem for people not familiar with SQL. Wikipedia has all the details, otherwise a quick Google search brings up plenty of results.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文