missing row in oracle - sql

SELECT *
FROM
(
SELECT stat,
SUM(DECODE(visa_country,'US',1,0)) AS USA,
SUM(DECODE(visa_country,'United Kingdom',1,0)) AS UK,
SUM(DECODE(visa_country,'Australia',1,0)) AS Australia,
SUM(DECODE(visa_country,'Canada',1,0)) AS Canada,
SUM(DECODE(visa_country,'Switzerland',1,0)) AS Switzerland,
SUM(DECODE(visa_country,'Singapore',1,0)) AS Singapore,
SUM(DECODE(visa_country,'Sweden',1,0)) AS Sweden,
SUM(DECODE(visa_country,'Netherlands',1,0)) AS Netherlands
FROM
(
SELECT *
FROM
(
SELECT stat,
visa_country,
DECODE(stat,'>= 0 '||'&'||' < 15', 1
,'>= 15 '||'&'||' < 30',2
,'>= 30 '||'&'||' < 45',3
,'>= 45 '||'&'||' < 60',4
,'>= 60 '||'&'||' < 75',5
,'>= 75 '||'&'||' < 90',6
,' >= 90',7) AS gr
FROM (
SELECT end_date,
visa_country,
CASE WHEN END_DATE between 0 and 14 THEN '>= 0 '||'&'||' < 15'
WHEN END_DATE BETWEEN 15 AND 29 THEN '>= 15 '||'&'||' < 30'
WHEN END_DATE BETWEEN 30 AND 44 THEN '>= 30 '||'&'||' < 45'
WHEN END_DATE BETWEEN 45 AND 59 THEN '>= 45 '||'&'||' < 60'
WHEN END_DATE BETWEEN 60 AND 74 THEN '>= 60 '||'&'||' < 75'
WHEN END_DATE BETWEEN 75 AND 89 THEN '>= 75 '||'&'||' < 90'
WHEN END_DATE > 90 THEN ' >= 90'
END AS stat
FROM
(
SELECT visa_country,
GREATEST(
NVL(TO_DATE(VP.WP_VALID_TILL,'DD-MON-YY'),TO_DATE(SYSDATE,'DD-MON-YY')),
NVL(TO_DATE(VP.VISA_VALID_TILL,'DD-MON-YY'),to_DATE(SYSDATE,'DD-MON-YY'))
)
- TO_DATE(SYSDATE,'DD-MON-YY') AS END_DATE
FROM visa_prac AS vp
)
) AS x
WHERE x.end_date > 0
)
) GROUP BY stat
ORDER BY gr
)
This is the code which gives the count of the employees ... But the problem here is that if any count is zero it is not showing that entire row....
Supppose no details falls in the range of >=75 & <90 then that row is not showing up..
Will be grateful for your kind help resolving it.

I think you need to remove the WHERE clause in the END
WHERE x.end_date >0
Because it is filtering out the rows in case of ZERO.
select * from
(
select stat, sum(decode(visa_country,'US',1,0))USA,
sum(decode(visa_country,'United Kingdom',1,0))UK,
sum(decode(visa_country,'Australia',1,0))Australia,
sum(decode(visa_country,'Canada',1,0)) Canada,
sum(decode(visa_country,'Switzerland',1,0))Switzerland,
sum(decode(visa_country,'Singapore',1,0))Singapore,
sum(decode(visa_country,'Sweden',1,0))Sweden,
sum(decode(visa_country,'Netherlands',1,0))Netherlands
from
(
select * FROM
(
SELECT stat,visa_country,DECODE(stat,'>= 0 '||'&'||' < 15',1,'>= 15 '||'&'||' < 30',2,'>= 30 '||'&'||' < 45',3,'>= 45 '||'&'||' < 60',4,'>= 60 '||'&'||' < 75' ,5,'>= 75 '||'&'||' < 90' ,6,' >= 90',7)gr
from (
select end_date,visa_country,case when END_DATE between 0 and 14 THEN '>= 0 '||'&'||' < 15'
when END_DATE BETWEEN 15 AND 29 THEN '>= 15 '||'&'||' < 30'
When END_DATE BETWEEN 30 AND 44 THEN '>= 30 '||'&'||' < 45'
When END_DATE BETWEEN 45 AND 59 THEN '>= 45 '||'&'||' < 60'
When END_DATE BETWEEN 60 AND 74 THEN '>= 60 '||'&'||' < 75'
When END_DATE BETWEEN 75 AND 89 THEN '>= 75 '||'&'||' < 90'
When END_DATE > 90 THEN ' >= 90'
END stat
from (select visa_country,GREATEST(nvl(TO_DATE(VP.WP_VALID_TILL,'DD-MON-YY'),to_DATE(sysdate,'DD-MON-YY')),NVL(TO_DATE(VP.VISA_VALID_TILL,'DD-MON-YY'),to_DATE(sysdate,'DD-MON-YY')))
-to_DATE(sysdate,'DD-MON-YY') AS END_DATE from visa_prac vp )
)x
)
) group by stat
order by gr
)

Related

SQL create age range from birth date values

i want to create a column AGEGROUP with the age ranges:
'under 30'
'30 - 40'
'40 - 50'
'50 - 60'
'60 - 70'
'over 70'
with the values of the current date -BIRTH_DATE from table XSALES.CUSTOMERS
Itried this but output shows:
"FROM keyword not found where expected"
select *,
case
when datediff(now(), BIRTH_DATE) / 365.25 < 30 then 'under 30'
when datediff(now(), BIRTH_DATE) / 365.25 < 41 then '30 - 40'
when datediff(now(), BIRTH_DATE) / 365.25 < 51 then '40 - 50'
when datediff(now(), BIRTH_DATE) / 365.25 < 61 then '50 - 60'
when datediff(now(), BIRTH_DATE) / 365.25 < 71 then '60 - 70'
else 'over 70'
end as AGEGROUP
from XSALES.CUSTOMERS;
Try with:
select *, AGEGROUP =
case
when datediff(Day, BIRTH_DATE, GetDate()) / 365.25 < 30 then 'under 30'
when datediff(Day, BIRTH_DATE, GetDate()) / 365.25 < 41 then '30 - 40'
when datediff(Day, BIRTH_DATE, GetDate()) / 365.25 < 51 then '40 - 50'
when datediff(Day, BIRTH_DATE, GetDate()) / 365.25 < 61 then '50 - 60'
when datediff(Day, BIRTH_DATE, GetDate()) / 365.25 < 71 then '60 - 70'
else 'over 70'
end
from XSALES.CUSTOMERS;

Split month days onto multiple row once that row meet a limit (say each bucket limit is 90)

Sample Data:
date1
date2
fiscal mon
days diff
22-10-21
29-10-21
Oct21
8
30-10-21
26-11-21
Nov21
28
27-11-21
24-12-21
Dec21
28
25-12-21
28-01-22
Jan22
35
29-01-22
25-02-22
Feb22
28
26-02-22
25-03-22
Mar22
28
26-03-22
29-04-22
Apr22
35
30-04-22
27-05-22
May22
28
28-05-22
24-06-22
Jun22
28
25-06-22
29-07-22
Jul22
35
30-07-22
13-08-22
Aug22
15
Required Output:
Month
Oct21
Nov21
Dec21
Jan22
Feb22
Mar22
Apr22
May22
Jun22
Jul22
Aug22
Bkt90
8
28
28
26
Bkt90-180
9
28
28
25
Bkt180+
10
28
28
35
15
With some transformations it could be done like below:
Your Sample Data
WITH
tbl_1 AS
(
Select To_Date('22-10-2021', 'dd-mm-yyyy') "DATE_1", To_Date('29-10-2021', 'dd-mm-yyyy') "DATE_2", 'Oct21' "MON", 8 "DAY_DIFF" From Dual Union All
Select To_Date('30-10-2021', 'dd-mm-yyyy') "DATE_1", To_Date('26-11-2021', 'dd-mm-yyyy') "DATE_2", 'Nov21' "MON", 28 "DAY_DIFF" From Dual Union All
Select To_Date('27-11-2021', 'dd-mm-yyyy') "DATE_1", To_Date('24-12-2021', 'dd-mm-yyyy') "DATE_2", 'Dec21' "MON", 28 "DAY_DIFF" From Dual Union All
Select To_Date('25-12-2021', 'dd-mm-yyyy') "DATE_1", To_Date('28-01-2022', 'dd-mm-yyyy') "DATE_2", 'Jan22' "MON", 35 "DAY_DIFF" From Dual Union All
Select To_Date('29-01-2022', 'dd-mm-yyyy') "DATE_1", To_Date('25-05-2022', 'dd-mm-yyyy') "DATE_2", 'Feb22' "MON", 28 "DAY_DIFF" From Dual Union All
Select To_Date('26-02-2022', 'dd-mm-yyyy') "DATE_1", To_Date('25-03-2022', 'dd-mm-yyyy') "DATE_2", 'Mar22' "MON", 28 "DAY_DIFF" From Dual Union All
Select To_Date('26-03-2022', 'dd-mm-yyyy') "DATE_1", To_Date('29-04-2022', 'dd-mm-yyyy') "DATE_2", 'Apr22' "MON", 35 "DAY_DIFF" From Dual Union All
Select To_Date('30-04-2022', 'dd-mm-yyyy') "DATE_1", To_Date('27-05-2022', 'dd-mm-yyyy') "DATE_2", 'May22' "MON", 28 "DAY_DIFF" From Dual Union All
Select To_Date('28-05-2022', 'dd-mm-yyyy') "DATE_1", To_Date('24-06-2022', 'dd-mm-yyyy') "DATE_2", 'Jun22' "MON", 28 "DAY_DIFF" From Dual Union All
Select To_Date('25-06-2022', 'dd-mm-yyyy') "DATE_1", To_Date('29-07-2022', 'dd-mm-yyyy') "DATE_2", 'Jul22' "MON", 35 "DAY_DIFF" From Dual Union All
Select To_Date('30-07-2022', 'dd-mm-yyyy') "DATE_1", To_Date('13-08-2022', 'dd-mm-yyyy') "DATE_2", 'Aug22' "MON", 15 "DAY_DIFF" From Dual
),
First Transformation (CTE transformed)
1st grouping (STEP)
Cumulativ Diff
Days Diff limitation and over the limits
transformed AS
(
SELECT
DATE_1,
DATE_2,
MON,
DAY_DIFF,
FLOOR(Sum(DAY_DIFF) OVER(PARTITION BY 1 ORDER BY DATE_1 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) / 90) "STEP",
Sum(DAY_DIFF) OVER(PARTITION BY 1 ORDER BY DATE_1 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) "CUM_DIFF",
CASE
WHEN Sum(DAY_DIFF) OVER(PARTITION BY 1 ORDER BY DATE_1 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) > 180
THEN DAY_DIFF - (Sum(DAY_DIFF) OVER(PARTITION BY 1 ORDER BY DATE_1 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) - 180)
WHEN Sum(DAY_DIFF) OVER(PARTITION BY 1 ORDER BY DATE_1 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) > 90
THEN DAY_DIFF - (Sum(DAY_DIFF) OVER(PARTITION BY 1 ORDER BY DATE_1 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) - 90)
ELSE 0
END "DAYS_LIMIT",
CASE
WHEN Sum(DAY_DIFF) OVER(PARTITION BY 1 ORDER BY DATE_1 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) > 180
THEN Sum(DAY_DIFF) OVER(PARTITION BY 1 ORDER BY DATE_1 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) - 180
WHEN Sum(DAY_DIFF) OVER(PARTITION BY 1 ORDER BY DATE_1 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) > 90
THEN Sum(DAY_DIFF) OVER(PARTITION BY 1 ORDER BY DATE_1 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) - 90
ELSE 0
END "DAYS_OVER"
FROM
tbl_1
),
Second Transformation (CTE buckets)
buckets AS
(
SELECT
DATE_1 "DATE_1",
DATE_2 "DATE_2",
MON "MON",
DAY_DIFF "DAY_DIFF",
CUM_DIFF "CUMM_DIFF",
CUM_DIFF * (STEP - FIRST_VALUE(STEP) OVER(PARTITION BY 1 ORDER BY DATE_1 ROWS BETWEEN 1 PRECEDING AND CURRENT ROW)) "CUM_DIFF_STEP",
STEP "STEP",
CASE WHEN DAYS_LIMIT <= 0 THEN DAY_DIFF ELSE DAYS_LIMIT END "DAYS_LIMIT",
FIRST_VALUE(STEP) OVER(PARTITION BY 1 ORDER BY DATE_1 ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) "CHG",
CASE
WHEN CUM_DIFF * (STEP - FIRST_VALUE(STEP) OVER(PARTITION BY 1 ORDER BY DATE_1 ROWS BETWEEN 1 PRECEDING AND CURRENT ROW)) = 0
THEN 0
ELSE
DAYS_OVER
END "DAYS_OVER"
FROM
transformed
ORDER BY DATE_1
),
So far the result is:
-- DATE_1 DATE_2 MON DAY_DIFF CUMM_DIFF CUM_DIFF_STEP STEP DAYS_LIMIT CHG DAYS_OVER
-- --------- --------- ----- ---------- ---------- ------------- ---------- ---------- ---------- ----------
-- 22-OCT-21 29-OCT-21 Oct21 8 8 0 0 8 0 0
-- 30-OCT-21 26-NOV-21 Nov21 28 36 0 0 28 0 0
-- 27-NOV-21 24-DEC-21 Dec21 28 64 0 0 28 0 0
-- 25-DEC-21 28-JAN-22 Jan22 35 99 99 1 26 0 9
-- 29-JAN-22 25-MAY-22 Feb22 28 127 0 1 28 1 0
-- 26-FEB-22 25-MAR-22 Mar22 28 155 0 1 28 1 0
-- 26-MAR-22 29-APR-22 Apr22 35 190 190 2 25 1 10
-- 30-APR-22 27-MAY-22 May22 28 218 0 2 28 2 0
-- 28-MAY-22 24-JUN-22 Jun22 28 246 0 2 28 2 0
-- 25-JUN-22 29-JUL-22 Jul22 35 281 281 3 35 2 101
-- 30-JUL-22 13-AUG-22 Aug22 15 296 0 3 15 3 0
Union All to create additional rows for overlapings (CTE buckets_all)
buckets_all AS
(
SELECT BKT, MON, DAYS_MON
FROM
(
SELECT
DATE_1 "DATE_1",
CASE
WHEN CHG = 0 THEN 'Bkt90'
WHEN CHG = 1 THEN 'Bkt180'
ELSE
'Bkt180+'
END "BKT",
MON "MON",
DAYS_LIMIT "DAYS_MON",
CHG "CHG"
FROM
buckets
UNION ALL
SELECT
DATE_1 "DATE_1",
CASE
WHEN CHG = 0 THEN 'Bkt180'
WHEN CHG = 1 THEN 'Bkt180+'
ELSE
'xxx'
END "BKT",
MON "MON",
DAYS_OVER "DAYS_MON",
CHG + 1 "CHG"
FROM
buckets
WHERE
DAYS_OVER > 0 And
CHG <= 1
ORDER BY DATE_1, CHG
)
)
Now we have the result that we need for final SQL (Pivoting)
-- BKT MON DAYS_MON
-- ------- ----- ----------
-- Bkt90 Oct21 8
-- Bkt90 Nov21 28
-- Bkt90 Dec21 28
-- Bkt90 Jan22 26
-- Bkt180 Jan22 9
-- Bkt180 Feb22 28
-- Bkt180 Mar22 28
-- Bkt180 Apr22 25
-- Bkt180+ Apr22 10
-- Bkt180+ May22 28
-- Bkt180+ Jun22 28
-- Bkt180+ Jul22 35
-- Bkt180+ Aug22 15
Main SQL Pivoting the data
SELECT
*
FROM
buckets_all
PIVOT
(
Max(DAYS_MON)
FOR MON IN('Oct21' "Oct21", 'Nov21' "Nov21", 'Dec21' "Dec21", 'Jan22' "Jan22", 'Feb22' "Feb22", 'Mar22' "Mar22", 'Apr22' "Apr22", 'May22' "May22", 'Jun22' "Jun22", 'Jul22' "Jul22", 'Aug22' "Aug22")
)
ORDER BY
CASE BKT
WHEN 'Bkt90' THEN 0
WHEN 'Bkt180' THEN 1
WHEN 'Bkt180+' THEN 2
END
R e s u l t
-- BKT Oct21 Nov21 Dec21 Jan22 Feb22 Mar22 Apr22 May22 Jun22 Jul22 Aug22
-- ------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
-- Bkt90 8 28 28 26
-- Bkt180 9 28 28 25
-- Bkt180+ 10 28 28 35 15

How to extract the time from a timestamp without timezone column?

I want to calculate the number of orders per each time interval for each day.
The format of the date is timestamp without timezone. I can't seem to extract only the time. I use this query for each day, but is there a way to have the time intervals for each day in the month in one table?
CASE WHEN date_created_utc >= timestamp '2020-09-01 08:00:00' AND date_created_utc <= timestamp '2020-09-01 11:00:00' THEN 'Q1'
WHEN date_created_utc >= timestamp '2020-09-01 11:00:01' AND date_created_utc <= timestamp '2020-09-01 14:00:00' THEN 'Q2'
WHEN date_created_utc >= timestamp '2020-09-01 14:00:01' AND date_created_utc <= timestamp '2020-09-01 16:00:00' THEN 'Q3'
WHEN date_created_utc >= timestamp '2020-09-01 16:00:01' AND date_created_utc <= timestamp '2020-09-01 20:00:00' THEN 'Q4'
WHEN date_created_utc >= timestamp '2020-09-01 20:00:01' AND date_created_utc <= timestamp '2020-09-01 23:59:00' THEN 'Q5'
END AS interval,
COUNT(id) as cnt
FROM order_processing
GROUP BY 1;
The desired output table:
Day Q1 Q2 Q3 Q4 Q5
1 28 57 50 65 27
2 23 50 60 90 66
3 58 60 80 70 67
You can convert to a time and then use comparisons. For aggregation:
COUNT(*) FILTER (WHERE date_created_utc::time >= '08:00:00' and date_created_utc::time < '11:00:00') as cnt_1
You just need the hour part to implement the logic: you can use extract():
select
date_created_utc::date day,
count(*) filter(where extract(hour from date_created_utc) between 8 and 10) q1,
count(*) filter(where extract(hour from date_created_utc) between 11 and 14) q2,
...
from order_processing
group by date_created_utc::date

select to group date and time in 5-minute intervals

I wish count the rows of a table grouped by date and time intervals of 5 minutes:
for example, if the minutes portion of HH:MM falls between 00 mins and 04 min it will be counted as 00, eg. 08:04 will be counted as 08:00
if the minutes portion falls between 05 mins and 09 mins it will be counted as 05, eg. 08:06 will be counted as 08:05
Table Data
Date Time
18/01/18 08:00
18/01/18 08:01
18/01/18 08:02
18/01/18 08:03
18/01/18 08:04
18/01/18 08:05
18/01/18 08:06
18/01/18 08:08
18/01/18 08:10
19/01/18 17:01
19/01/18 17:03
19/01/18 17:04
Expected Output
DATE TIME COUNT
18/01/2018 08:00 5
18/01/2018 08:05 3
18/01/2018 08:10 1
19/01/2018 17:00 3
Table Creation
create table TAB1 (tDATE DATE,tTIME VARCHAR2(5));
Data
insert into TAB1(tDATE,tTIME) values (to_date('18/01/2018','DD/MM/YYYY'),'08:00');
insert into TAB1(tDATE,tTIME) values (to_date('18/01/2018','DD/MM/YYYY'),'08:01');
insert into TAB1(tDATE,tTIME) values (to_date('18/01/2018','DD/MM/YYYY'),'08:02');
insert into TAB1(tDATE,tTIME) values (to_date('18/01/2018','DD/MM/YYYY'),'08:03');
insert into TAB1(tDATE,tTIME) values (to_date('18/01/2018','DD/MM/YYYY'),'08:04');
insert into TAB1(tDATE,tTIME) values (to_date('18/01/2018','DD/MM/YYYY'),'08:05');
insert into TAB1(tDATE,tTIME) values (to_date('18/01/2018','DD/MM/YYYY'),'08:06');
insert into TAB1(tDATE,tTIME) values (to_date('18/01/2018','DD/MM/YYYY'),'08:08');
insert into TAB1(tDATE,tTIME) values (to_date('18/01/2018','DD/MM/YYYY'),'08:10');
insert into TAB1(tDATE,tTIME) values (to_date('19/01/2018','DD/MM/YYYY'),'17:01');
insert into TAB1(tDATE,tTIME) values (to_date('19/01/2018','DD/MM/YYYY'),'17:03');
insert into TAB1(tDATE,tTIME) values (to_date('19/01/2018','DD/MM/YYYY'),'17:04');
Whenever I need such interval I use this generic function:
CREATE OR REPLACE FUNCTION MakeInterval(ts IN TIMESTAMP, roundInterval IN INTERVAL DAY TO SECOND) RETURN TIMESTAMP DETERMINISTIC IS
denom INTEGER;
BEGIN
IF roundInterval >= INTERVAL '1' HOUR THEN
denom := EXTRACT(HOUR FROM roundInterval);
IF MOD(24, denom) <> 0 THEN
RAISE VALUE_ERROR;
END IF;
RETURN TRUNC(ts) + TRUNC(EXTRACT(HOUR FROM ts) / denom) * denom * INTERVAL '1' HOUR;
ELSIF roundInterval >= INTERVAL '1' MINUTE THEN
denom := EXTRACT(MINUTE FROM roundInterval);
IF MOD(60, denom) <> 0 THEN
RAISE VALUE_ERROR;
END IF;
RETURN TRUNC(ts, 'hh') + TRUNC(EXTRACT(MINUTE FROM ts) / denom) * denom * INTERVAL '1' MINUTE;
ELSE
denom := EXTRACT(SECOND FROM roundInterval);
IF MOD(60, denom) <> 0 THEN
RAISE VALUE_ERROR;
END IF;
RETURN TRUNC(ts, 'mi') + TRUNC(EXTRACT(SECOND FROM ts) / denom) * denom * INTERVAL '1' SECOND;
END IF;
END MakeInterval;
Valid intervals are: 1,2,3,4,5,6,10,12,15,20,30,60 SECOND, 1,2,3,4,5,6,10,12,15,20,30,60 MINUTE, 1,2,3,4,6,8,12 HOUR
You store time in a separate column which is a bad design. First, make a proper DATE or TIMESTAMP value, for example: TO_DATE(TO_CHAR(tDATE,'YYYY-MM-DD')||tTIME, 'YYYY-MM-DDHH24:MI')
Then you could use it like this
SELECT
MakeInterval(TO_DATE(TO_CHAR(tDATE,'YYYY-MM-DD')||tTIME, 'YYYY-MM-DDHH24:MI'), INTERVAL '5' MINUTE), ...
Of course, if you don't like to use a separate function you can put all in one line:
TRUNC(TO_DATE(TO_CHAR(tDATE,'YYYY-MM-DD')||tTIME, 'YYYY-MM-DDHH24:MI'), 'hh') + TRUNC(EXTRACT(MINUTE FROM TO_DATE(TO_CHAR(tDATE,'YYYY-MM-DD')||tTIME, 'YYYY-MM-DDHH24:MI')) / 5) * INTERVAL '5' MINUTE;
This is a bit cumbersome in Oracle, but quite feasible with string arithmetic:
select date,
substring(time, 1, 3) || lpad(floor(cast(substring(time, -2) as number) / 12) * 12, 2, '0') as time,
count(*)
from tab1
group by date,
substring(time, 1, 3) || lpad(floor(cast(substring(time, -2) as number) / 12) * 12, 2, '0')
order by date, time;
try this,
select tdate,
SUBSTR(ttime, 1, 2)||':'||
LPAD(NVL(DECODE(SIGN(ROUND(SUBSTR(ttime, 4, 2), -1)-SUBSTR(ttime, 4, 2)),
1, ROUND(SUBSTR(ttime, 4, 2), -1)-5,
-1, ROUND(SUBSTR(ttime, 4, 2), -1)), 0),2, '0') time_,
count(1)
from tab1
group by tdate, SUBSTR(ttime, 1, 2)||':'||
LPAD(NVL(DECODE(SIGN(ROUND(SUBSTR(ttime, 4, 2), -1)-SUBSTR(ttime, 4, 2)),
1, ROUND(SUBSTR(ttime, 4, 2), -1)-5,
-1, ROUND(SUBSTR(ttime, 4, 2), -1)), 0),2, '0');
Thank you all. As to why there is a separate VARCHAR2 column for the time component, the tables were originally migrated from some legacy database that had a date type but without a time component the latter which was stored as a string. Here is my own idea which gives me exactly what I want:
select tDATE,substr(tTIME,1,3)||
case
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 0 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 5 then '00'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 5 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 10 then '05'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 10 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 15 then '10'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 15 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 20 then '15'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 20 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 25 then '20'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 25 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 30 then '25'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 30 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 35 then '30'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 35 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 40 then '35'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 40 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 45 then '40'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 45 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 50 then '45'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 50 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 55 then '50'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 55 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 60 then '55'
else '00'
end as tTIME
,count(*)
from TAB1
group by tDATE,substr(tTIME,1,3)||
case
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 0 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 5 then '00'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 5 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 10 then '05'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 10 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 15 then '10'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 15 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 20 then '15'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 20 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 25 then '20'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 25 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 30 then '25'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 30 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 35 then '30'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 35 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 40 then '35'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 40 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 45 then '40'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 45 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 50 then '45'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 50 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 55 then '50'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 55 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 60 then '55'
else '00'
end
order by 3 desc;

a sql query to compare results of following queries

SELECT pd_end_dt,SUM(nrx_cnt) Total_Count
FROM wkly_lnd.lnd_wkly_plan_rx_summary
WHERE pd_end_dt >= '01-Sep-08' AND pd_end_dt < '30-Sep-08'
GROUP BY pd_end_dt
SELECT pd_end_dt,SUM(nrx_cnt) Total_Count
FROM wkly_lnd.lnd_wkly_plan_rx_summary
WHERE pd_end_dt >= '01-Sep-07' AND pd_end_dt < '30-Sep-07'
GROUP BY pd_end_dt
the result set on running each query will be like
09/28/2007 00:00:00 702,457.36
09/21/2007 00:00:00 703,604.59
09/07/2007 00:00:00 636,619.92
09/14/2007 00:00:00 698,082.03
similarly for previous year
I need to calculate the difference of units sold as compared to last year and also to add one column which will find percentage change
There are a lot of things unsaid. I hope you receive clearer requirements in your daily work ...
Anyway, here is a simulation of your situation. It's based on the assumption that the days with data (one per week) are the same in 2007 as in 2008:
SQL> create table lnd_wkly_plan_rx_summary (pd_end_dt,nrx_cnt)
2 as
3 select date '2008-09-07', 100000 from dual union all
4 select date '2008-09-07', 536619.92 from dual union all
5 select date '2008-09-14', 698082.03 from dual union all
6 select date '2008-09-21', 403604.59 from dual union all
7 select date '2008-09-21', 200000 from dual union all
8 select date '2008-09-21', 100000 from dual union all
9 select date '2008-09-28', 702457.36 from dual union all
10 select date '2007-09-07', 400000 from dual union all
11 select date '2007-09-14', 450000 from dual union all
12 select date '2007-09-21', 500000 from dual union all
13 select date '2007-09-28', 550000 from dual union all
14 select date '2007-09-28', 100000 from dual
15 /
Tabel is aangemaakt.
And your original queries, slightly modified.
SQL> SELECT pd_end_dt
2 , SUM(nrx_cnt) Total_Count
3 FROM lnd_wkly_plan_rx_summary
4 WHERE pd_end_dt >= date '2008-09-01'
5 AND pd_end_dt < date '2008-09-30'
6 GROUP BY pd_end_dt
7 /
PD_END_DT TOTAL_COUNT
------------------- -----------
07-09-2008 00:00:00 636619,92
14-09-2008 00:00:00 698082,03
21-09-2008 00:00:00 703604,59
28-09-2008 00:00:00 702457,36
4 rijen zijn geselecteerd.
SQL> SELECT pd_end_dt
2 , SUM(nrx_cnt) Total_Count
3 FROM lnd_wkly_plan_rx_summary
4 WHERE pd_end_dt >= date '2007-09-01'
5 AND pd_end_dt < date '2007-09-30'
6 GROUP BY pd_end_dt
7 /
PD_END_DT TOTAL_COUNT
------------------- -----------
07-09-2007 00:00:00 400000
14-09-2007 00:00:00 450000
21-09-2007 00:00:00 500000
28-09-2007 00:00:00 650000
4 rijen zijn geselecteerd.
And the query with which you can compare the 2007 and 2008 data:
SQL> select to_char(pd_end_dt,'dd-mm') day_and_month
2 , sum(case trunc(pd_end_dt,'yyyy') when date '2007-01-01' then nrx_cnt end) sum2007
3 , sum(case trunc(pd_end_dt,'yyyy') when date '2008-01-01' then nrx_cnt end) sum2008
4 , sum(case trunc(pd_end_dt,'yyyy') when date '2008-01-01' then nrx_cnt end)
5 - sum(case trunc(pd_end_dt,'yyyy') when date '2007-01-01' then nrx_cnt end) difference
6 , ( sum(case trunc(pd_end_dt,'yyyy') when date '2008-01-01' then nrx_cnt end)
7 - sum(case trunc(pd_end_dt,'yyyy') when date '2007-01-01' then nrx_cnt end)
8 ) / sum(case trunc(pd_end_dt,'yyyy') when date '2008-01-01' then nrx_cnt end) * 100 percentage_difference
9 from lnd_wkly_plan_rx_summary
10 where ( ( pd_end_dt >= date '2007-09-01'
11 and pd_end_dt < date '2007-09-30'
12 )
13 or ( pd_end_dt >= date '2008-09-07'
14 and pd_end_dt < date '2008-09-30'
15 )
16 )
17 group by to_char(pd_end_dt,'dd-mm')
18 /
DAY_A SUM2007 SUM2008 DIFFERENCE PERCENTAGE_DIFFERENCE
----- ---------- ---------- ---------- ---------------------
07-09 400000 636619,92 236619,92 37,1681615
14-09 450000 698082,03 248082,03 35,5376617
21-09 500000 703604,59 203604,59 28,9373595
28-09 650000 702457,36 52457,36 7,46769313
4 rijen zijn geselecteerd.
Although rather verbose, I think it speaks for itself.
You may like the following rewrite, since it doesn't repeat the aggregate functions as much as in the query above:
SQL> select day_and_month
2 , sum2007
3 , sum2008
4 , sum2008-sum2007 difference
5 , 100*(sum2008-sum2007)/sum2008 percentage_difference
6 from ( select to_char(pd_end_dt,'dd-mm') day_and_month
7 , sum(case trunc(pd_end_dt,'yyyy') when date '2007-01-01' then nrx_cnt end) sum2007
8 , sum(case trunc(pd_end_dt,'yyyy') when date '2008-01-01' then nrx_cnt end) sum2008
9 from lnd_wkly_plan_rx_summary
10 where ( pd_end_dt >= date '2007-09-01'
11 and pd_end_dt < date '2007-09-30'
12 )
13 or ( pd_end_dt >= date '2008-09-07'
14 and pd_end_dt < date '2008-09-30'
15 )
16 group by to_char(pd_end_dt,'dd-mm')
17 )
18 /
DAY_A SUM2007 SUM2008 DIFFERENCE PERCENTAGE_DIFFERENCE
----- ---------- ---------- ---------- ---------------------
07-09 400000 636619,92 236619,92 37,1681615
14-09 450000 698082,03 248082,03 35,5376617
21-09 500000 703604,59 203604,59 28,9373595
28-09 650000 702457,36 52457,36 7,46769313
4 rijen zijn geselecteerd.
Hope this helps.
Regards,
Rob.
if you want to compare the results of the query year-on-year (ie for each day with the day of the preceeding year), you can group by the day of the year to_char('dd-mon'):
SQL> WITH lnd_wkly_plan_rx_summary AS (
2 SELECT DATE '2007-09-28' pd_end_dt, 702457.36 nrx_cnt FROM dual
3 UNION ALL SELECT DATE '2007-09-21', 703604.59 FROM dual
4 --
5 UNION ALL SELECT DATE '2008-09-28' pd_end_dt, 702557.36 nrx_cnt FROM dual
6 UNION ALL SELECT DATE '2008-09-21', 703404.59 FROM dual
7 )
8 SELECT to_char(pd_end_dt, 'dd-mon') pd_end_dt,
9 SUM(CASE
10 WHEN to_char(pd_end_dt, 'yyyy') = '2007' THEN
11 nrx_cnt
12 END) Total_2007,
13 SUM(CASE
14 WHEN to_char(pd_end_dt, 'yyyy') = '2008' THEN
15 nrx_cnt
16 END) Total_2008,
17 SUM(CASE
18 WHEN to_char(pd_end_dt, 'yyyy') = '2008' THEN
19 nrx_cnt
20 ELSE
21 -nrx_cnt
22 END) delta
23 FROM lnd_wkly_plan_rx_summary
24 WHERE ((pd_end_dt >= DATE '2007-09-01' AND pd_end_dt < DATE '2007-09-30') OR
25 (pd_end_dt >= DATE '2008-09-01' AND pd_end_dt < DATE '2008-09-30'))
26 GROUP BY to_char(pd_end_dt, 'dd-mon');
PD_END_DT TOTAL_2007 TOTAL_2008 DELTA
------------ ---------- ---------- ----------
28-sep 702457,36 702557,36 100
21-sep 703604,59 703404,59 -200
Why dont you look at the analytic functions provided with Oracle? I am assuming you are using Oracle as you have tagged your question with the Oracle tag. You can refer to http://www.oracle-base.com/articles/misc/LagLeadAnalyticFunctions.php.
I am simplifying your data set to look like this
09/08/2007 100
09/08/2008 200
09/09/2007 350
09/09/2008 400
09/10/2007 150
09/10/2008 175
These are your total counts on the 8,9 and 10th of September in the years 2007 and 2008
You can use the following query:
Assuming table to be T(end_date,cnt) (your names are too long! sorry )
Select end_date, cnt,
LAG(cnt,1,0) over (order by
to_number(to_char(end_dt,'dd')),to_number(to_char(end_dt,'mm'))) cntPrev,
cnt - LAG(cnt,1,0) over (order by
to_number(to_char(end_dt,'dd')),to_number(to_char(end_dt,'mm'))) cntDiff
from T
In simpler terms(this will not work if you copy, paste).
Let X=LAG(cnt,1,0) over (order by
to_number(to_char(end_dt,'dd')),to_number(to_char(end_dt,'mm')))
Your query is
Select end_date, cnt, X cntPrev, cnt-X cntDiff
from T;