Oracle SQL compare dates less than two days old - sql

Hi guys I need your support how to perform this logic.
I'm stucking currently and i really do not know how to procide further.
Target: compare ref_num, entry_date and status=1
with
ref_num, change_status_date, status 0 (always the last two rows)
Compare, if these dates change_status_date and entry_date are less then 2 days old then update the status value from status=1 to status=2, else If the days are more then 3 days old change to status=0
Any idea how to perfom a correct select sql and update sql?
+--------------+-----------------------+---------------------+----------+
| ref_num | entry_date | change_status_date | status
+--------------+-----------------------+---------------------+----------+
| x326585 | 28/04/2020 16:54:14 | | 1 |
| x326585 | 25/04/2020 13:14:00 | 27/04/2020 23:44:00 | 0 |
| x326585 | 20/04/2020 11:15:02 | 20/04/2020 23:52:01 | 0 |
| A142585 | 28/04/2020 16:55:14 | | 1 |
| A142585 | 26/04/2020 11:54:04 | 27/04/2020 22:54:51 | 0 |
| A142585 | 24/04/2020 10:44:14 | 25/04/2020 13:17:23 | 0 |
| B188532 | 29/04/2020 11:34:41 | | 1 |
| B188532 | 14/04/2020 11:44:24 | 15/05/2020 23:11:10 | 0 |
| B188532 | 11/04/2020 08:34:10 | 13/05/2020 11:44:41 | 0 |
+--------------+-----------------------+---------------------+----------+
END RESULTS:
+--------------+-----------------------+---------------------+----------+
| ref_num | entry_date | change_status_date | status
+--------------+-----------------------+---------------------+----------+
| x326585 | 28/04/2020 16:54:14 | 27/07/2020 23:47:31 | 2 | is less than 3 days (28/04/2020 16:54:14 - 27/04/2020 23:44:00) -> status 2
| x326585 | 25/04/2020 13:14:00 | 27/04/2020 23:44:00 | 0 |
| x326585 | 20/04/2020 11:15:02 | 20/04/2020 23:52:01 | 0 |
| A142585 | 28/04/2020 16:35:58 | 27/07/2020 23:47:31 | 2 | is less than 3 days (28/04/2020 16:35:58 - 27/04/2020 22:54:51) -> status 2
| A142585 | 26/04/2020 11:54:04 | 27/04/2020 22:54:51 | 0 |
| A142585 | 24/04/2020 10:44:14 | 25/04/2020 13:17:23 | 0 |
| B188532 | 29/04/2020 11:34:41 | 27/07/2020 23:47:31 | 0 | is more than 3 days (29/04/2020 11:34:41 - 15/05/2020 23:11:10) -> status 0
| B188532 | 14/04/2020 11:44:24 | 15/05/2020 23:11:10 | 0 |
| B188532 | 11/04/2020 08:34:10 | 13/05/2020 11:44:41 | 0 |
+--------------+-----------------------+---------------------+----------+
select x.ref_num, x.entry_date, x.change_status_date, x.status from kl_table x
Thank you for your support and advice

This is how I understood the question.
Sample data:
SQL> with test (ref_num, entry_date, change_status_date, status) as
2 (select 'x3', to_date('28.04.2020 16:54', 'dd.mm.yyyy hh24:mi'), null , 1 from dual union all
3 select 'x3', to_date('25.04.2020 13:14', 'dd.mm.yyyy hh24:mi'), to_date('27.04.2020 23:44', 'dd.mm.yyyy hh24:mi'), 0 from dual union all
4 select 'x3', to_date('20.04.2020 11:15', 'dd.mm.yyyy hh24:mi'), to_date('20.04.2020 23:52', 'dd.mm.yyyy hh24:mi'), 0 from dual union all
5 --
6 select 'b1', to_date('29.04.2020 11:34', 'dd.mm.yyyy hh24:mi'), null , 1 from dual union all
7 select 'b1', to_date('14.04.2020 11:44', 'dd.mm.yyyy hh24:mi'), to_date('15.05.2020 23:11', 'dd.mm.yyyy hh24:mi'), 0 from dual union all
8 select 'b1', to_date('11.04.2020 08:34', 'dd.mm.yyyy hh24:mi'), to_date('13.05.2020 11:44', 'dd.mm.yyyy hh24:mi'), 0 from dual
9 ),
Max change_status_date for that ref_num whose status = 0; it'll be compared to entry_date
10 temp as
11 (select
12 a.ref_num,
13 a.entry_date,
14 a.change_status_date,
15 --
16 (select max(b.change_status_date)
17 from test b
18 where b.ref_num = a.ref_num
19 and b.status = 0
20 ) compare_change_status_date,
21 a.status
22 from test a
23 )
Finally: I presume that change_status_date (that was NULL) should be replaced by sysdate. Difference between those dates should be ABS to eliminate negative numbers.
24 select
25 t.ref_num,
26 t.entry_date,
27 --
28 nvl(t.change_status_date, sysdate) change_status_date,
29 --
30 case when t.status = 1 then
31 case when abs(t.entry_date - t.compare_change_status_date) < 2 then 2
32 when abs(t.entry_date - t.compare_change_status_date) > 3 then 0
33 end
34 else t.status
35 end status
36 from temp t
37 order by t.ref_num desc, t.entry_date desc;
RE ENTRY_DATE CHANGE_STATUS_DA STATUS
-- ---------------- ---------------- ----------
x3 28.04.2020 16:54 28.07.2020 08:21 2
x3 25.04.2020 13:14 27.04.2020 23:44 0
x3 20.04.2020 11:15 20.04.2020 23:52 0
b1 29.04.2020 11:34 28.07.2020 08:21 0
b1 14.04.2020 11:44 15.05.2020 23:11 0
b1 11.04.2020 08:34 13.05.2020 11:44 0
6 rows selected.
SQL>
If you want to update rows whose status = 1, code I posted above can be reused for e.g. MERGE:
merge into test a
using (with temp
as (select a.ref_num,
a.entry_date,
a.change_status_date,
--
(select max (b.change_status_date)
from test b
where b.ref_num = a.ref_num
and b.status = 0)
compare_change_status_date,
a.status
from test a)
select t.ref_num,
t.entry_date,
--
nvl (t.change_status_date, sysdate) change_status_date,
--
case
when t.status = 1
then
case
when abs (
t.entry_date - t.compare_change_status_date) <
2
then
2
when abs (
t.entry_date
- t.compare_change_status_date) > 3
then
0
end
else
t.status
end
status
from temp t) x
on ( a.ref_num = x.ref_num
and a.entry_date = x.entry_date)
when matched
then
update set a.status = x.status
where a.status = 1;

Your solution will look like this.
update [tablename] set status=2 where DATEDIFF(day, [tablename].entry_date, [tablename].change_status_date) < 2
update [tablename] set status=0 where DATEDIFF(day, [tablename].entry_date, [tablename].change_status_date) > 3
Thanks

Related

Numerate values in a column regardless of order

I have a table like this:
Date
Week
2021-01-01
53
2021-01-02
53
2021-01-03
53
2021-01-04
1
2021-01-05
1
2021-01-06
1
2021-01-07
1
...
...
2021-12-30
52
2021-12-31
52
I want to rank weeks not with their values but with Date ascending order. I tried to use
dense_rank() over (order by Week)
and got this results:
Date
Week
2021-01-01
53
2021-01-02
53
2021-01-03
53
2021-01-04
1
2021-01-05
1
2021-01-06
1
2021-01-07
1
...
...
2021-12-30
52
2021-12-31
52
But 53rd week is on 53rd rank, not 1st as I want. Do you know what I need to use in that case? Thx
You can try to use MOD function in ORDER BY.
Because the Week Number seem like between 1 to 53, MOD function will calculate
MOD(53, 53)=> 0
MOD(1, 53) => 1
so on .... .
dense_rank() over (order by MOD(Week, 53))
use order by desc
select *, row_number()over(order by week desc) from table_name
You can simply play with Vertica's date/time functions - and add #D-Shih 's clever idea with the modulo function to it, and no dense-rank needed if the result is the one you display:
WITH
indata (dt) AS (
SELECT DATE '2020-12-30'
UNION ALL SELECT DATE '2020-12-31'
UNION ALL SELECT DATE '2021-01-01'
UNION ALL SELECT DATE '2021-01-02'
UNION ALL SELECT DATE '2021-01-03'
UNION ALL SELECT DATE '2021-01-04'
UNION ALL SELECT DATE '2021-01-05'
[...]
UNION ALL SELECT DATE '2021-12-30'
UNION ALL SELECT DATE '2021-12-31'
UNION ALL SELECT DATE '2022-01-01'
UNION ALL SELECT DATE '2022-01-02'
UNION ALL SELECT DATE '2022-01-03'
UNION ALL SELECT DATE '2022-01-04'
)
SELECT
dt
, WEEK(dt) AS stdweek
, WEEK_ISO(dt) AS isoweek
, MOD(WEEK(dt),53) AS stdwkmod53
, MOD(WEEK_ISO(dt),53) AS isowkmod53
FROM indata;
-- out dt | stdweek | isoweek | stdwkmod53 | isowkmod53
-- out ------------+---------+---------+------------+------------
-- out 2020-12-30 | 53 | 53 | 0 | 0
-- out 2020-12-31 | 53 | 53 | 0 | 0
-- out 2021-01-01 | 1 | 53 | 1 | 0
-- out 2021-01-02 | 1 | 53 | 1 | 0
-- out 2021-01-03 | 2 | 53 | 2 | 0
-- out 2021-01-04 | 2 | 1 | 2 | 1
-- out 2021-01-05 | 2 | 1 | 2 | 1
[...]
-- out 2021-12-30 | 53 | 52 | 0 | 52
-- out 2021-12-31 | 53 | 52 | 0 | 52
-- out 2022-01-01 | 1 | 52 | 1 | 52
-- out 2022-01-02 | 2 | 52 | 2 | 52
-- out 2022-01-03 | 2 | 1 | 2 | 1
-- out 2022-01-04 | 2 | 1 | 2 | 1

Break periods at the end of the month

SQL Server 2017
CREATE TABLE [TABLE_1]
(
PLAN_NR decimal(28,6) NULL,
START_DATE datetime NULL,
);
INSERT INTO TABLE_1 (PLAN_NR, START_DATE)
VALUES (1,'2020-05-01'), (2,'2020-08-01');
CREATE TABLE [TABLE_2]
(
PLAN_NR decimal(28,6) NULL,
PERIOD_NR decimal(28,6) NOT NULL
);
INSERT INTO TABLE_2 (PLAN_NR, PERIOD_NR)
VALUES (1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (1, 6), (1, 7), (1, 8),
(2, 1), (2, 2), (2, 3), (2, 4), (2, 5), (2, 6), (2, 7), (2, 8);
SQL-FIDDLE-LINK
In TABLE_1 there are plan number and plan start date.
TABLE_2 contains period numbers for each plan number.
I would like to compute the corresponding period start dates:
Each period is exactly 7 days long, unless the period contains a month end. Then the period should be divided into a range before the end of the month up to and including the last day of the month and a range after the end of the month.
The Select:
SELECT
t1.PLAN_NR, t2.PERIOD_NR,
FORMAT(DATEADD (d ,((t2.PERIOD_NR-1)*7) , t1.START_DATE ),'yyyy-MM-dd') START_DATE
FROM
TABLE_1 t1
JOIN
TABLE_2 t2 ON t1.PLAN_NR = t2.PLAN_NR
ORDER BY
t1.PLAN_NR, t2.PERIOD_NR ASC
This returns the start data but without the extra to consider the respective month end:
+---------+-----------+------------+
| PLAN_NR | PERIOD_NR | START_DATE |
+---------+-----------+------------+
| 1 | 1 | 2020-05-01 |
| 1 | 2 | 2020-05-08 |
| 1 | 3 | 2020-05-15 |
| 1 | 4 | 2020-05-22 |
| 1 | 5 | 2020-05-29 |
| 1 | 6 | 2020-06-05 |
| 1 | 7 | 2020-06-12 |
| 1 | 8 | 2020-06-19 |
| 2 | 1 | 2020-08-05 |
| 2 | 2 | 2020-08-12 |
| 2 | 3 | 2020-08-19 |
| 2 | 4 | 2020-08-26 |
| 2 | 5 | 2020-09-01 |
| 2 | 6 | 2020-09-02 |
| 2 | 7 | 2020-09-09 |
| 2 | 8 | 2020-09-16 |
+---------+-----------+------------+
I would like an output like this:
+---------+-----------+----------------------+
| PLAN_NR | PERIOD_NR | START_DATE |
+---------+-----------+----------------------+
| 1 | 1 | 2020-05-01 |
| 1 | 2 | 2020-05-08 |
| 1 | 3 | 2020-05-15 |
| 1 | 4 | 2020-05-22 |
| 1 | 5 | 2020-05-29 |< --- period part before new month
| 1 | 6 | 2020-06-01 |< --- period part after new month
| 1 | 7 | 2020-06-05 |
| 1 | 8 | 2020-06-12 |
| 2 | 1 | 2020-08-05 |
| 2 | 2 | 2020-08-12 |
| 2 | 3 | 2020-08-19 |
| 2 | 4 | 2020-08-26 |< --- period part before new month
| 2 | 5 | 2020-09-01 |< --- period part after new month
| 2 | 6 | 2020-09-02 |
| 2 | 7 | 2020-09-09 |
| 2 | 8 | 2020-09-16 |
+---------+-----------+----------------------+
Use window functions (LEAD / LAG ) to get the start and end of the period ...
SELECT t1.PLAN_NR
, t2.PERIOD_NR
, FORMAT(DATEADD (d ,((t2.PERIOD_NR-1)*7) , t1.START_DATE ),'yyyy-MM-dd') START_DATE
, CASE
WHEN
lead(
FORMAT(DATEADD (d ,((t2.PERIOD_NR-1)*7) , t1.START_DATE ),'yyyy-MM-dd')
) over (partition by
FORMAT(DATEADD (d ,((t2.PERIOD_NR-1)*7) , t1.START_DATE ),'yyyy-MM')
order by t2.period_nr)
IS NULL THEN '< --- period part before new month'
WHEN lag(
FORMAT(DATEADD (d ,((t2.PERIOD_NR-1)*7) , t1.START_DATE ),'yyyy-MM-dd')
) over (partition by
FORMAT(DATEADD (d ,((t2.PERIOD_NR-1)*7) , t1.START_DATE ),'yyyy-MM')
order by t2.period_nr)
IS NULL THEN '< --- period part after new month'
END as period_break
from TABLE_1 t1
join TABLE_2 t2
on t1.PLAN_NR = t2.PLAN_NR
order by t1.PLAN_NR, t2.PERIOD_NR asc
SQL Fiddle
PLAN_NR PERIOD_NR START_DATE period_break
1 1 2020-05-01 < --- period part after new month
1 2 2020-05-08 (null)
1 3 2020-05-15 (null)
1 4 2020-05-22 (null)
1 5 2020-05-29 < --- period part before new month
1 6 2020-06-05 < --- period part after new month
1 7 2020-06-12 (null)
1 8 2020-06-19 < --- period part before new month
2 1 2020-08-01 < --- period part after new month
2 2 2020-08-08 (null)
2 3 2020-08-15 (null)
2 4 2020-08-22 (null)
2 5 2020-08-29 < --- period part before new month
2 6 2020-09-05 < --- period part after new month
2 7 2020-09-12 (null)
2 8 2020-09-19 < --- period part before new month
SELECT
t1.PLAN_NR, t2.PERIOD_NR,
--row_number() over() but what if PERIOD_NR is not consecutive?
t2.PERIOD_NR + SUM(num.n) OVER(PARTITION BY t2.PLAN_NR ORDER BY t2.PERIOD_NR, num.n) AS PERIOD_NR_x,
FORMAT(CASE WHEN num.n = 1 THEN DATEADD(day, 1, EOMONTH(DATEADD (d ,((t2.PERIOD_NR-1)*7) , t1.START_DATE ))) ELSE DATEADD(d ,((t2.PERIOD_NR-1)*7) , t1.START_DATE ) END, 'yyyy-MM-dd') START_DATE
FROM
TABLE_1 t1
JOIN
TABLE_2 t2 ON t1.PLAN_NR = t2.PLAN_NR
CROSS APPLY
(
SELECT 0 AS n
UNION ALL
--new row for month change
SELECT 1 AS n
WHERE DATEDIFF(month, DATEADD(d ,(t2.PERIOD_NR-1)*7 , t1.START_DATE), DATEADD(d ,t2.PERIOD_NR*7 , t1.START_DATE)) = 1
) as num
ORDER BY
t1.PLAN_NR, t2.PERIOD_NR ASC

How to Use Group By on the Basis of Column Value?

I am trying to use Group By but unable to achieve the output
I want to Group by with Date, shift and with Mass.
Data is like :
date | shift | mass
---------+-------+------
01-05-20 | A | 5
01-05-20 | B | 3
01-05-20 | B | 3
02-05-20 | A | 11
02-05-20 | A | 5
02-05-20 | C | 12
02-05-20 | C | 12
02-05-20 | B | 5
OutPut which i want
date | shift | mass>3 | mass>10
---------+-------+--------+--------
01-05-20 | A | 1 | 0
01-05-20 | B | 2 | 0
02-05-20 | A | 1 | 1
02-05-20 | B | 1 | 0
02-05-20 | C | 0 | 2
You can use Conditional Aggregation through GROUPing BY mydate,shift :
SELECT mydate,shift,
SUM(CASE WHEN mass > 3 THEN 1 ELSE 0 END) AS "mass>3",
SUM(CASE WHEN mass >10 THEN 1 ELSE 0 END) AS "mass>10"
FROM t
GROUP BY mydate,shift
ORDER BY mydate,shift;
By the way( as you asked for it within a comment ), you can also use DECODE() function :
SELECT mydate,shift,
SUM(DECODE(SIGN(mass-3),1,1,0)) AS "mass>3",
SUM(DECODE(SIGN(mass-10),1,1,0)) AS "mass>10"
FROM t
GROUP BY mydate,shift
ORDER BY mydate,shift
Demo
SQL> with t(mydate, shift, mass) as
2 (
3 select '01-05-2020', 'A', 5 from dual union all
4 select '01-05-2020', 'B', 3 from dual union all
5 select '01-05-2020', 'B', 3 from dual union all
6 select '02-05-2020', 'A', 11 from dual union all
7 select '02-05-2020', 'A', 5 from dual union all
8 select '02-05-2020', 'C', 12 from dual union all
9 select '02-05-2020', 'C', 12 from dual union all
10 select '02-05-2020', 'B', 5 from dual
11 )
12 SELECT mydate,shift,
13 SUM(CASE WHEN mass >=3 AND
14 mass < 10 THEN 1 ELSE 0 END) AS "mass>=3",
15 SUM(CASE WHEN mass >10 THEN 1 ELSE 0 END) AS "mass>10"
16 FROM t
17 GROUP BY mydate,shift
18 ORDER BY mydate,shift;
MYDATE S mass>=3 mass>10
---------- - ---------- ----------
01-05-2020 A 1 0
01-05-2020 B 2 0
02-05-2020 A 1 1
02-05-2020 B 1 0
02-05-2020 C 0 2
SQL>
SQL>

Cleaning up duplicate chronological values

I am trying to clean up some chronological data to remove duplicate chronological data.
Example Table:
+--------+------------+----------------+
| emp_id | department | effective_date |
+--------+------------+----------------+
| 1 | 50 | 2015-04-01 |
| 1 | 50 | 2015-05-22 |
| 1 | null | 2015-07-04 |
| 1 | null | 2015-07-24 |
| 1 | null | 2015-07-30 |
| 1 | 50 | 2015-09-07 |
| 1 | 50 | 2016-01-16 |
| 1 | null | 2016-04-23 |
| 2 | 60 | 2015-01-20 |
| 2 | 60 | 2015-11-22 |
| 2 | 60 | 2016-07-20 |
| 3 | 50 | 2015-04-02 |
| 3 | 50 | 2015-07-15 |
| 3 | 60 | 2016-01-25 |
+--------+------------+----------------+
As you can see, the same individual with the same department may have the same department but multiple effective_dates. I want to clean this up with a query to only have the first date for each department change. However, I don't want to remove instances where someone went from department 50 to null then back to 50, as those are actual changes in position.
Example Output:
+--------+------------+----------------+
| emp_id | department | effective_date |
+--------+------------+----------------+
| 1 | 50 | 2015-04-01 |
| 1 | null | 2015-07-04 |
| 1 | 50 | 2015-09-07 |
| 1 | null | 2016-04-23 |
| 2 | 60 | 2015-01-20 |
| 3 | 50 | 2015-04-02 |
| 3 | 60 | 2016-01-25 |
+--------+------------+----------------+
How can I achieve this?
My solution is
DECLARE #myTable TABLE (emp_id INT, department INT, effective_date DATE);
INSERT INTO #myTable VALUES
(1, 50 , '2015-04-01'),
(1, 50 , '2015-05-22'),
(1, null, '2015-07-04'),
(1, null, '2015-07-24'),
(1, null, '2015-07-30'),
(1, 50 , '2015-09-07'),
(1, 50 , '2016-01-16'),
(1, null, '2016-04-23'),
(2, 60 , '2015-01-20'),
(2, 60 , '2015-11-22'),
(2, 60 , '2016-07-20'),
(3, 50 , '2015-04-02'),
(3, 50 , '2015-07-15'),
(3, 60 , '2016-01-25')
;WITH T AS (
SELECT *,
RN = ROW_NUMBER() OVER(PARTITION BY emp_id ORDER BY effective_date)
FROM #myTable
)
SELECT T1.emp_id, T1.department, T1.effective_date
FROM
T T1
LEFT JOIN T T2 ON T1.emp_id = T2.emp_id AND T1.RN -1 = T2.RN
WHERE (CASE WHEN ISNULL(T1.department,'') = ISNULL(T2.department,'') THEN 1 ELSE 0 END) = 0
ORDER BY T1.emp_id, T1.RN
Result:
emp_id department effective_date
----------- ----------- --------------
1 50 2015-04-01
1 NULL 2015-07-04
1 50 2015-09-07
1 NULL 2016-04-23
2 60 2015-01-20
3 50 2015-04-02
3 60 2016-01-25
(7 row(s) affected)
For delete the duplicate values:
;WITH T AS (
SELECT *,
RN = ROW_NUMBER() OVER(PARTITION BY emp_id ORDER BY effective_date)
FROM #myTable
)
DELETE T1
FROM
T T1
LEFT JOIN T T2 ON T1.emp_id = T2.emp_id AND T1.RN -1 = T2.RN
WHERE ( CASE
WHEN ISNULL(T1.department,'') <> ISNULL(T2.department,'') THEN 1
ELSE 0 END ) = 0
An alternative for where clause
WHERE ( CASE WHEN T1.department <> T2.department
OR (T1.department IS NULL AND T2.department IS NOT NULL)
OR (T2.department IS NULL AND T1.department IS NOT NULL)
THEN 1 ELSE 0 END ) = 0
This was tougher than expected:
declare #temp as table (emp_id int, department int,effective_date date)
insert into #temp
values
(1,50,'2015-04-01')
, (1,50,'2015-05-22')
, (1, null ,'2015-07-04')
, (1, null ,'2015-07-24')
, (1, null ,'2015-07-30')
, (1,50,'2015-09-07')
, (1,50,'2016-01-16')
, (1, null ,'2016-04-23')
, (2,60,'2015-01-20')
, (2,60,'2015-11-22')
, (2,60,'2016-07-20')
, (3,50,'2015-04-02')
, (3,50,'2015-07-15')
, (3,60,'2016-01-25')
;with cte as
(
--Please not I am changing null to -1 for comparison
select emp_id,isnull(department,-1) department,effective_date
,row_number() over (partition by emp_id order by effective_date) rn
from #temp
)
,cte2 as
(
--Compare to next record
select cte.*
,ctelast.emp_id cte2Emp
,ctelast.department cte2dept
,ctelast.effective_date cte2ED
,isSame = case when cte.department=ctelast.department then 1 else 0 end
from cte
join cte ctelast
on cte.emp_id=ctelast.emp_id and cte.rn = ctelast.rn-1
)
/*
Result of above:
emp_id department effective_date rn cte2Emp cte2dept cte2ED isSame
1 50 2015-04-01 1 1 50 2015-05-22 1
1 50 2015-05-22 2 1 -1 2015-07-04 0
1 -1 2015-07-04 3 1 -1 2015-07-24 1
1 -1 2015-07-24 4 1 -1 2015-07-30 1
1 -1 2015-07-30 5 1 50 2015-09-07 0
1 50 2015-09-07 6 1 50 2016-01-16 1
1 50 2016-01-16 7 1 -1 2016-04-23 0
2 60 2015-01-20 1 2 60 2015-11-22 1
2 60 2015-11-22 2 2 60 2016-07-20 1
3 50 2015-04-02 1 3 50 2015-07-15 1
3 50 2015-07-15 2 3 60 2016-01-25 0
*/
--Now you want both the first record and then any changes
select emp_id,department,effective_date from cte2 where rn=1
union all
select cte2emp,cte2dept,cte2.cte2ED from cte2 where isSame=0
order by 1,3
/*
result:
emp_id department effective_date
1 50 2015-04-01
1 -1 2015-07-04
1 50 2015-09-07
1 -1 2016-04-23
2 60 2015-01-20
3 50 2015-04-02
3 60 2016-01-25
*/

Get first and last of serial group in oracle

I'm trying to select from table (sorted):
+--------+-------+
| Serial | Group |
+--------+-------+
| 0100 | 99 |
| 0101 | 99 |
| 0102 | 99 |
| 096 | 92 |
| 097 | 92 |
| 099 | 93 |
| 23 | 16 |
| 95 | 87 |
| 99 | 90 |
| 100 | 90 |
| 101 | 90 |
| 102 | 90 |
| a | a |
| b | b |
| c | c |
+--------+-------+
and I would like table (first, last and quantity by group):fsdfsdfsdfdsfsdf
+------------+----------+----------+
| fromSerial | toSerial | quantity |
+------------+----------+----------+
| 0100 | 0102 | 3 |
| 096 | 097 | 2 |
| 099 | 099 | 1 |
| 99 | 102 | 4 |
| 23 | 23 | 1 |
| 95 | 95 | 1 |
| a | a | 1 |
| b | b | 1 |
| c | c | 1 |
+------------+----------+----------+
My query
Thanks.
you can use window analytic function row_number to partition the data based on the group column
you can also get number of elements in each partition
you can then do case based aggregation to get the from and to serial number values.
SQL Fiddle Demo
with cte
as
(
select "Serial", "Group", row_number() over ( partition by "Group" order by "Serial" ) as rn,
count(*) over ( partition by "Group") as cnt
from Table1
)
select max(case when rn =1 then "Serial" end) as "FromSerial",
max(case when rn =cnt then "Serial" end) as "ToSerial",
max(cnt) as quantity
from cte
group by "Group"
Use MIN, MAX and GROUP BY.
And, DO NOT use keyword GROUP as column name.
SQL> WITH DATA AS(
2 SELECT '0100' Serial, '99' "GROUP_1" FROM dual UNION ALL
3 SELECT '0101' , '99' FROM dual UNION ALL
4 SELECT '0102' , '99' FROM dual UNION ALL
5 SELECT '096' , '92' FROM dual UNION ALL
6 SELECT '097' , '92' FROM dual UNION ALL
7 SELECT '099', '93' FROM dual UNION ALL
8 SELECT '23' , '16' FROM dual UNION ALL
9 SELECT '95' , '87' FROM dual UNION ALL
10 SELECT '99' , '90' FROM dual UNION ALL
11 SELECT '100' , '90' FROM dual UNION ALL
12 SELECT '101' , '90' FROM dual UNION ALL
13 SELECT '102' , '90' FROM dual UNION ALL
14 SELECT 'A' , 'A' FROM dual UNION ALL
15 SELECT 'b' , 'b' FROM dual UNION ALL
16 SELECT 'c' , 'c' FROM dual
17 )
18 SELECT MIN(serial) fromserial,
19 MAX(Serial) toserial,
20 COUNT(*) quantity
21 FROM DATA
22 GROUP BY group_1
23 ORDER BY fromserial
24 /
FROM TOSE QUANTITY
---- ---- ----------
0100 0102 3
096 097 2
099 099 1
100 99 4
23 23 1
95 95 1
A A 1
b b 1
c c 1
9 rows selected.
SQL>
Try this query :
SELECT grp,
Cast(Min(Cast(serial AS INT)) AS VARCHAR2(30)) fromserial,
Cast(Max(Cast(serial AS INT)) AS VARCHAR2(30)) toserial,
Count(*) quantity
FROM yourtable
WHERE NVL(LENGTH(TRIM(TRANSLATE(serial, '0123456789', ' '))), 0) = 0
GROUP BY grp
UNION
SELECT grp,
Cast(Min(serial) AS VARCHAR2(30)) fromserial,
Cast(Max(serial) AS VARCHAR2(30)) toserial,
Count(*) quantity
FROM yourtable
WHERE NVL(LENGTH(TRIM(TRANSLATE(serial, '0123456789', ' '))), 0) != 0
GROUP BY grp
ORDER BY grp
Sqlfiddle