【问题标题】:Reduce overload on pl/sql减少 pl/sql 的过载
【发布时间】:2018-05-25 09:48:40
【问题描述】:

我需要对几个属性一一进行匹配。我希望避免使用多个选择语句。下面是例子。

    Table1
    Col1|Price|Brand|size
    -----------------------
    A|10$|BRAND1|SIZE1
    B|10$|BRAND1|SIZE1
    C|30$|BRAND2|SIZE2
    D|40$|BRAND2|SIZE4


    Table2
    Col1|Col2|Col3
    --------------
    B|XYZ|PQR
    C|ZZZ|YYY


    Table3
    Col1|COL2|COL3|LIKECOL1|Price|brand|size
    -----------------------------------------
    B|XYZ|PQR|A|10$|BRAND1|SIZE1
    C|ZZZ|YYY|D|NULL|BRAND2|NULL

在table3中,我需要通过检查以下条件从table2中插入数据。

  1. 为表 2 中的记录查找匹配项,如果品牌和尺寸、价格匹配
  2. 如果未找到匹配项,则仅尝试品牌、尺寸
  3. 仍然找不到匹配项,仅尝试品牌

在上面的示例中,对于 table2 中的第一条记录,发现与所有 3 个属性匹配,因此插入到 table3 和第二条记录中,记录 'D' 匹配但只有 'Brand'。

我能想到的就是将 3 个不同的插入语句(如下所示)写入 oracle pl/sql 块。

 insert into table3 
   select from tab2 
    where all 3 attributes are matching;

 insert into table3 
   select from tab2 
    where brand and price are matching 
      and not exists in table3 (not exists is to avoid 
                                inserting the same record which was already 
                                inserted with all 3 attributes matched);

 insert into table3 
   select from tab2 
    where Brand is matching and not exists in table3;

任何人都可以建议一种更好的方法来以更好的方式实现它,避免多次从 table2 中选择。

【问题讨论】:

    标签: oracle plsql query-performance


    【解决方案1】:

    这是OUTER APPLY的案例。

    OUTER APPLY 是一种横向连接,允许您连接动态视图,这些视图引用出现在 FROM 子句中的表。借助该功能,您可以定义一个动态视图来查找所有匹配项,按您指定的排序顺序对它们进行排序,然后使用FETCH FIRST 1 ROW ONLY 仅将第一个包含在结果中。

    使用OUTER APPLY意味着如果没有匹配,你仍然会得到表B记录——只是所有匹配列null。如果不想这样,可以将OUTER APPLY 更改为CROSS APPLY

    这是一个工作示例(一步一步的 cmets),厚颜无耻地从 Michael Piankov 的回答中窃取表创建脚本:

    create table Table1 (Col1,Price,Brand,size1)
    as select 'A','10','BRAND1','SIZE1' from dual union all
       select 'B','10','BRAND1','SIZE1' from dual union all
       select 'C','30','BRAND2','SIZE2' from dual union all
       select 'D','40','BRAND2','SIZE4'from dual 
    
    create table Table2(Col1,Col2,Col3)
    as select 'B','XYZ','PQR' from dual union all 
       select'C','ZZZ','YYY' from dual;
    
    -- INSERT INTO table3
    SELECT t2.col1, t2.col2, t2.col3,
    t1.col1 likecol1, 
    decode(t1.price,t1_template.price,t1_template.price, null) price,
    decode(t1.brand,t1_template.brand,t1_template.brand, null) brand,
    decode(t1.size1,t1_template.size1,t1_template.size1, null) size1
    FROM 
    -- Start with table2
    table2 t2
    -- Get the row from table1 matching on col1... this is our search template
    inner join table1 t1_template on
    t1_template.col1 = t2.col1
    -- Get the best match from table1 for our search 
    -- template, excluding the search template itself
    outer apply ( 
    SELECT * FROM table1 t1 
    WHERE 1=1
    -- Exclude search template itself
    and t1.col1 != t2.col1
    -- All matches include BRAND
    and t1.brand = t1_template.brand
    -- order by match strength based on price and size
    order by case when t1.price = t1_template.price and t1.size1 = t1_template.size1 THEN 1
    when t1.size1 = t1_template.size1 THEN 2
    else 3 END
    -- Only get the best match for each row in T2
    FETCH FIRST 1 ROW ONLY) t1;
    

    【讨论】:

    • 非常感谢您让我了解外部应用。现在明白这一点后,我的问题是,普通的外连接不能达到同样的效果吗?两者有什么区别?如果我写两次引用 table1 并如下所示会发生什么? SELECT <> FROM table1 t1 INNER JOIN table2 t2 ON t2.col1 = t1.col1 LEFT OUTER JOIN table1 t1_template ON (t1.price = t1_template.price AND t1.size1 = t1_template.size1) WHERE t1.col1 != t1_template.col1 AND t1.brand = t1_template.brand ORDER BY brand, price NULLS LAST, size1 NULLS LAST
    • OUTER APPLY 需要对每次搜索的匹配项进行排序,并根据您的要求只为您提供最强的匹配项。
    • 基于您提供指导的这个示例,我为我的需求开发了更多表和更多匹配条件的代码。我假设 order by 中的 case 子句与正常 order by 相同,并且只会按数字对列位置进行排序。但是在测试时我明白事实并非如此。那么,您能否按顺序说明 case 子句中数字的用法。
    • 是——CASE 的每个 WHEN 子句都是一个匹配条件。例如,WHEN 价格和尺寸匹配然后是“1”。 WHEN 只是大小匹配然后 2。数字没有什么特别之处 - 只是 1 在 2 之前排序,所以匹配更强的行首先排序,因为你 FETCH 只有第一行,你会仅从OUTER APPLY 中获得最强匹配。 “1”和“2”没什么特别的——它们不是列位置标识符。它们只是文字数字。我可以使用“A”和“B”。
    • CROSS APPLY 应该列出给定模板的所有匹配项,并将这些匹配项排在最前面。如果你必须去多个表来获得匹配,它们应该在CROSS APPLY 内连接,并在CROSS APPLY 内的ORDER BY 子句中说明。如果我理解正确的话,听起来你不应该创建第二个CROSS APPLY
    【解决方案2】:

    不幸的是,当你说匹配时,你不清楚你的意思是什么。如果有不止一场比赛,你的期望是什么? 它应该只是第一次匹配还是会生成所有可用的配对?

    关于如何避免多次插入的问题不止一种:

    1. 您可以使用带有INSERT first 和条件的多表插入。
    2. 您可以将 table1 加入 self 并获取所有对并在 where 条件下过滤结果
    3. 你可以使用解析函数
    4. 我想还有其他方法。但是为什么你想避免 3 个简单的插入。它易于阅读和维护。并且可能是

    下面有解析函数的例子:

    create table Table1 (Col1,Price,Brand,size1)
    as select 'A','10','BRAND1','SIZE1' from dual union all
       select 'B','10','BRAND1','SIZE1' from dual union all
       select 'C','30','BRAND2','SIZE2' from dual union all
       select 'D','40','BRAND2','SIZE4'from dual 
    
    create table Table2(Col1,Col2,Col3)
    as select 'B','XYZ','PQR' from dual union all 
       select'C','ZZZ','YYY' from dual
    
    with s as (
    select Col1,Price,Brand,size1, 
            count(*) over(partition by Price,Brand,size1 ) as match3, 
            count(*) over(partition by Price,Brand ) as match2,
            count(*) over(partition by Brand ) as match1,
            lead(Col1) over(partition by Price,Brand,size1 order by Col1) as like3, 
            lead(Col1) over(partition by Price,Brand order by Col1) as like2,
            lead(Col1) over(partition by Brand order by Col1) as like1,
            lag(Col1) over(partition by Price,Brand,size1 order by Col1) as like_desc3, 
            lag(Col1) over(partition by Price,Brand order by Col1) as like_desc2,
            lag(Col1) over(partition by Brand order by Col1) as like_desc1
    from Table1 t )
    select t.Col1,t.Col2,t.Col3, coalesce(s.like3, like_desc3, s.like1, like_desc1, s.like1, like_desc1),
        case when  match3 > 1 then size1 end as size1,
        case when  match1 > 1 then Brand end as Brand,
        case when  match2 > 1 then Price end as Price
            from table2 t
             left join s on s.Col1 = t.Col1 
    
    COL1    COL2    COL3    LIKE_COL    SIZE1   BRAND   PRICE
    B   XYZ PQR A   SIZE1   BRAND1  10
    C   ZZZ YYY D    -  BRAND2   - 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-10-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-25
      • 1970-01-01
      • 1970-01-01
      • 2012-05-23
      相关资源
      最近更新 更多