有没有办法删除这种类型的 SQL SELECT 中的嵌套查询?

Posted

技术标签:

【中文标题】有没有办法删除这种类型的 SQL SELECT 中的嵌套查询?【英文标题】:Is there a way to remove the nested query in this type of SQL SELECT? 【发布时间】:2011-07-25 20:45:03 【问题描述】:

鉴于此表结构和示例数据(查询中不应使用t3,此处仅显示t1和t2之间的关系):

      t1                 t2                         t3
--------------   -----------------   --------------------------------
| id | value |   | t1key | t3key |   | id | value                   |
|  1 |  2008 |   |     3 |     1 |   |  1 | "New intel cpu in 2010" |
|  2 |  2009 |   |     4 |     1 |   |  2 | "New amd cpu in 2008"   |
|  3 |  2010 |   |     6 |     1 |   |    |                     ... |
|  4 | intel |   |     1 |     2 |   --------------------------------
|  5 |   amd |   |     5 |     2 |
|  6 |   cpu |   |     6 |     2 |
|    |   ... |   |       |   ... |
--------------   -----------------

您将如何构建满足以下条件的 SQL 查询:

Given the input for t1.id is the set 6 returns t1.id set 3,4,6,1,5
Given the input for t1.id is the set 6,4 returns t1.id set 3,4,6
Given the input for t1.id is the set 5,4 returns t1.id set 

当表格更大时不会影响性能...?

【问题讨论】:

我不明白规格。 “Specifying t1.id is the set ...”是什么意思? 让我们调用表 t1 words,调用表 t3 phrases 和调用表 t2 word is in phrase。我猜你想找到与一组特定的words.ids在同一个短语中的所有words.id。对吗? @Stefan:“指定 t1.id 是集合 6”的意思是“WHERE t1.id = 6”.. 随着集合的增长,它变得很棘手。 @ypercube:差不多,但所有重要的词都已从短语中删除并放入 t2 :) 所以你想要包含所有指定单词的短语中包含的所有单词。 【参考方案1】:

这是我出色的贡献(至少我们现在假设它很出色:)

SELECT DISTINCT a2.t1key, COUNT( * ) AS cnt
FROM t2 AS a1
    LEFT JOIN t2 AS a2 ON a2.t3key = a1.t3key
WHERE a1.t1key IN ( 6, 4 ) 
GROUP BY a2.t3key, a2.t1key
HAVING cnt >=2

IN (6,4) 部分确实不言自明。在cnt >=2 中,2 是IN 子句中id-s 的数量。例如:您使用的是IN (6),那么您应该使用cnt >=1

我不确定是否需要 >,但我懒得不创建更大的数据集进行测试:)

【讨论】:

【参考方案2】:

不是很清楚你想要什么。

我将调用表 t1 word,调用表 t3 phrase 并调用表 t2 word is in phrase

那么我猜你想找到与一组特定 word.ids 位于同一短语中的所有 word.ids。对吗?

SELECT DISTINCT t1.id
FROM t1 
  JOIN t2
    ON t1.id = t2.t1key
  JOIN t2 copyt2
    ON copyt2.t3key = t2.t3key 
WHERE copyt2.t1key IN
  (6,4)       --what you want to check here

更正

阅读 Joe 的评论并重新阅读问题详细信息,我猜您想找到与您指定列表中的所有单词在同一短语中出现的所有单词。

这看起来像一个关系划分问题:

SELECT DISTINCT t2a.t1key
FROM t2 AS t2a
WHERE NOT EXISTS
  ( SELECT *
    FROM t2 AS t2b
    WHERE t2b.t1key IN (6,4)
      AND NOT EXISTS
      ( SELECT *
        FROM t2 AS t2c
        WHERE t2a.t3key = t2c.t3key
          AND t2c.t1key = t2b.t1key
      )
  )

第二个解决方案:

SELECT a.t1key
FROM t2 AS a
  JOIN t2 as b
    ON  a.t3key = b.t3key
WHERE b.t1key IN (6,4)       --list you want to check
GROUP BY a.t1key, a.t3key
HAVING COUNT(*) = 2          --size of list
;

第三种解决方案:

SELECT DISTINCT t1key
FROM t2
WHERE t3key IN
  ( SELECT t3key
    FROM t2
    WHERE t1key IN (6,4)
    GROUP BY t3key
    HAVING COUNT(*) = 2
  )
;

注意:第一种(NON EXISTS)的解决方案与其他两种有很大的不同:

如果您尝试使用其成员未出现在表 t2 中的列表,例如 (2)(2,7),它将显示来自 t2 的所有 t1key。

在这种情况下,第 2 和第 3 解决方案将根本不显示任何键。

【讨论】:

t3 只是为了帮助您了解 t1 和 t2 之间的关系 使用您的查询,您的 6,4 值的结果返回 3,4,6,1,5 而不是 3,4,6。我不知道如何使它更清楚,我有一组输入,并且我想要在给定表结构的问题中指定的输出。 是的!这行得通,这正是我得到的(但我使用了 GROUP BY 和 HAVING)......但问题仍然是它是超级嵌套的!你知道打破这种嵌套的方法吗?大声笑 @Tony:在你的数据库中测试两种方式(我猜你在 t2 上有索引),看看哪个运行得更快。 +1 表示“第三种解决方案”。这可能是迄今为止最有效的答案。【参考方案3】:
select distinct t1key
from t2
where t3key in
(
    select t3key from t2 where t1key = 6
    intersect
    select t3key from t2 where t1key = 4
)

==> 3, 4, 6

您需要添加更多“相交”子句,具体取决于您的输入集中有多少项目。

在 SQL Server 上测试。

【讨论】:

啊,遗憾的是我忘了提到 mysql...但是是的,解决方案有效...有没有办法绕过嵌套?如果输入是 3,4,6,7,8,你将如何编写语句? 如果您的输入是 3,4,6,7,8,您需要添加更多“相交选择...”行。换句话说,您的程序将需要根据输入集中的项目数动态生成查询文本。至于嵌套,您可以通过将子查询分解为内部连接和相交来摆脱它,但在我的测试中,它会降低效率。 我刚刚使用 t2 中 300,000 行的测试数据查看了此处所有答案的 SQL Server 估计执行计划成本。 Stefan 的正确答案和我的答案首先并列,然后是 vbence,然后是 ypercube 的正确答案。但这可能并不能证明任何事情:) 您应该使用自己的数据测试所有答案。 Tnnx 进行测试!你能检查我最后的补充吗?我认为性能也会受到 t2 中密钥分布的影响。 @ypercube 嘿,干得好,您的最新解决方案胜过我和 Stefan 的解决方案。【参考方案4】:
select distinct t2b.t1key
from 
  t2 t2a
  inner join t2 t2b on t2a.t3key = t2b.t3key
where t2a.t1key in (6, 5) /* or whatever */

从 t1(关键字)开始,您会得到所有包含“cpu”(或其他)的 t3(表达式)。您不需要直接加入 t3,也不需要那里的任何数据。第二次加入 t2 时,您将获得包含在找到的表达式中的所有其他关键字。您只需要返回它们的 t1key。


更正:如果您不想要子查询,您可以为每个要搜索的关键字创建一个连接:

select distinct t2b.t1key
from 
  t2 t2a
  inner join t2 t2b on t2a.t3key = t2b.t3key and t2a.t1key = 6
  inner join t2 t2c on t2a.t3key = t2c.t3key and t2a.t1key = 5

【讨论】:

对于输入 6,4,这将返回 1,3,4,5,6,而不是 3,4,6。我想他想找到包含所有单词的短语,而不是任何单词,如果你明白我的意思的话。【参考方案5】:

他在那里, 你确定你选择了正确的表结构吗? 它似乎没有被规范化 - 虽然我不知道每个表可以代表什么实体。

将数据库设计至少保持在第三范式很重要(请参阅Wikipedia article

您的查询将更加自然且易于表述

【讨论】:

它看起来很正常化。 (对于 t1 和 t3 之间的多对多关系)

以上是关于有没有办法删除这种类型的 SQL SELECT 中的嵌套查询?的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法在 SQL Select 语句中使用来自 JSON 对象的值?

SQL Server中怎样可以从SELECT语句的结果集中删除重复行

SQL从待选项中随机选一个

无法删除用户的解决办法

有没有办法删除sql中的单元格[关闭]

有没有办法选择引用游标的内容作为 SQL Select 语句的一部分?