【问题标题】:Oracle join view for best matchesOracle 加入视图以获得最佳匹配
【发布时间】:2015-12-02 20:27:03
【问题描述】:

我希望创建一个将两个表连接在一起的视图。

T1 =

Col1 Col2
AA   BB
EE   FF
YY   ZZ
11   00

T2 =

Col1 Col2 Col3
AA   BB   1
AA   CC   2
CC   BB   3
GG   FF   4
GG   HH   5
EE   HH   6
XX   YY   7
XX   WW   8
YY   RR   9

此视图的规则是基于以下规则的最佳匹配方案:

1. Return Col3 from T2 if T1.Col1 & T1.Col2 = T2.Col1 & T2.Col2
ELSE
2. Return Col3 if T1.Col2 = T2.Col2
ELSE
3. Return Col3 if T1.Col = T2.Col1
ELSE
4. Return NULL

因此,在这些示例中,我希望最终视图包含:

AA BB 1   (Rule 1 match)
EE FF 4   (Rule 2 match)
YY ZZ 9   (Rule 3 match)
11 00 NULL (Rule 4 match)

我遇到的困难在于它满足多个规则的情况(例如,第 1 行和第 3 行,其中规则 1 和 2 被击中,或者第 4 和 6 行,其中规则 2 和 3 分别被击中)。

我意识到在这个例子中规则 3 被多次命中 - 这很好,因为它只会在其他规则不正确时才会命中规则 3,而 应该 只产生 1结果(如示例 3 所示)。

有没有办法进行类似的联合来满足这些级联规则,或者是否需要使用预过滤创建额外的视图(例如计数

excel 中的一个公式是: =IF(AND(A3=$F$2,B3=$G$2),"规则 1",IF((B3=$G$2),"规则 2",IF((A3=$F$2),"规则 3 ","NULL")))

其中 A3 = T2.Col1,B3 = T2.Col2 G2 = T1.Col2 和 F2 = T1.Col1。

【问题讨论】:

    标签: sql oracle join views union


    【解决方案1】:

    我会这样做:

    with t1 as (select 'AA' col1, 'BB' col2 from dual union all
                select 'EE' col1, 'FF' col2 from dual union all
                select 'YY' col1, 'ZZ' col2 from dual union all
                select '11' col1, '00' col2 from dual),
         t2 as (select 'AA' col1, 'BB' col2, 1 col3 from dual union all
                select 'AA' col1, 'CC' col2, 2 col3 from dual union all
                select 'CC' col1, 'BB' col2, 3 col3 from dual union all
                select 'GG' col1, 'FF' col2, 4 col3 from dual union all
                select 'GG' col1, 'HH' col2, 5 col3 from dual union all
                select 'EE' col1, 'HH' col2, 6 col3 from dual union all
                select 'XX' col1, 'YY' col2, 7 col3 from dual union all
                select 'XX' col1, 'WW' col2, 8 col3 from dual union all
                select 'YY' col1, 'RR' col2, 9 col3 from dual),
        res as (select t1.col1,
                       t1.col2,
                       t2.col3,
                       case when t1.col1 = t2.col1 and t1.col2 = t2.col2 then 1
                            when t1.col2 = t2.col2 then 2
                            when t1.col1 = t2.col1 then 3
                       end join_level,
                       min (case when t1.col1 = t2.col1 and t1.col2 = t2.col2 then 1
                                 when t1.col2 = t2.col2 then 2
                                 when t1.col1 = t2.col1 then 3
                            end) over (partition by t1.col1, t1.col2) min_join_level
                from   t1
                       left outer join t2 on (t1.col1 = t2.col1 or t1.col2 = t2.col2))
    select col1,
           col2,
           col3
    from   res
    where  join_level = min_join_level
    or     join_level is null;
    
    
    COL1 COL2       COL3
    ---- ---- ----------
    11   00                                       
    AA   BB            1
    EE   FF            4
    YY   ZZ            9
    

    即。首先进行连接(在这种情况下,t1 left outer join t2 on (t2.col1 = t1.col1 or t2.col2 = t1.col2) 包括 t1.col1 = t2.col1 and t1.col2 = t2.col2 所在的行),然后根据哪个连接条件优先过滤结果。


    这里有一个稍微不同的选择,使用聚合而不是像上面的答案那样的分析函数:

    with t1 as (select 'AA' col1, 'BB' col2 from dual union all
                select 'EE' col1, 'FF' col2 from dual union all
                select 'YY' col1, 'ZZ' col2 from dual union all
                select '11' col1, '00' col2 from dual),
         t2 as (select 'AA' col1, 'BB' col2, 1 col3 from dual union all
                select 'AA' col1, 'CC' col2, 2 col3 from dual union all
                select 'CC' col1, 'BB' col2, 3 col3 from dual union all
                select 'GG' col1, 'FF' col2, 4 col3 from dual union all
                select 'GG' col1, 'HH' col2, 5 col3 from dual union all
                select 'EE' col1, 'HH' col2, 6 col3 from dual union all
                select 'XX' col1, 'YY' col2, 7 col3 from dual union all
                select 'XX' col1, 'WW' col2, 8 col3 from dual union all
                select 'YY' col1, 'RR' col2, 9 col3 from dual)
    select t1.col1,
           t1.col2,
           min(t2.col3) keep (dense_rank first order by case when t1.col1 = t2.col1 and t1.col2 = t2.col2 then 1
                                                             when t1.col2 = t2.col2 then 2
                                                             when t1.col1 = t2.col1 then 3
                                                        end) col3
    from   t1
           left outer join t2 on (t1.col1 = t2.col1 or t1.col2 = t2.col2)
    group by t1.col1,
             t1.col2;
    
    COL1 COL2       COL3
    ---- ---- ----------
    11   00             
    AA   BB            1
    EE   FF            4
    YY   ZZ            9
    

    注意如果碰巧有不止一行满足最高优先级的可用连接条件,这些可能会返回不同的结果。第一个查询将返回具有(可能)不同的 col3 的每一行,而第二个查询将只返回一个具有最低可用 col3 值的行。


    如果 T2 包含,您希望看到什么:

    COL1 COL2       COL3
    ---- ---- ----------
    AA   BB            1
    AA   CC            2
    CC   BB            3
    GG   FF            4
    GG   HH            5
    EE   HH            6
    XX   YY            7
    XX   WW            8
    YY   RR            9
    YY   SS           10
    

    第一个查询会给你:

    COL1 COL2       COL3
    ---- ---- ----------
    11   00             
    AA   BB            1
    EE   FF            4
    YY   ZZ           10
    YY   ZZ            9
    

    第二个查询会给你:

    COL1 COL2       COL3
    ---- ---- ----------
    11   00             
    AA   BB            1
    EE   FF            4
    YY   ZZ            9
    

    【讨论】:

    • 嗨,通过您的代码似乎是正确的(需要在我的视图和表格中指出 t1 和 t2 以确认)但是您能解释一下使用这两种解决方案的优点/缺点吗?
    • 这在某种程度上取决于您希望在连接有多个匹配项的情况下发生什么 - 例如。如果在 T2 中有一行 (col1, col2, col3) = ('YY', 'PP', 10),第一个查询将返回两行 t1 (col1, col2) = ('YY', 'ZZ ) 分别使用 col3 = 9 和 10,而第二个将仅返回一行,其中 col3 = 9。
    • 我遵循了解决方案 2,但将 t1 定义为 (select * from view1) t2 (select * from view2),然后替换了 col 名称。但是,我在 Col3 上遇到了障碍 - 这应该是视图 2 中有效的现有列吗?我这样做(随机挑选了一个列),并且在选择rownum 5秒,所以我杀死了查询,以防它击中递归障碍)。信息:view1 有几百万行,view2 有 >500,000。
    • 如果 col3 是您要针对 t1 行显示的内容,那么是的,它需要是来自 view2 的列或在 t2 子查询中派生的列。此外,我不一定希望它运行得特别快。
    • 我已经成功地改变了上述情况,只为 t1 拉回 col1 和 col2,从而获得更好的性能。也许我过度简化了我的示例,但我还想做的是撤回 t2.col4 t2.col5 t2.col6 等,而不仅仅是 col3 - 需要进行哪些调整?我是否需要一个伪列,以便我根据 rownum 从 view2 拉回(col3 col4 等),还是比这更容易?
    【解决方案2】:

    也许是这个方法,它将三个公共表表达式的结果集链接在一起,每个表达式实现不同的连接并检查 T1 中行的 rowid 是否已经从成功的连接中投影出来:

    with
      first_join as (
        select t1.col1,
               t1.col2,
               t2.col3,
               t1.rowid
        from   t1 join t2 on t1.col1 = t2.col1 and t1.col2 = t2.col2),
      second_join as (
        select t1.col1,
               t1.col2,
               t2.col3,
               t1.rowid
        from   t1 join t2 on  t1.col2 = t2.col2
        where  t1.rowid not in (select rowid from first_join)),
      third_join as (
          select t1.col1,
                 t1.col2,
                 t2.col3,
          from   t1 join t2 on  t1.col1 = t2.col1
          where  t1.rowid not in (select rowid from first_join union all
                                  select rowid from second_join))
    select col1, col2, col3 from first_join  union all
    select col1, col2, col3 from second_join union all
    select col1, col2, col3 from third_join
    

    【讨论】:

    • 这有一些问题: 1. 它不包括没有连接的行。 2. 在third_join 子查询中t2.col3 后面多了一个逗号, 3. 您需要在每个子查询中为rowid 设置别名,并引用select rowid 子查询中的那些)。我想一个额外的联合都可以选择那些t1.col1 != t2.col2 and t1.col2 != t2.col2?
    • “t1”实际上是一个视图,所以当我模拟它时,我得到了视图编译错误:ORA-01445: cannot select ROWID from, or sample, a join view without a key-保留表现在将尝试其他建议的答案@Boneist
    • David 的查询确实存在一些语法问题,正如我在上面的评论中提到的那样,但是如果您的 t1 和/或 t2 是视图,那么您必须找到其他方法来为该行;也许你可以这样做:t1 as (select row_number() over (order by col1, col2) rn, col1, col2 from your_t1_view) 然后在其他任何地方引用它?
    • @bobdylan 好吧,并不是说它是一个视图,而是你说它们是表。如果您要让人们花时间解决您的问题,请务必正确描述问题,这一点非常重要。
    • 如果 t1 / t2 是表格,则会出现该错误;它是对执行此操作的 rowid 的引用 - 您需要在 first_join 和 second_join 子查询中为其设置别名。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-12
    • 1970-01-01
    • 1970-01-01
    • 2019-11-24
    • 2017-02-08
    相关资源
    最近更新 更多