Oracle 查询查找第一行或第二行
Oracle query to find the first or second row
我有一组记录人员及其条款的数据。我只关心术语 201030、201040 和 201110。所有其他的都将被忽略。有的人会有几条记录,有的人只有一条。我正在写一个查询来获取每个人的第一个学期。如果第一项是 201030,它应该查看下一行。我需要 return 第二行的术语,如果它是 201040 或 201110,否则只是 return 201030。Return 第一行术语,如果它是 201040 或 201110。这是有点混乱所以我试着用不同场景的图像来。绿色框表示我应该保留的记录。
这是我目前的查询。它运行但不应用案例逻辑。它只是 return 的第一学期。我应该如何更改此查询?
select person_id,
min(term) as min_term, -- including this field to verify the 1st term before changes
case
when term = '201030'
then lead(min(term), 1, min(term)) over (order by min(term))
else term
end as term
from my_table
group by person_id
这是一种选择。阅读代码中的注释。
SQL> with test (person_id, term) as
2 -- sample data
3 (select 53303, 201030 from dual union all
4 select 53303, 201040 from dual union all
5 select 53303, 201110 from dual union all
6 select 53303, 201140 from dual union all
7 --
8 select 14627, 201030 from dual union all
9 select 14627, 201110 from dual union all
10 select 14627, 201510 from dual union all
11 --
12 select 14702, 201030 from dual union all
13 --
14 select 28103, 201030 from dual union all
15 select 28103, 201230 from dual union all
16 select 28103, 201240 from dual union all
17 select 28103, 201310 from dual union all
18 --
19 select 33634, 201040 from dual union all
20 select 33634, 201110 from dual union all
21 select 33634, 201130 from dual union all
22 --
23 select 32356, 201510 from dual
24 ),
25 rns as
26 -- select next term and row numbers
27 (select person_id,
28 term,
29 lead(term) over (partition by person_id order by term) next_term,
30 row_number() over (partition by person_id order by term) rn
31 from test
32 )
33 select person_id,
34 max(case when rn = 1 and term = 201030 then
35 case when next_term in (201040, 201110) then next_term
36 else term
37 end
38 when rn = 1 and term in (201040, 201110) then term
39 end) result
40 from rns
41 group by person_id;
PERSON_ID RESULT
---------- ----------
14702 201030
32356
33634 201040
14627 201110
28103 201030
53303 201040
6 rows selected.
SQL>
从 Oracle 12 开始,您可以使用 MATCH_RECOGNIZE
:
SELECT *
FROM (
SELECT *
FROM my_table
WHERE term IN (201030, 201040, 201110)
)
MATCH_RECOGNIZE (
PARTITION BY person_id
ORDER BY term_order
ALL ROWS PER MATCH
PATTERN ( ^ {- IS30? -} ANY_ROW )
DEFINE
IS30 AS term = 201030
)
在此之前,您可以使用解析函数:
SELECT person_id,
CASE
WHEN term = 201030
THEN next_term
ELSE term
END AS term
FROM (
SELECT t.*,
ROW_NUMBER() OVER (PARTITION BY person_id ORDER BY term_order) AS rn,
LEAD(term, 1, term) OVER (PARTITION BY person_id ORDER BY term_order)
AS next_term
FROM my_table t
WHERE term IN (201030, 201040, 201110)
)
WHERE rn = 1;
其中,对于示例数据:
CREATE TABLE my_table (person_id, term, term_order) As
SELECT 14627, 201030, 1 FROM DUAL UNION ALL -- Not this one
SELECT 14627, 201110, 2 FROM DUAL UNION ALL -- This one
SELECT 14627, 201510, 3 FROM DUAL UNION ALL -- Ignore this
SELECT 14702, 201030, 1 FROM DUAL UNION ALL -- This one
SELECT 28103, 201030, 1 FROM DUAL UNION ALL -- This one
SELECT 28103, 201230, 2 FROM DUAL UNION ALL -- Ignore this
SELECT 28103, 201240, 3 FROM DUAL UNION ALL -- Ignore this
SELECT 28103, 201310, 4 FROM DUAL UNION ALL -- Ignore this
SELECT 33634, 201040, 1 FROM DUAL UNION ALL -- This one
SELECT 33634, 201110, 2 FROM DUAL UNION ALL -- Not this one
SELECT 33634, 201130, 3 FROM DUAL UNION ALL -- Ignore this
SELECT 32356, 201510, 1 FROM DUAL UNION ALL -- Ignore this
SELECT 53303, 201030, 1 FROM DUAL UNION ALL -- Not this one
SELECT 53303, 201040, 2 FROM DUAL UNION ALL -- This one.
SELECT 53303, 201110, 3 FROM DUAL UNION ALL -- Not this one
SELECT 53303, 201140, 4 FROM DUAL; -- Ignore this
注意:在 SQL 中,表格是无序的。如果您希望它们按特定顺序排列,那么您需要一些东西来应用该顺序(例如 task_order
列)并使用 ORDER BY
子句。
输出:
PERSON_ID
TERM_ORDER
TERM
14627
2
201110
14702
1
201030
28103
1
201030
33634
1
201040
53303
2
201040
db<>fiddle here
我有一组记录人员及其条款的数据。我只关心术语 201030、201040 和 201110。所有其他的都将被忽略。有的人会有几条记录,有的人只有一条。我正在写一个查询来获取每个人的第一个学期。如果第一项是 201030,它应该查看下一行。我需要 return 第二行的术语,如果它是 201040 或 201110,否则只是 return 201030。Return 第一行术语,如果它是 201040 或 201110。这是有点混乱所以我试着用不同场景的图像来。绿色框表示我应该保留的记录。
这是我目前的查询。它运行但不应用案例逻辑。它只是 return 的第一学期。我应该如何更改此查询?
select person_id,
min(term) as min_term, -- including this field to verify the 1st term before changes
case
when term = '201030'
then lead(min(term), 1, min(term)) over (order by min(term))
else term
end as term
from my_table
group by person_id
这是一种选择。阅读代码中的注释。
SQL> with test (person_id, term) as
2 -- sample data
3 (select 53303, 201030 from dual union all
4 select 53303, 201040 from dual union all
5 select 53303, 201110 from dual union all
6 select 53303, 201140 from dual union all
7 --
8 select 14627, 201030 from dual union all
9 select 14627, 201110 from dual union all
10 select 14627, 201510 from dual union all
11 --
12 select 14702, 201030 from dual union all
13 --
14 select 28103, 201030 from dual union all
15 select 28103, 201230 from dual union all
16 select 28103, 201240 from dual union all
17 select 28103, 201310 from dual union all
18 --
19 select 33634, 201040 from dual union all
20 select 33634, 201110 from dual union all
21 select 33634, 201130 from dual union all
22 --
23 select 32356, 201510 from dual
24 ),
25 rns as
26 -- select next term and row numbers
27 (select person_id,
28 term,
29 lead(term) over (partition by person_id order by term) next_term,
30 row_number() over (partition by person_id order by term) rn
31 from test
32 )
33 select person_id,
34 max(case when rn = 1 and term = 201030 then
35 case when next_term in (201040, 201110) then next_term
36 else term
37 end
38 when rn = 1 and term in (201040, 201110) then term
39 end) result
40 from rns
41 group by person_id;
PERSON_ID RESULT
---------- ----------
14702 201030
32356
33634 201040
14627 201110
28103 201030
53303 201040
6 rows selected.
SQL>
从 Oracle 12 开始,您可以使用 MATCH_RECOGNIZE
:
SELECT *
FROM (
SELECT *
FROM my_table
WHERE term IN (201030, 201040, 201110)
)
MATCH_RECOGNIZE (
PARTITION BY person_id
ORDER BY term_order
ALL ROWS PER MATCH
PATTERN ( ^ {- IS30? -} ANY_ROW )
DEFINE
IS30 AS term = 201030
)
在此之前,您可以使用解析函数:
SELECT person_id,
CASE
WHEN term = 201030
THEN next_term
ELSE term
END AS term
FROM (
SELECT t.*,
ROW_NUMBER() OVER (PARTITION BY person_id ORDER BY term_order) AS rn,
LEAD(term, 1, term) OVER (PARTITION BY person_id ORDER BY term_order)
AS next_term
FROM my_table t
WHERE term IN (201030, 201040, 201110)
)
WHERE rn = 1;
其中,对于示例数据:
CREATE TABLE my_table (person_id, term, term_order) As
SELECT 14627, 201030, 1 FROM DUAL UNION ALL -- Not this one
SELECT 14627, 201110, 2 FROM DUAL UNION ALL -- This one
SELECT 14627, 201510, 3 FROM DUAL UNION ALL -- Ignore this
SELECT 14702, 201030, 1 FROM DUAL UNION ALL -- This one
SELECT 28103, 201030, 1 FROM DUAL UNION ALL -- This one
SELECT 28103, 201230, 2 FROM DUAL UNION ALL -- Ignore this
SELECT 28103, 201240, 3 FROM DUAL UNION ALL -- Ignore this
SELECT 28103, 201310, 4 FROM DUAL UNION ALL -- Ignore this
SELECT 33634, 201040, 1 FROM DUAL UNION ALL -- This one
SELECT 33634, 201110, 2 FROM DUAL UNION ALL -- Not this one
SELECT 33634, 201130, 3 FROM DUAL UNION ALL -- Ignore this
SELECT 32356, 201510, 1 FROM DUAL UNION ALL -- Ignore this
SELECT 53303, 201030, 1 FROM DUAL UNION ALL -- Not this one
SELECT 53303, 201040, 2 FROM DUAL UNION ALL -- This one.
SELECT 53303, 201110, 3 FROM DUAL UNION ALL -- Not this one
SELECT 53303, 201140, 4 FROM DUAL; -- Ignore this
注意:在 SQL 中,表格是无序的。如果您希望它们按特定顺序排列,那么您需要一些东西来应用该顺序(例如 task_order
列)并使用 ORDER BY
子句。
输出:
PERSON_ID TERM_ORDER TERM 14627 2 201110 14702 1 201030 28103 1 201030 33634 1 201040 53303 2 201040
db<>fiddle here