Calculate substraction by a specific quarter of year - sql

I have some records after SQL generating:
YEARS MONTHS SUMMONTH SUMQUARTER QTR
----- ------ -------- ---------- ---
2009 Jan 363639 855922 1
2009 Feb 128305 855922 1
2009 Mar 363978 855922 1
2009 Apr 376633 1058871 2
2009 May 299140 1058871 2
2009 Jun 383098 1058871 2
2009 Jul 435000 1063577 3
2009 Aug 266227 1063577 3
2009 Sep 362350 1063577 3
2009 Oct 449366 1017906 4
2009 Nov 280943 1017906 4
2009 Dec 287597 1017906 4
2010 Jan 418277 661083 1
2010 Feb 129895 661083 1
2010 Mar 112911 661083 1
2010 Apr 163593 685625 2
2010 May 228505 685625 2
2010 Jun 293527 685625 2
2010 Jul 451608 1044364 3
2010 Aug 356683 1044364 3
2010 Sep 236073 1044364 3
2010 Oct 247365 798925 4
2010 Nov 414100 798925 4
2010 Dec 137460 798925 4
24 rows selected
The SUMQUARTER column sum up each quarter of a year...
The qtr specify it belongs to which quarter.
The problem is how to have a subtraction of sumquarter between 2 different years to get the specific query result?
The difference is not the: max value-min value.
It is the user-defined value that he want to input...
Let say...
For example, the user want to see the substraction(sumquarter) between 2009 qtr=2 and 2010 qtr=2,
user may change the parameter(years,qtr) of sql to view the record.
This mean the result should be: (1058871 - 685625)
Here is the SQL that I am currently using:
select years,months,summonth,sumquarter,qtr
from(
select years,months,summonth,sumhour,hours,to_char(ym, 'Q') qtr,
sum(sumhour) over(partition by years || to_char(ym, 'Q') order by years || to_char(ym, 'Q')) sumquarter,ym,
count(days) over(partition by years,months,hours) days_month
from(
select years, months, days, hours, mins, sumHour,
SUM (sumHour) OVER (PARTITION BY years,months,days) sumDay,
SUM (sumHour) OVER (PARTITION BY years,months) sumMonth,
SUM (sumHour) OVER (PARTITION BY years) sumyear,
to_date(years || months, 'YYYYMon', 'NLS_DATE_LANGUAGE=American') ym
from (
SELECT x.years, x.months, x.days, x.hours, x.mins, sum(x.value) as sumHour
FROM xmltest,
XMLTABLE ('$d/cdata/name' passing doc as "d"
COLUMNS
years integer path 'year',
months varchar(3) path 'month',
days varchar(2) path 'day',
hours varchar(2) path 'hour',
mins varchar(2) path 'minute',
value float path 'value'
) as X
group by x.years, x.months, x.days, x.hours, x.mins
order by x.years, x.months, x.days
)
)
)
group by years,months,summonth,sumquarter,qtr,ym
order by ym
The sql pattern maybe something like this:...??
select ((select sumquarter from table where years=2009 and qtr=2) - (select sumquarter from table where years=2010 and qtr=2)) from table
Actually, it doesn't work...
The result maybe look in this view:
SUBTRACT
----------
373246
Thanks everyone helps!!:)

I'd use the following:
select
((select sumquarter from table where years=2009 and qtr=2 and rownum=1) -
(select sumquarter from table where years=2010 and qtr=2 and rownum=1)) as substract
from dual

try this query:
select (case when years=2009 and qtr=2 then sumquater end) - (case when years=2010 and qtr=2 then sumquater end) from table

Related

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.

Querying an SQL table to get all new values in a column according to filter in another column

Sorry I couldn't make the question clearer, let me explain it here.
I have a table that has two columns:
year, ID
------------
2013, 01
2013, 27
2013, 33
2014, 22
2014, 33
2014, 01
2014, 13
2015, 45
2015, 13
2015, 22
What I want to do is the following: check all new IDs that appeared in 2014 from 2013, and all new IDs that appeared in 2015 from 2014, and so on and so forth...
So, from the example above, the expected answer to my query should be a table like:
year, new
-------------
2014, 2
2015, 1
Since in 2014 there are two new IDs in relation to 2013 (22 and 13), and in 2015 there is one new ID in relation to 2014 (45).
Sorry for the horrible formatting, I'm new to this site. Any help is appreciated, thanks.
If I understand correctly, you want lag():
select year, count(*)
from (select t.*, lag(year) over (partition by id order by year) as prev_year
from t
) t
where prev_year is null or prev_year < year - 1
group by year;
You need to find the all codes that didn't exist with a year y - 1 that shower at year y:
with cte as
(
select cast(2013 as int) as year, '01' as id
union select 2013, 27
union select 2013, 33
union select 2014, 22
union select 2014, 33
union select 2014, '01'
union select 2014, 13
union select 2015, 45
union select 2015, 13
union select 2015, 22
)
select year, count(*) from cte a where not exists(select 1 from cte b where a.year -1 = b.year and a.id = b.id)
and a.year -1 in (select distinct year from cte)
group by year

restructuring sql rows into columns of the same type

This is my first time using a forum like this for SQL assistance so bear with me! I have a large query set for student data, each student has one row where the primary join table is their application record (all other tables joined onto that). Now, I'm looking to add in degree data, however many students have multiple degrees so it's duplicating the student rows (boo!).
Here is a shortened version of what it looks like:
StudentID CohortTerm DegreeYear DegreeMonth
1 2009 Spring 2011 05
1 2009 Spring 2013 10
2 2012 Fall 2015 05
3 2015 Fall 2017 05
3 2015 Fall 2018 05
Here is what I want it to look like:
StudentID CohortTerm Degree1_Y Degree1_M Degree2_Y Degree2_M
1 2009 Spring 2011 05 2013 10
2 2012 Fall 2015 05
3 2015 Fall 2017 05 2018 05
And here's another way doing this with analytical functions:
WITh test_data AS
(
SELECT 1 StudentID, 2009 Cohort, 'Spring' Term, 2011 DegreeYear, '05' DegreeMonth FROM dual
UNION ALL
SELECT 1, 2009, 'Spring', 2013, '10' FROM dual
UNION ALL
SELECT 2, 2012, 'Fall' , 2015, '05' FROM dual
UNION ALL
SELECT 3, 2015, 'Fall' , 2017, '05' FROM dual
UNION ALL
SELECT 3, 2015, 'Fall' , 2018, '05' FROM dual
)
SELECT * FROM
(
SELECT ROW_NUMBER() OVER (PARTITION BY Cohort ORDER BY Cohort, DegreeYear, DegreeMonth) rno
, StudentID, Cohort, Term, DegreeYear, DegreeMonth
, LEAD(DegreeYear) OVER (PARTITION BY Cohort ORDER BY Cohort, DegreeYear, DegreeMonth) DegreeYear2
, LEAD(DegreeMonth) OVER (PARTITION BY Cohort ORDER BY Cohort, DegreeYear, DegreeMonth) DegreeMonth2
FROM test_data
)
WHERE rno = 1
ORDER BY Cohort, DegreeYear, DegreeMonth
/
Output:
RNO STUDENTID COHORT TERM DEGREEYEAR DEGREEMONTH DEGREEYEAR2 DEGREEMONTH2
1 1 2009 Spring 2011 05 2013 10
1 2 2012 Fall 2015 05
1 3 2015 Fall 2017 05 2018 05
As others have commented, pivot is what you are looking for. See pivot docs here.

how to split the date period in sql?

Info:
Date 1 nov 2016 to 5 nov 2016 = 10 members;
then people joined
Date 3 nov 2016 to 7 Nov 2016 =12 members.
Now I want the data for date 1 nov 2016 to 10 nov 2016.
Output will be
From date To date No.of members
1 nov 2016 2 nov 2016 10
3 nov 2016 5 nov 2016 22
6 nov 2016 7 nov 2016 12
Kindly Give me the suggestion to make query for that
In standard SQL, you would do this with a conditional sum:
with dates as (
select startdate as dte, cnt
from t
union all
select startdate + interval '1 day', - cnt
from t
)
select dte, sum(sum(cnt)) over (order by dte)
from dates
group by dte
order by dte;

SQL NOOB - Oracle joins and Row Number

I was hoping to get some guidance on a SQL script I am trying to put together for Oracle database 11g.
I am attempting to perform a count of claims from the 'claim' table, and order them by year / month / and enterprise.
I was able to get a count of claims and order them like I would like, however I need to pull data from another table and I am having trouble combining the 'row_number' function with a join.
Here is my script so far:
SELECT TO_CHAR (SYSTEM_ENTRY_DATE, 'YYYY') YEAR,
TO_CHAR (SYSTEM_ENTRY_DATE, 'MM') MONTH,
ENTERPRISE_IID,
COUNT (*) CLAIMS
FROM (SELECT CLAIM.CLAIM_EID,
CLAIM.SYSTEM_ENTRY_DATE,
CLAIM.ENTERPRISE_IID,
ROW_NUMBER () OVER (PARTITION BY CLAIM.CLAIM_EID, CLAIM.ENTERPRISE_IID
ORDER BY CLAIM.SYSTEM_ENTRY_DATE DESC) RN
FROM CLAIM
WHERE CLAIM_IID IN (SELECT DISTINCT (CLAIM_IID)
FROM CLAIM_LINE
WHERE STATUS <> 'D')
AND CLAIM.CONTEXT = '1'
AND CLAIM.CLAIM_STATUS = 'A'
AND CLAIM.LAST_ANALYSIS_DATE IS NOT NULL)
WHERE RN = 1
GROUP ENTERPRISE_IID,
TO_CHAR (SYSTEM_ENTRY_DATE, 'YYYY'),
TO_CHAR (SYSTEM_ENTRY_DATE, 'MM');
So far all of my data is coming from the 'claim' table. This pulls the following result:
YEAR MONTH ENTERPRISE_IID CLAIMS
---- ----- -------------- ----------
2016 01 6 1
2015 08 6 3
2016 02 6 2
2015 09 6 2
2015 07 6 2
2015 09 5 22
2015 11 5 29
2015 12 5 27
2016 04 5 8
2015 07 5 29
2015 05 5 15
2015 06 5 5
2015 10 5 45
2016 03 5 54
2015 03 5 10
2016 02 5 70
2016 01 5 55
2015 08 5 32
2015 04 5 12
19 rows selected.
The enterprise_IID is the primary key on the 'enterprise' table. The 'enterprise' table also contains the 'name' attribute for each entry. I would like to join the claim and enterprise table in order to show the enterprise name for this count, and not the enterprise_IID.
As you can tell I am rather new to Oracle and SQL, and I am a bit stuck on this one. I was thinking that I should do an inner join between the two tables, but I am not quite sure how to do that when using the row_number function.
Or perhaps I am taking the wrong approach here, and someone could push me in another direction.
Here is what I tried:
SELECT TO_CHAR (SYSTEM_ENTRY_DATE, 'YYYY') YEAR,
TO_CHAR (SYSTEM_ENTRY_DATE, 'MM') MONTH,
ENTERPRISE_IID,
ENTERPRISE.NAME,
COUNT (*) CLAIMS
FROM (SELECT CLAIM.CLAIM_EID,
CLAIM.SYSTEM_ENTRY_DATE,
CLAIM.ENTERPRISE_IID,
ROW_NUMBER () OVER (PARTITION BY CLAIM.CLAIM_EID, CLAIM.ENTERPRISE_IID
ORDER BY CLAIM.SYSTEM_ENTRY_DATE DESC) RN
FROM CLAIM, enterprise
INNER JOIN ENTERPRISE
ON CLAIM.ENTERPRISE_IID = ENTERPRISE.ENTERPRISE_IID
WHERE CLAIM_IID IN (SELECT DISTINCT (CLAIM_IID)
FROM CLAIM_LINE
WHERE STATUS <> 'D')
AND CLAIM.CONTEXT = '1'
AND CLAIM.CLAIM_STATUS = 'A'
AND CLAIM.LAST_ANALYSIS_DATE IS NOT NULL)
WHERE RN = 1
GROUP BY ENTERPRISE.NAME,
ENTERPRISE_IID,
TO_CHAR (SYSTEM_ENTRY_DATE, 'YYYY'),
TO_CHAR (SYSTEM_ENTRY_DATE, 'MM');
Thank you in advance!
"Desired Output"
YEAR MONTH NAME CLAIMS
---- ----- ---- ----------
2016 01 Ent1 1
2015 08 Ent1 3
2016 02 Ent1 2
2015 09 Ent1 2
2015 07 Ent1 2
2015 09 Ent2 22
2015 11 Ent2 29
2015 12 Ent2 27
2016 04 Ent2 8
2015 07 Ent2 29
2015 05 Ent2 15
2015 06 Ent2 5
2015 10 Ent2 45
2016 03 Ent2 54
2015 03 Ent2 10
2016 02 Ent2 70
2016 01 Ent2 55
2015 08 Ent2 32
2015 04 Ent2 12
19 rows selected.
You can try this. Joins can be used when calculating row numbers with row_number function.
SELECT TO_CHAR (SYSTEM_ENTRY_DATE, 'YYYY') YEAR,
TO_CHAR (SYSTEM_ENTRY_DATE, 'MM') MONTH,
ENTERPRISE_IID,
NAME,
COUNT (*) CLAIMS
FROM (SELECT CLAIM.CLAIM_EID,
CLAIM.SYSTEM_ENTRY_DATE,
CLAIM.ENTERPRISE_IID,
ENTERPRISE.NAME,
ROW_NUMBER () OVER (PARTITION BY CLAIM.CLAIM_EID, CLAIM.ENTERPRISE_IID
ORDER BY CLAIM.SYSTEM_ENTRY_DATE DESC) RN
FROM CLAIM --, enterprise (this is not required as the table is being joined already)
INNER JOIN ENTERPRISE ON CLAIM.ENTERPRISE_IID = ENTERPRISE.ENTERPRISE_IID
INNER JOIN (SELECT DISTINCT CLAIM_IID FROM CLAIM_LINE WHERE STATUS <> 'D') CLAIM_LINE
ON CLAIM.CLAIM_IID = CLAIM_LINE.CLAIM_IID
WHERE CLAIM.CONTEXT = '1'
AND CLAIM.CLAIM_STATUS = 'A'
AND CLAIM.LAST_ANALYSIS_DATE IS NOT NULL) t
WHERE RN = 1
GROUP BY NAME, --ENTERPRISE.NAME (The alias ENTERPRISE is not accessible here.)
ENTERPRISE_IID,
TO_CHAR(SYSTEM_ENTRY_DATE, 'YYYY'),
TO_CHAR(SYSTEM_ENTRY_DATE, 'MM');
I'd write the query like this:
SELECT TO_CHAR(TRUNC(c.system_entry_date,'MM'),'YYYY') AS year
, TO_CHAR(TRUNC(c.system_entry_date,'MM'),'MM') AS month
, e.enterprise_name AS name
, COUNT(*) AS claims
FROM (
SELECT r.claim_eid
, r.enterprise_iid
, MAX(r.system_entry_date) AS system_entry_date
FROM ( SELECT DISTINCT l.claim_iid
FROM claim_line l
WHERE l.status <> 'D'
) d
JOIN claim r
ON r.claim_iid = d.claim_iid
AND r.context = '1'
AND r.claim_status = 'A'
AND r.last_analysis_date IS NOT NULL
GROUP
BY r.claim_eid
, r.enterprise_iid
) c
JOIN enterprise e
ON e.enterprise_iid = c.enterprise_iid
GROUP
BY c.enterprise_iid
, TRUNC(c.system_entry_date,'MM')
, e.enterprise_name
ORDER
BY e.enterprise_name
, TRUNC(c.system_entry_date,'MM')
A few notes:
I prefer to qualify ALL column references with the table name or short table alias, and assign aliases to all inline views.
Since the usage of ROW_NUMBER() appears to be get the "latest" system_entry_date for a claim and eliminate duplicates, I'd prefer to use a GROUP BY and a MAX() aggregate.
I prefer to use a join operation rather than the NOT IN (subquery) pattern. (Or, I would tend to use a NOT EXISTS (correlated subquery) pattern.
I don't think it matters too much if you use TO_CHAR or EXTRACT. The TO_CHAR gets you the leading zero in the month, I don't think EXTRACT(MONTH ) gets you the leading zero. I'd use whichever gets me closest to the resultset I need.Personally, I would return just a single column, either containing the year and month as one string e.g. TO_CHAR( , 'YYYYMM') or just a DATE value. It all depends what I'm going to be doing with that.
Just hypothesis to start with, because requirement of query output unclear:
SELECT
C.ENTERPRISE_IID,
E.ENTERPRISE_NAME,
extract(year from CLAIM.SYSTEM_ENTRY_DATE) SYSTEM_ENTRY_YEAR,
extract(month from CLAIM.SYSTEM_ENTRY_DATE) SYSTEM_ENTRY_MONTH,
count(distinct C.CLAIM_EID) CLAIM_COUNT
FROM
CLAIM C,
ENTERPRISE E
WHERE
C.CLAIM_IID IN (
SELECT DISTINCT (CLAIM_IID)
FROM CLAIM_LINE
WHERE STATUS <> 'D'
)
AND C.CONTEXT = '1'
AND C.CLAIM_STATUS = 'A'
AND C.LAST_ANALYSIS_DATE IS NOT NULL
AND E.ENTERPRISE_IID = C.ENTERPRISE_IID
GROUP BY
C.ENTERPRISE_IID,
E.ENTERPRISE_NAME,
extract(year from CLAIM.SYSTEM_ENTRY_DATE),
extract(month from CLAIM.SYSTEM_ENTRY_DATE)
ORDER BY
extract(year from CLAIM.SYSTEM_ENTRY_DATE),
extract(month from CLAIM.SYSTEM_ENTRY_DATE),
E.ENTERPRISE_NAME