【问题标题】:How to find rows in one table that have no corresponding row in another table如何在一个表中查找另一表中没有对应行的行
【发布时间】:2010-11-27 18:39:02
【问题描述】:

我有两个表之间的 1:1 关系。我想查找表 A 中所有在表 B 中没有对应行的行。我使用此查询:

SELECT id 
  FROM tableA 
 WHERE id NOT IN (SELECT id 
                    FROM tableB) 
ORDER BY id desc

id 是两个表中的主键。除了主键索引,我在 tableA(id desc) 上也有一个索引。

使用 H2(Java 嵌入式数据库),这会导致对 tableB 进行全表扫描。我想避免全表扫描。

如何重写此查询以快速运行?我应该使用什么索引?

【问题讨论】:

  • 每次写“WHERE col [NOT] IN (SELECT col FROM othertable)”时,最好使用 [NOT] EXISTS 进行重构。

标签: sql optimization h2


【解决方案1】:
select tableA.id from tableA left outer join tableB on (tableA.id = tableB.id)
where tableB.id is null
order by tableA.id desc 

如果你的数据库知道如何做索引交集,这只会触及主键索引

【讨论】:

  • 这就是我喜欢 Stack Overflow 的原因。周六,SQL 题——5分钟准确答题成功!
  • 您在其他答案中也得到了一些很好的建议。自然我认为我的会是最快的 :-) 但 db 实现差异很大,而且我对 H2 没有经验。如果您对不同的方法进行基准测试并用您的结果更新问题,那就太好了。
【解决方案2】:

您也可以使用exists,因为有时它比left join 更快。您必须对它们进行基准测试以确定您要使用哪一个。

select
    id
from
    tableA a
where
    not exists
    (select 1 from tableB b where b.id = a.id)

为了表明existsleft join 更高效,以下是这些查询在 SQL Server 2008 中的执行计划:

left join - 子树总成本:1.09724:

exists - 子树总成本:1.07421:

【讨论】:

  • +1:如果子查询(在这种情况下是相关的)返回至少一行,则认为 EXISTS 条件“满足”。
  • 基准测试是个好主意。我正在绞尽脑汁试图弄清楚数据库可以在幕后为存在+相关子查询做什么,这将使其比仅索引哈希连接更快。你知道吗?
  • Exists 没有使用您的标准相关子查询。它使用半连接。在 SQL Server 2008 上,left join 的执行计划是两次索引扫描,哈希匹配,筛选器,选择。对于not exists,它是两次索引扫描到一个哈希匹配到一个select--no 过滤器。 exists 哈希匹配实际上比left join 稍快。 left join 的总成本为 1.09,not exists 的总成本为 1.07,DimCustomerAdventureWorksDWAdventureWorksDW2008
  • 很好!!谢谢。那是一个聪明的优化器。当然成本是近似值,但我是根据过滤器与半连接原则购买的。
【解决方案3】:

您必须检查 tableA 中的每个 ID 与 tableB 中的每个 ID。功能齐全的 RDBMS(例如 Oracle)将能够将其优化为 INDEX FULL FAST SCAN 而根本不接触表。不知道H2的优化器有没有这么聪明。

H2 确实支持 MINUS 语法,所以你应该试试这个

select id from tableA
minus
select id from tableB
order by id desc

这可能会执行得更快;这当然值得进行基准测试。

【讨论】:

    【解决方案4】:

    对于我的小型数据集,Oracle 为几乎所有这些查询提供了完全相同的计划,即使用主键索引而不接触表。 MINUS 版本是个例外,尽管计划成本更高,但它却能获得更少的一致性。

    --Create Sample Data.
    d r o p table tableA;
    d r o p table tableB;
    
    create table tableA as (
       select rownum-1 ID, chr(rownum-1+70) bb, chr(rownum-1+100) cc 
          from dual connect by rownum<=4
    );
    
    create table tableB as (
       select rownum ID, chr(rownum+70) data1, chr(rownum+100) cc from dual
       UNION ALL
       select rownum+2 ID, chr(rownum+70) data1, chr(rownum+100) cc 
          from dual connect by rownum<=3
    );
    
    a l t e r table tableA Add Primary Key (ID);
    a l t e r table tableB Add Primary Key (ID);
    
    --View Tables.
    select * from tableA;
    select * from tableB;
    
    --Find all rows in tableA that don't have a corresponding row in tableB.
    
    --Method 1.
    SELECT id FROM tableA WHERE id NOT IN (SELECT id FROM tableB) ORDER BY id DESC;
    
    --Method 2.
    SELECT tableA.id FROM tableA LEFT JOIN tableB ON (tableA.id = tableB.id)
    WHERE tableB.id IS NULL ORDER BY tableA.id DESC;
    
    --Method 3.
    SELECT id FROM tableA a WHERE NOT EXISTS (SELECT 1 FROM tableB b WHERE b.id = a.id) 
       ORDER BY id DESC;
    
    --Method 4.
    SELECT id FROM tableA
    MINUS
    SELECT id FROM tableB ORDER BY id DESC;
    

    【讨论】:

    • 写它d r o p,它让人们阅读代码,你得到+1
    【解决方案5】:

    我无法告诉您这些方法中的哪一种最适合 H2(或者即使它们都可以工作),但我确实写了一篇文章,详细介绍了 TSQL 中可用的所有(好的)方法。您可以试一试,看看它们是否适合您:

    http://code.msdn.microsoft.com/SQLExamples/Wiki/View.aspx?title=QueryBasedUponAbsenceOfData&referringTitle=Home

    【讨论】:

      【解决方案6】:
      select parentTable.id from parentTable
      left outer join childTable on (parentTable.id = childTable.parentTableID) 
      where childTable.id is null
      

      【讨论】:

      • 请不要解释一下吗?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-01-18
      • 1970-01-01
      • 1970-01-01
      • 2012-01-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多