让我们假设我们有一个像这样的表:
+--------+------------+---------------+----------------+
| Name | Position | Initial Date | Final Date |
+--------+------------+---------------+----------------+
| XXX | 1 | 2016/06/07 | 2016/06/08 |
| XXX | 2 | 2016/06/08 | 2016/06/09 |
| XXX | 3 | 2016/06/09 | 2016/06/10 |
| XXX | 4 | 2016/06/13 | 2016/06/14 |
| XXX | 6 | 2016/06/14 | 2016/06/15 |
| YYY | 1 | 2016/06/02 | 2016/06/03 |
+--------+------------+---------------+----------------+
我想更新它,添加一个新字段,表示组的第一个位置。 形成组的一部分意味着遵循以下规则:
考虑到所有这一切,这应该是结果:
+--------+------------+---------------+----------------+------------+
| Name | Position | Initial Date | Final Date | New field |
+--------+------------+---------------+----------------+------------+
| XXX | 1 | 2016/06/07 | 2016/06/08 | 1 |
| XXX | 2 | 2016/06/08 | 2016/06/09 | 1 |
| XXX | 3 | 2016/06/09 | 2016/06/10 | 1 |
| XXX | 4 | 2016/06/13 | 2016/06/14 | 4 |
| XXX | 6 | 2016/06/14 | 2016/06/15 | 6 |
| YYY | 1 | 2016/06/02 | 2016/06/03 | 1 |
+--------+------------+---------------+----------------+------------+
我只能在2名成员的小组上工作,但我不知道如何在超过2名成员的情况下处理它。
这是我使用的示例代码,显然不适用于大组。
update table1 f1
set f1.new_field = NVL((select f2.position
from table1 f2
where f1.name = f2.name and
f2.position = f1.position+1 and
f1.final_date = f2.initial_date),f1.position);
我应该使用递归查询来解决这个问题吗?在这种情况下,我不知道如何在SQL中实现它。
非常感谢任何帮助!
答案 0 :(得分:4)
您可以使用一系列分析函数来执行此操作,如下所示:
with sample_data as (select 'XXX' name, 1 position, to_date('07/06/2016', 'dd/mm/yyyy') initial_date, to_date('08/06/2016', 'dd/mm/yyyy') final_date from dual union all
select 'XXX' name, 2 position, to_date('08/06/2016', 'dd/mm/yyyy') initial_date, to_date('09/06/2016', 'dd/mm/yyyy') final_date from dual union all
select 'XXX' name, 3 position, to_date('09/06/2016', 'dd/mm/yyyy') initial_date, to_date('10/06/2016', 'dd/mm/yyyy') final_date from dual union all
select 'XXX' name, 4 position, to_date('13/06/2016', 'dd/mm/yyyy') initial_date, to_date('14/06/2016', 'dd/mm/yyyy') final_date from dual union all
select 'XXX' name, 6 position, to_date('14/06/2016', 'dd/mm/yyyy') initial_date, to_date('15/06/2016', 'dd/mm/yyyy') final_date from dual union all
select 'YYY' name, 1 position, to_date('02/06/2016', 'dd/mm/yyyy') initial_date, to_date('03/06/2016', 'dd/mm/yyyy') final_date from dual)
-- end of mimicking a table called "sample_data" containing your data
select name,
position,
initial_date,
final_date,
min(position) over (partition by name, grp_sum) new_field
from (select name,
position,
initial_date,
final_date,
sum(change_grp_required) over (partition by name order by position) grp_sum
from (select name,
position,
initial_date,
final_date,
case when position - lag(position, 1, position) over (partition by name order by position) != 1
or initial_date != lag(final_date, 1, initial_date - 1) over (partition by name order by position) then 1
else 0
end change_grp_required
from sample_data));
NAME POSITION INITIAL_DATE FINAL_DATE NEW_FIELD
---- ---------- ------------ ---------- ----------
XXX 1 2016/06/07 2016/06/08 1
XXX 2 2016/06/08 2016/06/09 1
XXX 3 2016/06/09 2016/06/10 1
XXX 4 2016/06/13 2016/06/14 4
XXX 6 2016/06/14 2016/06/15 6
YYY 1 2016/06/02 2016/06/03 1
最里面的子查询确定当前行和上一行的位置和日期是否相关。如果不是,则输入1,否则输入0。
下一个子查询然后计算这些数字的运行总和 - 这具有为相关行创建相同数字的效果(例如,对于位置1到3为1,对于位置4为2,对于位置6为3),我们可以然后用来反对。
外部查询然后只查找每个名称的最小位置编号和新创建的分组列。
然后,您可以在update
语句中使用此查询来执行实际更新(显然,您不需要初始sample_data
子查询,因为您只需在其余部分中使用table_name直接查询)。
答案 1 :(得分:4)
您可以使用LAG()
和LAST_VALUE()
分析函数获取每个组的初始位置,然后使用MERGE
(而不是UPDATE
)更新表格。
Oracle安装程序:
CREATE TABLE table_name ( Name, Position, Initial_Date, Final_Date ) AS
SELECT 'XXX', 1, DATE '2016-06-07', DATE '2016-06-08' FROM DUAL UNION ALL
SELECT 'XXX', 2, DATE '2016-06-08', DATE '2016-06-09' FROM DUAL UNION ALL
SELECT 'XXX', 3, DATE '2016-06-09', DATE '2016-06-10' FROM DUAL UNION ALL
SELECT 'XXX', 4, DATE '2016-06-13', DATE '2016-06-14' FROM DUAL UNION ALL
SELECT 'XXX', 6, DATE '2016-06-14', DATE '2016-06-15' FROM DUAL UNION ALL
SELECT 'YYY', 1, DATE '2016-06-02', DATE '2016-06-03' FROM DUAL;
ALTER TABLE table_name ADD new_field INT;
更新查询:
MERGE INTO table_name d
USING (
SELECT LAST_VALUE( start_of_group ) IGNORE NULLS
OVER ( PARTITION BY Name ORDER BY position )
AS new_field
FROM (
SELECT name,
position,
CASE WHEN position - 1 = LAG( position )
OVER ( PARTITION BY NAME
ORDER BY position )
AND initial_date = LAG( final_date )
OVER ( PARTITION BY NAME
ORDER BY position )
THEN NULL
ELSE position
END AS start_of_group
FROM table_name t
)
) s
ON ( d.ROWID = s.ROWID )
WHEN MATCHED THEN
UPDATE SET new_field = s.new_field;
<强>输出强>:
SELECT * FROM table_name;
NAME POSITION INITIAL_DATE FINAL_DATE NEW_FIELD
---- ---------- ------------------- ------------------- ----------
XXX 1 2016-06-07 00:00:00 2016-06-08 00:00:00 1
XXX 2 2016-06-08 00:00:00 2016-06-09 00:00:00 1
XXX 3 2016-06-09 00:00:00 2016-06-10 00:00:00 1
XXX 4 2016-06-13 00:00:00 2016-06-14 00:00:00 4
XXX 6 2016-06-14 00:00:00 2016-06-15 00:00:00 6
YYY 1 2016-06-02 00:00:00 2016-06-03 00:00:00 1
答案 2 :(得分:1)
您可以使用窗口功能执行此操作。
select t.*, min(position) over (partition by name, grp) as new_field
from (select t.*,
sum(case when (prev_position = position - 1) and
(prev_final_date = initial_date)
then 0 else 1
end) over (partition by name) as grp
from (select t.*,
lag(position) over (partition by name order by position) as prev_position,
lag(final_date) over (partition by name order by position) as prev_final_date
from t
) t
) t;
基本思想是确定新组是否开始。这是第一次使用lag()
获取&#34;之前&#34;中的数据。行。我在猜测&#34;之前&#34;基于position
(而不是initial_date
)。
然后,当一个小组开始时会创建一个标志 - &#34; 1&#34;对于一个新的团队,&#34; 0&#34;如果不。此标志的累积总和标识一个组。
最外面的查询只是将组中的最小位置指定为新字段。