I want cummulative row for a given input - sql

I have table like below
Months cnt
Jan 2
Feb 3
Mar 5
I want output like below
Months cnt
Jan 2
Feb 2
Feb 3
Mar 2
Mar 3
Mar 5
I tried using below query but not getting the required output
Select distinct months, cnt, level
from (select months, cnt, rownum row_cnt
from tablename)
connect by level <= row_cnt
Order by months, cnt, level

Here's one option which converts month's names into their ordinal number (1 for Jan, 2 for Feb, etc.) and then - using self join - returns the result.
SQL> with test (months, cnt) as
2 (select 'jan', 2 from dual union all
3 select 'feb', 3 from dual union all
4 select 'mar', 5 from dual
5 ),
6 temp as
7 (select
8 months,
9 to_number(to_char(to_date(months, 'mon', 'nls_date_language=english'), 'mm')) mon,
10 cnt
11 from test
12 )
13 select a.months, b.cnt
14 from temp a join temp b on a.mon >= b.mon
15 order by a.mon, b.cnt;
MON CNT
--- ----------
jan 2
feb 2
feb 3
mar 2
mar 3
mar 5
6 rows selected.
SQL>

You need a self join:
select t.months, tt.cnt
from tablename t inner join tablename tt
on extract(month from to_date(t.Months,'MON')) >= extract(month from to_date(tt.Months,'MON'))
order by extract(month from to_date(t.Months,'MON')), tt.cnt
See the demo.
Results:
> MONTHS | CNT
> :----- | --:
> Jan | 2
> Feb | 2
> Feb | 3
> Mar | 2
> Mar | 3
> Mar | 5

Related

ORACLE: Splitting a string into multiple rows

I am trying to split a string "HHHWWWHHHHWWWWWHHWWWWWHHWWWWWHH"
is there any possibility to make like :
H
H
H
W
W
W
BRANCH_CODE YEAR MONTH HOLIDAY_LIST
1 001 2021 1 HHHWWWHHHHWWWWWHHWWWWWHHWWWWWHH
2 001 2021 2 WWWWWHHWWWWWHHWWWWWHHWHWWWHH
From Oracle 12, you can use:
SELECT branch_code, year, month, day, holiday
FROM branches
CROSS JOIN LATERAL (
SELECT LEVEL AS day,
SUBSTR(holiday_list, LEVEL, 1) AS holiday
FROM DUAL
CONNECT BY LEVEL <= LENGTH(holiday_list)
)
Which, for the sample data:
CREATE TABLE branches (BRANCH_CODE, YEAR, MONTH, HOLIDAY_LIST) AS
SELECT '001', 2021, 1, 'HHHWWWHHHHWWWWWHHWWWWWHHWWWWWHH' FROM DUAL UNION ALL
SELECT '001', 2021, 2, 'WWWWWHHWWWWWHHWWWWWHHWHWWWHH' FROM DUAL
Outputs:
BRANCH_CODE
YEAR
MONTH
DAY
HOLIDAY
001
2021
1
1
H
001
2021
1
2
H
001
2021
1
3
H
001
2021
1
4
W
...
...
...
...
...
001
2021
1
29
W
001
2021
1
30
H
001
2021
1
31
H
001
2021
2
1
W
001
2021
2
2
W
001
2021
2
3
W
...
...
...
...
...
001
2021
2
26
W
001
2021
2
27
H
001
2021
2
28
H
db<>fiddle here
If it's Oracle:
with data AS (
select 'WWWWWHHWWWWWHHWWWWWHHWHWWWHH' AS letters
from dual
)
select substr (
letters,
level,
1
) value
from data
connect by level <=
length ( letters )

Counts and divide from two different selects with dates

I have a table with this kind of structure (Sample only)
ID | STATUS | DATE |
--- -------- ------
1 OPEN 31-01-2022
2 CLOSE 15-11-2021
3 CLOSE 21-10-2021
4 OPEN 11-10-2021
5 OPEN 28-09-2021
I would like to know the counts of close vs open records by week. So it will be count(close)/count(open) where close.week = open.week
If there are no matching values, need to return 0 of course.
I got to this query below
SELECT *
FROM
(SELECT COUNT(*) AS 'CLOSE', DATEPART(WEEK, DATE) AS 'WEEKSA', DATEPART(YEAR, DATE) AS 'YEARA' FROM TABLE
WHERE STATUS IN ('CLOSE')
GROUP BY DATEPART(WEEK, DATE),DATEPART(YEAR, DATE)) TMPA
FULL OUTER JOIN
(SELECT COUNT(*) AS 'OPEN', DATEPART(WEEK, DATE) AS 'WEEKSB', DATEPART(YEAR, DATE) AS 'YEARB' FROM TABLE
WHERE STATUS IN ('OPEN')
GROUP BY DATEPART(WEEK, DATE),DATEPART(YEAR, DATE)) TMPB
ON TMPA.WEEKSA = TMPB.WEEKSB AND TMPA.YEARA = TMPB.YEARB
My results are as below (sample only)
close | weeksa | yeara | open | weeksb | yearb |
------ -------- ------ ------- ------- ------
3 2 2021
1 3 2021
1 4 2021
2 20 2021 2 20 2021
7 22 2021
2 23 2021
7 26 2021
7 27 2021
2 28 2021 14 28 2021
2 29 2021
10 30
24 31 2021
2 32 2021 5 32
4 33 2021
1 34 2021 13 34 2021
6 35 2021
1 36 2021
1 38 2021
1 39 2021
2 41 2021
4 43 2021
1 45 2021
2 46 2021 25 46 2021
1 47 2021 5 47 2021
4 48 2021
1 49 2021 20 49 2021
1 50 2021 17 50 2021
1 51 2021
How do I do the math now?
If I do another select the query fails. So I guess either syntax is bad or the whole concept is wrong.
The required result should look like this (Sample)
WEEK | YEAR | RATIO |
----- ------ -------
2 2021 0
3 2021 0
4 2021 0
5 2021 0.93
20 2021 0.1
22 2021 0
23 2021 0
26 2021 0
1 2022 0.75
2 2022 0.23
4 2022 0.07
Cheers!
I have added some test data to check the logic, adding the same in the code.
;with cte as(
select 1 ID, 'OPEN' as STATUS, cast('2021 -01-31' as DATE) DATE
union select 10 ID, 'CLOSE' as STATUS, cast('2021 -01-31' as DATE) DATE
union select 11 ID, 'CLOSE' as STATUS, cast('2021 -01-31' as DATE) DATE
union select 12 ID, 'CLOSE' as STATUS, cast('2021 -01-31' as DATE) DATE
union select 22 ID, 'CLOSE' as STATUS, cast('2021 -01-31' as DATE) DATE
union select 32 ID, 'CLOSE' as STATUS, cast('2021 -01-31' as DATE) DATE
union select 2,'CLOSE',cast('2021-11-28' as DATE)
union select 3,'CLOSE',cast('2021-10-21' as DATE)
union select 8,'CLOSE',cast('2021-10-21' as DATE)
union select 9,'CLOSE',cast('2021-10-21' as DATE)
union select 4,'OPEN', cast('2021-10-11' as DATE)
union select 5,'CLOSE', cast('2021-09-28' as DATE)
union select 6,'OPEN', cast('2021-09-27' as DATE)
union select 7,'CLOSE', cast('2021-09-26' as DATE) )
, cte2 as (
select DATEPART(WEEK,date) as week_number,* from cte)
,cte3 as(
select week_number,year(date) yr,count(case when status = 'open' then 1 end)open_count,count(case when status <> 'open' then 1 end) close_count from cte2 group by week_number,year(date))
select week_number as week,yr as year,
cast(case when open_count = 0 then 1.0 else open_count end /
case when close_count = 0 then 1.0 else close_count end as numeric(3,2)) as ratio
from cte3

Generate a range of records depending on from-to dates

I have a table of records like this:
Item
From
To
A
2018-01-03
2018-03-16
B
2021-05-25
2021-11-10
The output of select should look like:
Item
Month
Year
A
01
2018
A
02
2018
A
03
2018
B
05
2021
B
06
2021
B
07
2021
B
08
2021
Also the range should not exceed the current month. In example above we are asuming current day is 2021-08-01.
I am trying to do something similar to THIS with CONNECT BY LEVEL but as soon as I also select my table next to dual and try to order the records the selection never completes. I also have to join few other tables to the selection but I don't think that would make a difference.
I would very much appreciate your help.
Row generator it is, but not as you did it; most probably you're missing lines #11 - 16 in my query (or their alternative).
SQL> with test (item, date_from, date_to) as
2 -- sample data
3 (select 'A', date '2018-01-03', date '2018-03-16' from dual union all
4 select 'B', date '2021-05-25', date '2021-11-10' from dual
5 )
6 -- query that returns desired result
7 select item,
8 extract(month from (add_months(date_from, column_value - 1))) month,
9 extract(year from (add_months(date_from, column_value - 1))) year
10 from test cross join
11 table(cast(multiset
12 (select level
13 from dual
14 connect by level <=
15 months_between(trunc(least(sysdate, date_to), 'mm'), trunc(date_from, 'mm')) + 1
16 ) as sys.odcinumberlist))
17 order by item, year, month;
ITEM MONTH YEAR
----- ---------- ----------
A 1 2018
A 2 2018
A 3 2018
B 5 2021
B 6 2021
B 7 2021
B 8 2021
7 rows selected.
SQL>
Recursive CTEs are the standard SQL approach to this type of problem. In Oracle, this looks like:
with cte(item, fromd, tod) as (
select item, fromd, tod
from t
union all
select item, add_months(fromd, 1), tod
from cte
where add_months(fromd, 1) < last_day(tod)
)
select item, extract(year from fromd) as year, extract(month from fromd) as month
from cte
order by item, fromd;
Here is a db<>fiddle.

how to create column based on other column in sql?

id year
1 2017
1 2018
1 2019
2 2018
2 2019
3 2017
3 2019
8 2017
4 2018
4 2019
I need to create column based on id and year column:
if a id present in 2017 and 2018 (subsequent year) then mark 'P' against 2017.
if a id present in 2018 and 2019 then mark 'P' then mark 'P' against 2017.
if a id present in 2017 but not in subsequent year then mark 'N' against 2017
If there is no data of subsequent year then mark 'N' in the previous year (2019)
output :
id year mark
1 2017 P
1 2018 P
1 2019 N
2 2018 P
2 2019 N
3 2017 N
3 2019 N
8 2017 P
4 2018 P
4 2019 N
You can try Lead() function. but please check output for Id = 8. Ideally it should be 'N'
SELECT *
,CASE WHEN LEAD(Year) OVER (PARTITION BY ID ORDER BY YEAR) - YEAR = 1 THEN 'P' ELSE 'N' END
FROM #Table
Hmmm . . . I'm thinking to generate flags for each year and then apply the logic. Based on the rules you describe:
select t.*,
(case when year in (2017, 2018) and
flag_2017 > 0 and flag_2018 > 0
then 'P'
when year in (2017) and
flag_2018 > 0 and flag_2019 > 0
then 'P'
else 'N'
end) as mark
from (select t.*,
sum(case when year = 2017 then 1 else 0 end) over (partition by id) as flag_2017,
sum(case when year = 2018 then 1 else 0 end) over (partition by id) as flag_2018,
sum(case when year = 2019 then 1 else 0 end) over (partition by id) as flag_2019
from t
) t;
Your sample results don't seem to follow your rules, but some simple variation on this appears to be what you want.
You don't need a physical column. What you want can be expressed as a query, using exists() or lead()
the LEAD() version:
\i tmp.sql
CREATE TABLE years(id integer, zyear integer);
INSERT INTO years (id , zyear ) VALUES
(1, 2017) , (1, 2018) , (1, 2019)
, (2, 2018) , (2, 2019) , (3, 2017)
, (3, 2019) , (8, 2017)
, (4, 2018) , (4, 2019)
;
SELECT id, zyear
, CASE when yy.nxt=yy.zyear+1 THEN 'P' ELSE 'N' END AS flagged
FROM (
SELECT id, zyear
, lead(zyear) OVER (partition by id ORDER BY zyear) AS nxt
FROM years
) yy
;
or, the EXISTS()-version:
SELECT id, zyear
, CASE when yy.xx THEN 'P' ELSE 'N' END AS flagged
FROM (
SELECT id, zyear
, EXISTS ( select * FROM years x where x.id=y.id and x.zyear = y.zyear+1) AS xx
FROM years y
) yy
;
Result: (the same for both versions)
psql:tmp.sql:2: NOTICE: drop cascades to table tmp.years
DROP SCHEMA
CREATE SCHEMA
SET
CREATE TABLE
INSERT 0 10
id | zyear | flagged
----+-------+---------
1 | 2017 | P
1 | 2018 | P
1 | 2019 | N
2 | 2018 | P
2 | 2019 | N
3 | 2017 | N
3 | 2019 | N
4 | 2018 | P
4 | 2019 | N
8 | 2017 | N
(10 rows)

Calculate manual week number of year in Oracle SQL

i have a date column and along that i need to calculate another column in oracle for week number of they year, the weeks should be from sunday to saturday, starting first day of the year.
for example for current year
Week 1 : 1 Jan 2020 (Wednesday) - 4 Jan 2020(Saturday)
Week 2 : 5 Jan 2020 (Sunday) - 11 Jan 2020(Saturday)
. . . . .
Week 5 : 26 Jan 2020 (Sunday) - 1 Feb 2020 (Saturday)
and so on...
You need to write your own logic using a hierarchy query.
Something like the following:
SQL> SELECT WEEKNUMBER,
2 WEEK_START,
3 CASE WHEN WEEKNUMBER = 1 THEN FIRST_WEEKEND ELSE WEEK_START + 6 END AS WEEK_END
4 FROM
5 (SELECT
6 CASE
7 WHEN LEVEL = 1 THEN FIRST_DAY
8 ELSE FIRST_WEEKEND + ( LEVEL - 2 ) * 7 + 1
9 END AS WEEK_START,
10 FIRST_WEEKEND,
11 LEVEL AS WEEKNUMBER
12 FROM
13 ( SELECT
14 TRUNC(SYSDATE, 'YEAR') FIRST_DAY,
15 NEXT_DAY(TRUNC(SYSDATE, 'YEAR'), 'SATURDAY') FIRST_WEEKEND
16 FROM DUAL )
17 CONNECT BY
18 CASE WHEN LEVEL = 1 THEN FIRST_DAY
19 ELSE FIRST_WEEKEND + ( LEVEL - 2 ) * 7 + 1
20 END < ADD_MONTHS(TRUNC(SYSDATE, 'YEAR'), 12));
WEEKNUMBER WEEK_STAR WEEK_END
---------- --------- ---------
1 01-JAN-20 04-JAN-20
2 05-JAN-20 11-JAN-20
3 12-JAN-20 18-JAN-20
4 19-JAN-20 25-JAN-20
5 26-JAN-20 01-FEB-20
6 02-FEB-20 08-FEB-20
7 09-FEB-20 15-FEB-20
8 16-FEB-20 22-FEB-20
9 23-FEB-20 29-FEB-20
10 01-MAR-20 07-MAR-20
11 08-MAR-20 14-MAR-20
.......
.......
53 27-DEC-20 02-JAN-21
Cheers!!
One other option would be
with t as
(
select trunc(sysdate,'yyyy')+level-1 as day, to_char(trunc(sysdate,'yyyy')+level-1,'DY','NLS_DATE_LANGUAGE=AMERICAN') as weekday, level - 1 as lvl
from dual
connect by level <= 366
), t1 as
(
select case
when lvl = 0 then
day
else
case
when weekday = 'SUN' then
day
end
end as day1,
lvl
from t
), t2 as
(
select case
when weekday = 'SAT' then
day
end as day2,
lvl
from t
)
select concat( 'Week ', wk1) as week, day1, day2
from
(
select row_number() over (order by lvl) wk1, day1
from t1 where day1 is not null ) t1
left join
(
select row_number() over (order by lvl) wk2, day2
from t2 where day2 is not null ) t2
on wk2 = wk1
by using select .. from dual connect by level syntax and case..when expression to scan all the current year.
Demo