如何解决Oracle SQL将行中的一列与同一列中的前一行进行比较
这是我的表(Table1)当前在Oracle数据库中的方式。
<CustomTooltip id="123"><Text>...</Text></CustomTooltip>
我试图获得一个输出,该输出将每一行的Product列进行比较,以得到如下所示的内容: 在这里,我正在比较第1行和第2行,以查看第2行是否具有第1行中没有的新产品(NEW_PRODUCTS)。
似乎我可以使用LAG或LEAD函数,但由于产品之间使用ID Year_Mth Product
123 201901 1,2,3
123 201902 1,4,5
123 201903 2,3,6
123 201904 1,5,6
分隔符,因此似乎很棘手。
,
解决方法
这是一种选择。看起来和您的数据模型一样丑陋:)查看代码中的注释。如果您不确定每个CTE的功能,建议您分步运行以下代码并查看其结果。
为了便于阅读,我将其分为几部分。
SQL> with
2 test (id,year_mth,product) as
3 -- your sample data (as well as some of my sample data)
4 (select 123,201901,'1,2,3' from dual union all
5 select 123,201902,4,5' from dual union all
6 select 123,201903,'2,3,6' from dual union all
7 select 123,201904,5,6' from dual union all
8 --
9 select 888,'apple,banana' from dual union all
10 select 888,banana' from dual union all
11 select 888,lemon' from dual
12 ),
13 py as
14 (select id,15 year_mth ymp,-- "this" year_mth
16 lead(year_mth) over (partition by id order by year_mth) ymn -- "next" year_mth
17 from test
18 order by id,year_mth
19 ),20 tabp as
21 -- products that belong to "THIS" year_mth split to rows
22 (select
23 t.id,24 t.year_mth,25 p.ymp,26 p.ymn,27 regexp_substr(t.product,'[^,]+',1,c.column_value) product
28 from test t join py p on t.id = p.id and t.year_mth = p.ymp cross join
29 table(cast(multiset(select level from dual
30 connect by level <= regexp_count(product,',') + 1
31 ) as sys.odcinumberlist)) c
32 ),33 tabn as
34 -- products that belong to "NEXT" year_mth split to rows
35 (select
36 t.id,37 t.year_mth,38 p.ymp,39 p.ymn,40 regexp_substr(t.product,c.column_value) product
41 from test t join py p on t.id = p.id and t.year_mth = p.ymn cross join
42 table(cast(multiset(select level from dual
43 connect by level <= regexp_count(product,') + 1
44 ) as sys.odcinumberlist)) c
45 ),
-
46 newprod as 47 -- MINUS set operator finds differences between "NEXT" and "THIS" year_mth 48 (select id,ymn,product from tabn 49 minus 50 select id,product from tabp 51 ) 52 -- finally,aggregate new products (result of the previous MINUS set operation) 53 select 54 t.id,55 t.year_mth,56 t.product,57 listagg(case when t.rn = 1 then t.product else n.product end,') 58 within group (order by n.product) new_products 59 from (select a.id,60 a.year_mth,61 a.product,62 row_number() over (partition by a.id order by a.year_mth) rn 63 from test a 64 ) t left join newprod n on t.id = n.id and t.year_mth = n.ymn 65 group by t.id,t.year_mth,t.product 66 order by t.id,t.year_mth;
-
ID YEAR_MTH PRODUCT NEW_PRODUCTS
123 201901 1,3 1,3 123 201902 1,5 4,5 123 201903 2,6 3,6 123 201904 1,6 1,5 888 201901 apple,banana apple,banana 888 201902 apple,banana 888 201903 apple,lemon lemon
已选择7行。
SQL>
在需要使用此类定界字符串的情况下,使用xml函数(例如fn:string-join(),fn:tokenize())通常非常方便。
例如:
xmltable(
'let $x:=tokenize($a,","),$y:=tokenize($b,")
return fn:string-join($x[not(.=$y)],")'
passing product as "a",prev_product as "b"
columns New_Products varchar(100) path '.'
) x
此xmltable()拆分输入参数product和prev_product,并从prev_product中不在product中的那些子字符串中返回:
- 函数
tokenize($a,")
使用逗号作为分隔符来拆分输入字符串$ a。 -
$x[not(.=$y)]
从$ x返回$ y中不存在的那些值 - 函数
string-join($arg1,")
使用逗号作为分隔符来连接$ arg1中的值。
完整示例:
with
test (id,product) as
-- your sample data (as well as some of my sample data)
(select 123,3' from dual union all
select 123,5' from dual union all
select 123,6' from dual union all
select 123,6' from dual union all
--
select 888,banana' from dual union all
select 888,lemon' from dual
)
select
t.*,x.*
from
(
select
t.*,lag(t.product)over(partition by id order by year_mth) prev_product
from test t
) t,xmltable(
'let $x:=tokenize($a,prev_product as "b"
columns New_Products varchar(100) path '.'
) x;
我使上面的xquery很久了,只是为了使其更具可读性。
在现实生活中,xquery会更短:
fn:string-join(tokenize($a,")[not(.=tokenize($b,"))],")
with
test (id,xmltable(
'fn:string-join(tokenize($a,prev_product as "b"
columns New_Products varchar(100) path '.'
) x
,
与我的相似,如果要重新透视,请在末尾添加一个listagg和group-by查询...
WITH
input(id,product) AS (
SELECT 123,3' FROM dual
UNION ALL SELECT 123,5' FROM dual
UNION ALL SELECT 123,6' FROM dual
UNION ALL SELECT 123,6' FROM dual
),i(i) AS (
SELECT 1 FROM dual
UNION ALL SELECT 2 FROM dual
UNION ALL SELECT 3 FROM dual
UNION ALL SELECT 4 FROM dual
UNION ALL SELECT 5 FROM dual
),unpivot AS (
SELECT
id,i,REGEXP_SUBSTR(product,'\d+',i) AS prd
FROM input CROSS JOIN i
WHERE REGEXP_SUBSTR(product,i) <> ''
)
SELECT
*,CASE
WHEN LAG(year_mth) OVER(PARTITION BY id,prd ORDER BY year_mth) IS NULL
THEN 'new'
ELSE 'old'
END
FROM unpivot ORDER BY 3,4;
-- out id | i | year_mth | prd | case
-- out -----+---+----------+-----+------
-- out 123 | 1 | 201901 | 1 | new
-- out 123 | 2 | 201901 | 2 | new
-- out 123 | 3 | 201901 | 3 | new
-- out 123 | 1 | 201902 | 1 | old
-- out 123 | 2 | 201902 | 2 | old
-- out 123 | 3 | 201902 | 4 | new
-- out 123 | 4 | 201902 | 5 | new
-- out 123 | 1 | 201903 | 2 | old
-- out 123 | 2 | 201903 | 3 | old
-- out 123 | 3 | 201903 | 4 | old
-- out 123 | 4 | 201903 | 6 | new
-- out 123 | 1 | 201904 | 1 | old
-- out 123 | 2 | 201904 | 4 | old
-- out 123 | 3 | 201904 | 5 | old
-- out 123 | 4 | 201904 | 6 | old
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。