【问题标题】:SQL-Query: EXISTS in SubtableSQL 查询:存在于子表中
【发布时间】:2011-03-28 10:33:13
【问题描述】:

我有两个表 tabData 和 tabDataDetail。 我希望父表(tabData)中的所有idData(PK)在子表(tabDataDetail,FK is fiData)中具有only行:

  • fiActionCode=11 单独 或
  • fiactionCode=11 和 fiActionCode=34

任何其他组合均无效。如何获得?

我尝试过但没有成功(速度很慢,还给了我 only fiActioncode 34 的行):


(来源:bilder-hochladen.net

感谢您的宝贵时间。


编辑:感谢大家的回答。不幸的是,现在我没有足够的时间来检查哪个是最好的或根本有效的。我将第一个有效的标记为答案。

EDIT2:我认为标记的答案确实是最有效和最紧凑的解决方案。

EDIT3:Codesleuth 的回答很有趣,因为它只返回行而不是只有一个 fiActionCode=11。很难看到,因为它仅适用于 20 个 tabDataDetail-rows ot 41524189 总行有两个。无论如何,这不是我所要求的 100% 或更确切地说是我正在寻找的内容。

【问题讨论】:

  • 从未提及输出中需要哪些列。它只是 tabData 列还是需要来自 tabDataDetail 的任何数据?
  • 只有我感兴趣的主键(idData)应该按(如果需要)分组(和排序)。但要检查结果,最好也有 fiActionCode。

标签: sql sql-server exists


【解决方案1】:
Select ...
From tabData As T1
Where Exists    (
                Select 1
                From tabDataDetail As TDD1
                Where TDD1.fiData = T1.idData
                    And TDD1.fiactionCode = 11
                )
    And Not Exists    (
                      Select 1
                      From tabDataDetail As TDD1
                      Where TDD1.fiData = T1.idData
                          And TDD1.fiactionCode Not In(11,34)
                    )

为了扩展我的逻辑,第一个检查(更正)是确保存在 fiActionCode = 11 的行。第二个检查首先定义我们不想要的行集。除了 fiActionCode = 11 或 34 之外,我们不想要任何东西。因为那是我们不想要的项目集合,所以我们搜索该集合中不存在的任何内容。

【讨论】:

  • 谢谢。但这也给了我在 Childtable 中只有 34 作为 fiActionCode 的行。这些应该被排除在外。
  • @Tim - 已修复。只需要额外检查以确保 fiActionCode = 11 存在。
  • PK 是 idData 和 tablename 是 tabData 但除此之外它似乎工作(我不知道为什么)。我有 400k 行,所以很难检查。
  • @Tim - 一个简单的验证检查是确定任何结果行是否有一个 tabDataDetail 行,其 fiActionCode 值等于 34 以外的值(我们知道它必须有一个值 = 11 的行)。只需将内部连接添加到 tabDataDetail 就可以更轻松地进行目视检查。
  • @Thomas:超级高效,不错!
【解决方案2】:

推理

  1. LEFT OUTER JOIN 排除所有 idData 不同于 11 或 34 的 idData
  2. HAVING 排除所有只有有 34
  3. 的 idData
  4. 剩余记录(应该)满足所有约束

测试数据

DECLARE @tabData TABLE (idData INTEGER)
DECLARE @tabDataDetail TABLE (fiData INTEGER, fiActionCode INTEGER)

INSERT INTO @tabData VALUES (1)
INSERT INTO @tabData VALUES (2)
INSERT INTO @tabData VALUES (3)
INSERT INTO @tabData VALUES (4)
INSERT INTO @tabData VALUES (5)

/* Only idData 1 & 2 should be returned */
INSERT INTO @tabDataDetail VALUES (1, 11)
INSERT INTO @tabDataDetail VALUES (2, 11)
INSERT INTO @tabDataDetail VALUES (2, 34)
INSERT INTO @tabDataDetail VALUES (3, 99)
INSERT INTO @tabDataDetail VALUES (4, 11)
INSERT INTO @tabDataDetail VALUES (4, 99)
INSERT INTO @tabDataDetail VALUES (5, 34)

查询

SELECT  *
FROM    @tabData d
        INNER JOIN @tabDataDetail dd ON dd.fiData = d.idData
        INNER JOIN (
          SELECT  idData
          FROM    @tabData d
                  INNER JOIN @tabDataDetail dd ON dd.fiData = d.idData
                  LEFT OUTER JOIN (
                    SELECT  fiData
                    FROM    @tabDataDetail
                    WHERE   fiActionCode NOT IN (11, 34)
                  ) exclude ON exclude.fiData = d.idData
          WHERE   exclude.fiData IS NULL                
          GROUP BY
                  idData
          HAVING  MIN(fiActionCode) = 11        
        ) include ON include.idData = d.idData

【讨论】:

  • 谢谢,但我收到了几个“无法绑定多部分标识符“d.idData”。和“不明确的列名”
  • 你删除了@吗?查询在我的系统上运行没有问题。
  • 也可以工作(有点慢)。谢谢
  • @Tim,正确的索引可能会大大加快速度,但很难(如果不是不可能的话)击败 Thomas 的 NOT EXISTS 解决方案。
【解决方案3】:

根据 cmets 中对其他答案的说明编辑了我的答案。

select td.idData
 from tabData td
  left join tabDataDetail tdd
   on td.idData = tdd.fiData
    and tdd.fiActionCode = 11
  left join tabDataDetail tdd2
   on td.idData = tdd2.fiData
    and tdd2.fiActionCode = 34
  left join tabDataDetail tdd3
   on td.idData = tdd3.fiData
    and tdd3.fiActionCode not in (11,34)
 where (tdd.fiData is not null
  or (tdd.fiData is not null and tdd2.fiData is not null))
  and tdd3.fiData is null
 group by td.idData

【讨论】:

    【解决方案4】:

    编辑:Apols - 我明白你对子行的意思。这不是特别有效。还要感谢 Lieven 提供的数据。

    SELECT idData FROM
    tabData td
    WHERE EXISTS 
    (
        SELECT 1 
            FROM tabDataDetail tdd 
            WHERE tdd.fiData = td.idData AND fiActionCode = 11
     )
    AND NOT EXISTS
    (
        SELECT 1 
            FROM tabDataDetail tdd 
            WHERE tdd.fiData = td.idData AND fiActionCode <> 11
     )
    UNION
    SELECT idData 
        FROM tabData td
        WHERE EXISTS 
        (
            SELECT 1 
                FROM tabDataDetail tdd 
                WHERE tdd.fiData = td.idData AND fiActionCode = 11
         )
        AND EXISTS
        (
            SELECT 1 
                FROM tabDataDetail tdd 
                WHERE tdd.fiData = td.idData AND fiActionCode = 34
         )
    AND NOT EXISTS
    (
        SELECT 1 
            FROM tabDataDetail tdd 
            WHERE tdd.fiData = td.idData AND fiActionCode NOT IN (11, 34)
     )
    

    【讨论】:

      【解决方案5】:

      感谢@Lieven提供数据代码来测试:

      DECLARE @tabData TABLE (idData INTEGER)
      DECLARE @tabDataDetail TABLE (idDataDetail int IDENTITY(1,1),
          fiData INTEGER, fiActionCode INTEGER)
      
      INSERT INTO @tabData VALUES (1)
      INSERT INTO @tabData VALUES (2)
      INSERT INTO @tabData VALUES (3)
      INSERT INTO @tabData VALUES (4)
      INSERT INTO @tabData VALUES (5)
      
      /* Only idData 1 & 2 should be returned */
      INSERT INTO @tabDataDetail (fiData,fiActionCode) VALUES (1, 11)
      INSERT INTO @tabDataDetail (fiData,fiActionCode) VALUES (2, 11)
      INSERT INTO @tabDataDetail (fiData,fiActionCode) VALUES (2, 34)
      INSERT INTO @tabDataDetail (fiData,fiActionCode) VALUES (3, 99)
      INSERT INTO @tabDataDetail (fiData,fiActionCode) VALUES (4, 11)
      INSERT INTO @tabDataDetail (fiData,fiActionCode) VALUES (4, 99)
      INSERT INTO @tabDataDetail (fiData,fiActionCode) VALUES (5, 34)
      

      查询:

      SELECT  td.idData
      FROM    @tabData td
              INNER JOIN @tabDataDetail tdd ON td.idData = tdd.fiData
      WHERE   tdd.fiActionCode = 11 -- check 11 exists
              AND NOT EXISTS ( SELECT * FROM @tabDataDetail WHERE fiData = td.idData
                                AND idDataDetail <> tdd.idDataDetail )
                  -- ensures *only* 11 exists (0 results from subquery)
      UNION
      SELECT  td.idData
      FROM    @tabData td
              INNER JOIN @tabDataDetail tdd1 ON td.idData = tdd1.fiData
              INNER JOIN @tabDataDetail tdd2 ON td.idData = tdd2.fiData
      WHERE   tdd1.fiActionCode = 11 -- check 11 exists
              AND tdd2.fiActionCode = 34 -- check 34 exists
      

      返回:

      idData
      ------------
      1
      2
      
      (2 行受影响)

      这里只有 1 个子查询(它是 COUNT 而不是非常慢的 NOT EXISTS),这会创建一个非常简洁的执行计划,如果您遇到速度问题,它应该会有所帮助。

      【讨论】:

      • @CodeSleuth,别提了。根据性能分析器,我的解决方案的成本为 24%,您的解决方案为 32%。我很想知道在现实世界的场景中是否成立。
      • 我还没有真正详细研究过这个问题或这个答案,但总的来说,NOT EXISTS 在 SQL Server 中比COUNT(*) 更有效,因为它执行了反半联接。它只需要检查匹配的行是否不存在。不计算所有匹配的。
      • @Lieven:什么成本?只是子查询?抱歉,我似乎把NOT INNOT EXISTS 混为一谈了——我错了,在我的回答中,性能实际上比COUNT 好。我会编辑它。干杯!
      • @Lieven,@Martin Smith:已修复,再次抱歉:(
      • 查询返回 212040 个 tabData 行,但正确的结果应该是 212050。我还不知道为什么。
      【解决方案6】:

      这是通过我认为的数据进行的一次传递。

      这取决于数据分布是否比进行 2 次单独查找更可取。

      WITH matches AS
      (
      SELECT fiData
      FROM tabDataDetail 
      GROUP BY fiData
      HAVING COUNT(CASE WHEN fiactionCode = 11 THEN 1 END) > 0
      AND COUNT(CASE WHEN fiactionCode NOT IN (11,34) THEN 1 END) = 0
      )
      SELECT ...
      FROM idData i
      JOIN matches m
      ON  m.fiData = i.idData
      

      【讨论】:

      • 这也有效(将“SELECT ... FROM idData i”更改为“SELECT idData FROM tabData i”时)。谢谢。
      猜你喜欢
      • 2016-11-30
      • 1970-01-01
      • 2017-10-04
      • 1970-01-01
      • 2019-04-05
      • 1970-01-01
      • 2020-06-21
      • 2018-01-24
      • 1970-01-01
      相关资源
      最近更新 更多