很抱歉这篇文章,直到现在我才意识到它已经这么长了。尽管您已经检查了答案,但我希望您阅读这篇文章能够深入了解为什么首选此解决方案以及它是如何从您的原始查询演变而来的。
第一件事
您的查询
$q = "SELECT title,name,company,address1,address2
FROM registrations
WHERE title != 0 AND id IN (
SELECT registrar_id
FROM registrations_industry
WHERE industry_id = '$industryid'
)";
看起来不错。IN
语法等效于多个匹配OR
项。例如
WHERE field_id IN (101,102,103,105)
在功能上等同于
WHERE (field_id = 101
OR field_id = 102
OR field_id = 103
OR field_id = 105)
您通过引入子查询使其复杂化一点,没问题。只要您的子查询返回一列(而您的也如此),将其传递给IN
就可以了。
在您的情况下,您正在registrations.id
比较registrations_industry.registrar_id
. (注意:这只是<table>.<field>
语法,没什么特别的,但有助于消除您的字段所在的表的歧义。)
这似乎很好。
发生什么了
SQL 将首先运行子查询,生成registrar_id
s 的结果集,其中 sindustry_id
按指定设置。
然后 SQL 将运行外部查询,将子查询替换为其结果,您将从与子查询返回的 s之一匹配的registrations
位置获取行。registrations.id
registrar_id
子查询有助于调试您的代码,因为您可以拉出子查询并单独运行它,确保其输出符合您的预期。
优化
虽然子查询有利于调试,但它们很慢,至少比使用优化JOIN
语句慢。
在这种情况下,您可以使用JOIN
.
首先,您将从基本完全相同的外部查询开始:
SELECT title,name,company,address1,address2
FROM registrations
WHERE title != 0 AND ...
但是您也对registrations_industry
表格中的数据感兴趣,因此您需要将其包括在内。给我们
SELECT title,name,company,address1,address2
FROM registrations, registrations_industry
WHERE title != 0 AND ...
我们需要修复......现在我们有了registrations_industry
表格,我们可以:
SELECT title,name,company,address1,address2
FROM registrations, registrations_industry
WHERE title != 0
AND id = registrar_id
AND industry_id = '$industryid'
现在,如果两个表都有一个id
列,则可能会出现问题——因为只是说id
是模棱两可的。我们可以通过使用<table>.<field>
语法来消除歧义。如在
SELECT registrations.title, registrations.name,
registrations.company, registrations.address1, registrations.address2
FROM registrations, registrations_industry
WHERE registrations.title != 0
AND registrations_industry.industry_id = '$industryid'
我们不必对所有字段引用都使用这种语法,但为了清楚起见,我们选择了这种语法。由于所有表名,查询现在变得不必要地复杂。我们可以缩短它们,同时仍然提供消歧和清晰度。我们通过创建表别名来做到这一点。
SELECT r.title, r.name, r.company, r.address1, r.address2
FROM registrations r, registrations_industry ri
WHERE r.title != 0
AND ri.industry_id = '$industryid'
通过将r
和放在子句ri
中的两个表之后FROM
,我们可以使用这些快捷方式来引用它们。这清理了查询,但仍然使我们能够清楚地指定字段来自哪些表。
旁注:我们可以通过包含可选的AS
eg而不仅仅是,但我通常保留字段别名。FROM registrations
AS
r
FROM registrations r
AS
如果您现在运行查询,您将获得所谓的“笛卡尔积”或 SQL 术语中的CROSS JOIN
. 这是因为我们没有定义这两个表之间的任何关系,而事实上,有一个。为了解决这个问题,我们需要重新引入丢失的原始查询的一部分:两个表之间的关系
r.id = ri.registrar_id
这样我们的查询现在看起来像
SELECT r.title, r.name, r.company, r.address1, r.address2
FROM registrations r, registrations_industry ri
WHERE r.title != 0
AND r.id = ri.registrar_id
AND ri.industry_id = '$industryid'
这应该可以完美地工作。
吹毛求疵——隐式与显式连接
但是我的挑剔者需要指出这被称为“隐式连接”。基本上你正在加入表格但不使用JOIN
语法。
一个更简单的隐式连接示例是
SELECT *
FROM foo f, bar b
WHERE f.id = b.foo_id
相应的显式语法是
SELECT *
FROM foo f
JOIN bar b ON f.id = b.foo_id
结果将是相同的,但它使用正确(和更清晰)的语法。foo
(它更清楚,因为它明确地统计了和表之间存在关系,bar
并且它由 定义f.id = b.foo_id
。)
我们可以类似地表达您的隐式查询
SELECT r.title, r.name, r.company, r.address1, r.address2
FROM registrations r, registrations_industry ri
WHERE r.title != 0
AND r.id = ri.registrar_id
AND ri.industry_id = '$industryid'
明确如下
SELECT r.title, r.name, r.company, r.address1, r.address2
FROM registrations r
JOIN registrations_industry ri ON r.id = ri.registrar_id
WHERE r.title != 0
AND ri.industry_id = '$industryid'
如您所见,表之间的关系现在在JOIN
子句中,因此WHERE
和后续的AND
andOR
子句可以自由表达任何限制。另一种看待这个问题的方法是,如果您取出WHERE + AND/OR
子句,表之间的关系仍然存在,结果仍然“有意义”,而如果您使用隐式方法并删除WHERE + AND/OR
子句,您的结果集将包含以下行误导。
最后,JOIN
语法本身将导致 in 中的行registrations
,但没有任何相应的行 inregistrations_industry
不被返回。
根据您的用例,您可能希望行 fromregistrations
出现在结果中,即使registrations_industry
. 为此,您将使用所谓的OUTER JOIN
. 在这种情况下,我们想要所谓的 a LEFT OUTER JOIN
,因为我们想要左侧表的所有行 ( registrations
)。我们可以选择用于RIGHT OUTER JOIN
正确的表或仅用于两个表OUTER JOIN
的外部连接。
因此我们的查询变成
SELECT r.title, r.name, r.company, r.address1, r.address2
FROM registrations r
LEFT OUTER JOIN registrations_industry ri ON r.id = ri.registrar_id
WHERE r.title != 0
AND ri.industry_id = '$industryid'
我们完成了。
最终结果是我们有一个查询是
- 运行时间更快
- 更紧凑/简洁
- 更明确地说明字段来自哪些表
- 更明确的表之间的关系