0

你能告诉我我的解释是否正确(最后和部分)吗?

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

下面真的是我不确定的地方:

... AND id IN (select registrar_id from registrations_industry where industry_id='$industryid')

解释: 从连接表registrations_industry中获取id(registrations id field)等于registrar_id(field)的任何匹配,其中industry_id等于集合$industryid

此 select 语句是否被视为子例程,因为它是主查询中的查询?

所以一个例子是注册表 id 搜索到 23 看起来像:

注册(表)

id=23,title=owner,name=mike,company=nono,address1=1234 s walker lane,address2

registrations_industry(表)

id=256, registrar_id=23, industry_id=400<br>
id=159, registrar_id=23, industry_id=284<br>
id=227, registrar_id=23, industry_id=357

我假设这将返回 3 条具有相同注册表数据的记录,当然还有不同的 registrations_industry 返回。

4

3 回答 3

2

对于给定的测试数据集,您的查询将返回一条记录。这一个: id=23,title=owner,name=mike,company=nono,address1=1234 s walker lane,address2
要获得三个具有相同注册表数据和不同registrations_industry 的记录,您需要使用JOIN.

像这样的东西:

SELECT r.title, r.name, r.company, r.address1, r.address2 
FROM registrations AS r
LEFT OUTER JOIN registrations_industry AS ri
ON ri.registrar_id=r.id
WHERE r.title!=0 AND ri.industry_id={$industry_id}
于 2012-06-26T22:04:10.123 回答
2

很抱歉这篇文章,直到现在我才意识到它已经这么长了。尽管您已经检查了答案,但我希望您阅读这篇文章能够深入了解为什么首选此解决方案以及它是如何从您的原始查询演变而来的。

第一件事

您的查询

$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_ids 的结果集,其中 sindustry_id按指定设置。

然后 SQL 将运行外部查询,将子查询替换为其结果,您将从与子查询返回的 s之一匹配的registrations位置获取行。registrations.idregistrar_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,我们可以使用这些快捷方式来引用它们。这清理了查询,但仍然使我们能够清楚地指定字段来自哪些表。

旁注:我们可以通过包含可选的ASeg而不仅仅是,但我通常保留字段别名。FROM registrationsASrFROM registrations rAS

如果您现在运行查询,您将获得所谓的“笛卡尔积”或 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和后续的ANDandOR子句可以自由表达任何限制。另一种看待这个问题的方法是,如果您取出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'

我们完成了。

最终结果是我们有一个查询是

  • 运行时间更快
  • 更紧凑/简洁
  • 更明确地说明字段来自哪些表
  • 更明确的表之间的关系
于 2012-06-26T22:41:07.733 回答
0

此查询的更简单版本是:

SELECT title,  name, company, address1, address2
  FROM registrations, registrations_industry
 WHERE title != 0
   AND id = registrar_id
   AND industry_id = '$industryid'

你的版本是一个子查询,这个版本是一个简单的连接。您对查询的假设通常是正确的,但 SQL 更难优化,并且对于任何试图阅读代码的人来说更难解开。此外,您将无法从该父 SELECT 语句中的 registrations_industry 表中提取数据,因为从技术上讲,它不是连接的,并且子表不是父查询的一部分。

于 2012-06-26T22:00:37.170 回答