减少 pl/sql 上的过载
Reduce overload on pl/sql
我有一个需求,一个一个地匹配几个属性。我希望避免使用多个 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 中,我需要通过检查以下条件从表 2 中插入数据。
- 在表 2 中查找匹配的记录,如果品牌和尺码、价格匹配
- 如果找不到匹配项,则只尝试品牌、尺码
- 仍未找到匹配项,请仅尝试品牌
在上面的示例中,对于 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;
任何人都可以建议一个更好的方法来避免多次 select 从 table2 中获取更好的方法。
不幸的是,我不清楚你所说的匹配是什么意思。如果有不止一场比赛,你的期望是什么?
它应该只是第一次匹配还是会生成所有可用的对?
关于如何避免多次插入的问题,有不止一种方法:
- 您可以使用带有
INSERT first
和条件的多表插入。
- 您可以将 table1 连接到 self 并获取所有对并在 where 条件
中过滤结果
- 你可以使用解析函数
- 我想还有另一种方法。但是为什么要避免 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 -
这是 OUTER APPLY
的案例。
OUTER APPLY
是一种横向连接,它允许您连接引用 table 之前出现在 FROM
子句中的动态视图。有了这种能力,您可以定义一个动态视图来查找所有匹配项,按照您指定的优先顺序对它们进行排序,然后使用 FETCH FIRST 1 ROW ONLY
仅在结果中包含第一个。
使用 OUTER APPLY
意味着如果没有匹配项,您仍然会得到 table B 记录——只有所有匹配列 null
。如果你不想这样,你可以将 OUTER APPLY
更改为 CROSS APPLY
。
这是一个工作示例(带有逐步注释),无耻地从 Michael Piankov 的回答中窃取了 table 创建脚本:
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;
我有一个需求,一个一个地匹配几个属性。我希望避免使用多个 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 中,我需要通过检查以下条件从表 2 中插入数据。
- 在表 2 中查找匹配的记录,如果品牌和尺码、价格匹配
- 如果找不到匹配项,则只尝试品牌、尺码
- 仍未找到匹配项,请仅尝试品牌
在上面的示例中,对于 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;
任何人都可以建议一个更好的方法来避免多次 select 从 table2 中获取更好的方法。
不幸的是,我不清楚你所说的匹配是什么意思。如果有不止一场比赛,你的期望是什么? 它应该只是第一次匹配还是会生成所有可用的对?
关于如何避免多次插入的问题,有不止一种方法:
- 您可以使用带有
INSERT first
和条件的多表插入。 - 您可以将 table1 连接到 self 并获取所有对并在 where 条件 中过滤结果
- 你可以使用解析函数
- 我想还有另一种方法。但是为什么要避免 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 -
这是 OUTER APPLY
的案例。
OUTER APPLY
是一种横向连接,它允许您连接引用 table 之前出现在 FROM
子句中的动态视图。有了这种能力,您可以定义一个动态视图来查找所有匹配项,按照您指定的优先顺序对它们进行排序,然后使用 FETCH FIRST 1 ROW ONLY
仅在结果中包含第一个。
使用 OUTER APPLY
意味着如果没有匹配项,您仍然会得到 table B 记录——只有所有匹配列 null
。如果你不想这样,你可以将 OUTER APPLY
更改为 CROSS APPLY
。
这是一个工作示例(带有逐步注释),无耻地从 Michael Piankov 的回答中窃取了 table 创建脚本:
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;