【问题标题】:Verify existance of all combinations in table验证表中所有组合是否存在
【发布时间】:2016-03-07 19:15:38
【问题描述】:

我想为 oracle 编写一个查询,以验证表中是否存在所有组合。 我的问题是表的“键列”是链接到其他表的 FK,这意味着组合是基于其他表的行。

ERD 示例:
所以,如果表 A 有 3 行(1-3),表 B 有 4 行,表 C 有 2 行,MyTable 必须有这些行(3x4x2,总共 24 行):

id, a_fk, b_fk, c_fk, someValue
x, 1, 1 ,1, ..
x, 1, 1, 2, ..
x, 1, 2, 1, ..
x, 1, 2, 2, ..
x, 1, 3, 1, ..
x, 1, 3, 2, ..
..............

我不知道怎么写,因为组合的可用数据可能会改变。

感谢您的帮助!

【问题讨论】:

  • 您是否尝试查询您拥有的所有组合,或找出任何缺失的组合,或者获取一个简单的是/否标志来说明是否缺少任何组合?
  • 识别缺失的组合/获得是/否标志。这两个选项都足够好。
  • 从性能的角度来看,该选项很重要。如果您只需要一个是/否标志,那么带有 EXISTS 或 NOT IN 的查询将在第一个 TRUE 时短路。如果需要一个列表,那么最好的方法可能取决于可用的索引。在小型数据集上,性能似乎不是什么大问题,但是从性能角度习惯于最佳实践是一个好习惯,因为当现实世界抛出数百万(或数亿)数据的那一天到来时对着你排。

标签: sql database oracle combinations


【解决方案1】:

您可以使用cross joins 识别所有可能的组合,这会生成行的笛卡尔积:

select a.id, b.id, c.id
from tablea a
cross join tableb b
cross join tablec c

根据您想要的确切结果,您可以通过各种方式使用它来查看您有什么或没有什么。要列出不存在的组合,请使用the minus set operator

select a.id, b.id, c.id
from tablea a
cross join tableb b
cross join tablec c
minus
select fk_a, fk_b, fk_c
from my_table mt;

或者您可以使用not exists 而不是减号,如其他答案所示。

如果您想将它们全部与主表的列一起列出(如果存在),否则为空,您可以使用左外连接:

select a.id, b.id, c.id, mt.id
from tablea a
cross join tableb b
cross join tablec c
left join my_table mt
on mt.fk_a = a.id and mt.fk_b = b.id and mt.fk_c = c.id

您还可以计算第一个查询的结果,然后在case 语句中使用它来获得一个简单的是/否答案,以显示是否所有组合都存在。等等——这真的取决于你想看到什么。

【讨论】:

    【解决方案2】:

    使用CROSS JOIN 从您的三个(或更多)表中获取记录集的笛卡尔积,并应用NOT EXISTS 子句为您提供mytable 中不存在的那些行(在输出中)。

    select a.id, b.id, c.id
    from tbla a cross join tblb b cross join tblc c
    where not exists (
      select 1
      from mytable t
      where t.fk_a = a.id
        and t.fk_b = b.id
        and t.fk_c = c.id
      )
    

    【讨论】:

      【解决方案3】:

      为了获得可能的组合,交叉连接有效。

      所以你可以得到你的 24 行:

      with a as (
          select 1 id1 from dual union all
          select 2 id1 from dual union all
          select 3 id1 from dual )
        , b as (
          select 1 id2 from dual union all
          select 2 id2 from dual union all
          select 3 id2 from dual union all
          select 4 id2 from dual )   
        , c as (
          select 1 id3 from dual union all
          select 2 id3 from dual )
      select id1, id2, id3 
      from a cross join b cross join c;   
      

      从那里查找表中存在或不存在的组合是一个非常简单的步骤。要获取不在目标表中的组合,您可以:

      with a as (
          select 1 id1 from dual union all
          select 2 id1 from dual union all
          select 3 id1 from dual )
        , b as (
          select 1 id2 from dual union all
          select 2 id2 from dual union all
          select 3 id2 from dual union all
          select 4 id2 from dual )   
        , c as (
          select 1 id3 from dual union all
          select 2 id3 from dual )
        , t as ( 
           select 1 id1, 1 id2, 1 id3 from dual union all
           select 1 id1, 1 id2, 2 id3 from dual union all
           select 1 id1, 2 id2, 1 id3 from dual union all
           select 1 id1, 2 id2, 2 id3 from dual union all
           select 1 id1, 3 id2, 1 id3 from dual union all
           select 1 id1, 4 id2, 2 id3 from dual )
      select lst.id1, lst.id2, lst.id3
      from (  
          select id1, id2, id3 
          from a cross join b cross join c ) lst
      where not exists (select 1 from t
                        where t.id1 = lst.id1 
                          and t.id2 = lst.id2
                          and t.id3 = lst.id3)        
      

      或者,使用 NOT IN 测试:

      select lst.id1, lst.id2, lst.id3
      from (  
          select id1, id2, id3 
          from a cross join b cross join c ) lst
      where (id1, id2, id3) not IN (select distinct id1, id2, id3  from t)
      

      Alex 的减号做同样的事情,都得出相同的结果集 - 哪个选项效果最好可能取决于复合表中的记录数、可用索引,以及 - 最重要的是 - 你到底是什么想要。

      如果您只想知道缺少一个或多个组合,请使用尽快短路的选项。例如,EXISTS 将在遇到评估为 TRUE 的案例时停止检查

      【讨论】:

        【解决方案4】:

        所以,MyTable 中的总行数应该等于其他表中总行数的乘积,你可以这样尝试:

        select
        case when (select count(*) from MyTable) =
        ((select count(*) from TableA) * (select count(*) from TableB)... )
        then 'Everything is fine'
        else 'Some rows are missing' end
        from dual
        

        仅当您对 (fk_a, fk_b, fc_...) 有唯一约束时,这才有效

        如果你没有,你可以使用 distinct

        select
        case when (select count(distinct concat(fk_a, fk_b,....)) from MyTable) =
        ((select count(*) from TableA) * (select count(*) from TableB)... )
        then 'Everything is fine'
        else 'Some rows are missing' end
        from dual
        

        【讨论】:

        • 我尝试运行此查询,但得到:ORA-00923 FROM keyword not found where expected
        • @kordirko 我的查询只是示例,您无法运行它,您是否对其进行了修改以满足您的实际结构?
        • 好的,我将if...then...else 替换为SELECT CASE WHEN ....... THEN 'Everything is fine' else 'Some rows are missing' end FROM dual,效果非常棒。
        • 如果my_table 中有多个 ow 且 a、b 和 c 的组合相同,则不会得到正确答案。
        • @AlexPoole 是的,正在更新
        猜你喜欢
        • 2018-05-06
        • 1970-01-01
        • 2021-06-21
        • 2014-12-11
        • 2015-11-29
        • 1970-01-01
        • 1970-01-01
        • 2019-05-16
        • 1970-01-01
        相关资源
        最近更新 更多