减少pl / sql的重载

时间:2018-05-25 09:48:41

标签: oracle plsql query-performance

我要求逐个匹配几个属性。我希望避免多个select语句。以下是示例。

    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

在表3中,我需要通过检查以下条件来插入table2中的数据。

  1. 如果品牌和尺寸,价格匹配
  2. ,请在table2中找到记录匹配项
  3. 如果未找到匹配项,请尝试使用品牌,尺寸
  4. 仍未找到匹配项,仅限品牌
  5. 在上面的例子中,对于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中选择。

2 个答案:

答案 0 :(得分:2)

这是OUTER APPLY的情况。

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

使用OUTER APPLY表示如果没有匹配,您仍会获得表B记录 - 只有所有匹配列null。如果您不想这样,可以将OUTER APPLY更改为CROSS APPLY

这是一个工作示例(一步一步的评论),从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;

答案 1 :(得分:0)

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

关于你如何避免多次插入的问题,有多种方法:

  1. 您可以使用带有INSERT first和条件的多表插入。
  2. 您可以将table1连接到self并获取所有对并在条件
  3. 中过滤结果
  4. 您可以使用分析功能
  5. 我想还有另外一种方法。但是为什么你要避免3个简单的插入。它易于阅读和维护。可能
  6. 下一步有分析功能的例子:

    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   -