Oracle : SUM values between max and min dates - sql

I am struggling with a query and think I need some help.
An example of the data I use :
I need to sum the values between the max DATE of each year that have the O (zero) TYPE.
The results should be :
YEAR - SUM
2015 - 10
2016 - 20
2017 - 20
2018 - 35
The sum must include lines with TYPE 1 or 2 but these lines cannot be used in the identification of the MAX DATE by year.
To be clear, in the data I provided, if we had
then the max DATE for 2018 would be 05/07/2018 and the results of the query :
YEAR - SUM
2015 - 10
2016 - 20
2017 - 20
2018 - 30
Identifying max value per year isn't a problem but excluding 1 and 2 TYPEs and then summing data is where I'm stuck at.
thanks in advance.
EDIT : correction of the results.
EDIT2 : another example
The expected results
YEAR - SUM
2015 - 10
2016 - 20
2017 - 20
2018 - 30
The expected results
YEAR - SUM
2015 - 10
2016 - 20
2017 - 20
2018 - 38

I don't know what "max date" has to do with this. You seem to want:
select to_char(date, 'YYYY') as yr, sum(value)
from t
where type = 0
group by to_char(date, 'YYYY')
order by to_char(date, 'YYYY');
EDIT:
If you want both the max date and the sum, then use conditional aggregation:
select to_char(date, 'YYYY') as yr, max(date) as max_date,
sum(case when type = 0 then value end) as sum_type0
from t
group by to_char(date, 'YYYY')
order by to_char(date, 'YYYY');

Related

Multiple Group by in SQL

I have a table like this.
Year Month TenDays Pay
========================================
2015 8 2 12
2016 8 1 43
2016 8 2 11
2016 9 1 22
2016 9 2 33
2016 9 3 4
2016 9 3 25
I want to have SQL query that calculate sum of 'Pay' as 'TotalTenDays' group by 'year' and 'Month' and 'TenDays'
and also calculate sum of 'Pay' as 'TotalMonth' group by 'year' and 'Month'.
I can do that with "union all" but I am searching for a way without using union and 'with cte as()'.
Is it Possible?
Expected table must be like this:
Year Month TenDays TotalTenDays TotalMonth
====================================================================
2015 8 2 12 12
2016 8 1 43 54
2016 8 2 11 54
2016 9 1 22 84
2016 9 2 33 84
2016 9 3 29 84
The answer depends on the database dialect.
The first 4 columns are standard SQL GROUP BY logic, i.e. GROUP BY Year, Month, TenDays with a SUM(Pay) AS TotalTenDays result column.
The TotalMonth column is best done with a windowing function using the OVER clause, but that's only if the SQL dialect supports it.
E.g. for SQL Server, you can do this:
SELECT Year, Month, TenDays
, SUM(Pay) AS TotalTenDays
, SUM(SUM(Pay)) OVER (PARTITION BY Year, Month) AS TotalMonth
FROM MyTable
GROUP BY Year, Month, TenDays
ORDER BY Year, Month, TenDays
See SQL Fiddle for running query using MS SQL Server 2017.
If the SQL dialect doesn't support windowing functions, then suggestion in comment by Jonathan Leffler is a good alternative:
You need to create two sub-queries that do the aggregations, and then join the results of those two sub-queries. Each sub-query will have a different GROUP BY clause.
SELECT a.Year, a.Month, a.TenDays, a.TotalTenDays, b.TotalMonth
FROM ( SELECT Year, Month, TenDays
, SUM(Pay) AS TotalTenDays
FROM MyTable
GROUP BY Year, Month, TenDays
) a
JOIN ( SELECT Year, Month
, SUM(Pay) AS TotalMonth
FROM MyTable
GROUP BY Year, Month
) b ON b.Year = a.Year
AND b.Month = a.Month
ORDER BY a.Year, a.Month, a.TenDays
See SQL Fiddle for running query using MySQL 5.6.

Grouping data on SQL Server

I have this table in SQL Server:
Year Month Quantity
----------------------------
2015 January 10
2015 February 20
2015 March 30
2014 November 40
2014 August 50
How can I identify the different years and months adding two more columns that group the same years with a number and then different months in sequential way like the example
Year Month Quantity Group Subgroup
------------------------------------------------
2015 January 10 1 1
2015 February 20 1 2
2015 March 30 1 3
2014 November 40 2 1
2014 August 50 2 2
You can use DENSE_RANK to calculate the groups for you:
SELECT t1.*, DENSE_RANK() OVER (ORDER BY Year DESC) AS [Group],
DENSE_RANK() OVER (PARTITION BY Year ORDER BY DATEPART(month, Month + ' 01 2010')) AS [SubGroup]
FROM t1
ORDER BY 4, 5
See this fiddle.
To associate group and subgroup with a number you can do this:
WITH RankedTable AS (
SELECT year, month, quantity,
ROW_NUMBER() OVER (partition by year order by Month) AS rn
FROM yourtable)
SELECT year, month, quantity,
SUM (CASE WHEN rn = 1 THEN 1 ELSE 0 END) OVER (ORDER BY YEAR) as year_group,
rn AS subgroup
FROM RankedTable
Here ROW_NUMBER() OVER clause calculates rank of a month within a year.
And SUM() ... OVER calculates running SUM for the months with rank 1.
SQL Fiddle

Getting Month values for specific year

Fisc_prd stands for month
fisc_yr stands for year
I need the result in such a way that if current month is 01(that is Jan) then i need all the period for previous year i.e 1 to 12 and if the current month is not equal to 01 then i need all the period for current year less than current month(e.g if the current month is 3 then i need 1 to 2 as fisc_prd)
I can only get all the fisc_prd for previous year but cant get fisc_prd less than current month for current year. In Below query, CALENDAR table contains all the Month and Year values.By usig this query i can get all the fisc_prd for previous year when current month is 01.
SELECT FISC_PRD,FISC_YR FROM CALENDAR WHERE FISC_YR=(SELECT DECODE(TO_NUMBER(TO_CHAR(SYSDATE,'MM')),01,(TO_NUMBER(TO_CHAR(SYSDATE,'YYYY'))-1) ,TO_NUMBER(TO_CHAR(SYSDATE,'YYYY'))) FROM DUAL)
Please share your ideas
If you're basing this on the current date then you can use a connect-by hierarchical query to get the period and year:
select extract(month from add_months(sysdate, 1-level)) as fisc_prd,
extract(year from add_months(sysdate, 1-level)) as fisc_year
from dual
where level > 1
connect by level <= case when extract(month from sysdate) = 1 then 13
else extract(month from sysdate) end;
Today that gives:
FISC_PRD FISC_YEAR
---------- ----------
1 2015
To check how it behaves for other dates you can use a modified version that uses a specific date but the same logic; I'm using a bind variable here:
variable dt varchar2;
exec :dt := '2015-01-17';
select extract(month from add_months(to_date(:dt, 'YYYY-MM-DD'), 1-level)) as fisc_prd,
extract(year from add_months(to_date(:dt, 'YYYY-MM-DD'), 1-level)) as fisc_year
from dual
where level > 1
connect by level <= case
when extract(month from to_date(:dt, 'YYYY-MM-DD')) = 1 then 13
else extract(month from to_date(:dt, 'YYYY-MM-DD'))
end;
FISC_PRD FISC_YEAR
12 2014
11 2014
10 2014
9 2014
8 2014
7 2014
6 2014
5 2014
4 2014
3 2014
2 2014
1 2014
Changing the value of dt seems to give the result you described; for example with
exec :dt := '2015-03-17';
you get:
FISC_PRD FISC_YEAR
---------- ----------
2 2015
1 2015
But this is just to test the logic, you can just use sysdate rather than a bind variable.
The 1-level and using 13 if the current month is January are because you don't want the current month to be included in the result.
This ought to work:
with sample_data as (select to_number(to_char(add_months(sysdate, 6 - level), 'mm')) fisc_prd,
to_number(to_char(add_months(sysdate, 6 - level), 'yyyy')) fisc_yr
from dual
connect by level <= 24)
select fisc_prd,
fisc_yr
from sample_data
where case when to_char(sysdate, 'mm') = '01' then to_number(to_char(sysdate, 'yyyy')) -1
else to_number(to_char(sysdate, 'yyyy')) end = fisc_yr
and case when to_char(sysdate, 'mm') = '01' then 13
else to_number(to_char(sysdate, 'mm')) end > fisc_prd;
If your colum "FISC_YR" is datetime datatype then the below query will works for you
SELECT FISC_PRD,FISC_YR
FROM CALENDAR
WHERE
(month(getdate())!=1 AND month(FISC_YR) in(month(getdate())- 1,month(getdate())-2) AND year(getdate())=year(FISC_YR))
OR
(month(getdate())=1 AND year(getdate())-1=year(FISC_YR))

SQL counts of tests given in year

I'm trying to make a sum of tests given in a year from a specific table. This is what I have so far:
SELECT DISTINCT TO_CHAR(test_date, 'YYYY') AS Year, SUM(yearCount)
FROM(
SELECT COUNT(test_date) AS yearCount
FROM test_record
), test_record
GROUP BY test_record.test_date
ORDER BY Year ASC;
Which gives me the output:
YEAR SUM(YEARCOUNT)
---- --------------
1958 12
1991 12
1996 12
1998 12
2000 12
2001 12
2010 12
2012 12
2013 12
Now, I understand my problem lies here: SELECT COUNT(test_date) AS yearCount , because I have 12 entries in the table so it's obviously giving the count of the number of entries in the table. I need the count of tests given in each year, i.e. the output should look like this:
YEAR SUM(YEARCOUNT)
---- --------------
1958 1
1991 1
1996 1
1998 1
2000 1
2001 1
2010 1
2012 1
2013 4
So basically my question boils down to: How do I count by year in a date column?
(I'm using ORACLE 7 I believe)
EDIT: Thanks to the below help I was able to get my desired output, but they were both a little "wrong", so I didn't accept them (sorry if that's a Faux pas). Here is my script:
SELECT TO_CHAR(test_date, 'YYYY') AS Year, COUNT(test_date)
FROM test_record
GROUP BY TO_CHAR(test_date, 'YYYY')
ORDER BY Year ASC;
You want to group by year and not test date.
Select count(*), to_date('YYYY',test_data) as year
From test_record
Group by to_date('YYYY',test_date)
You can do group by alone , no need for subquery
SELECT TO_CHAR(test_date, 'YYYY') AS Year, COUNT(test_date)
GROUP BY TO_CHAR(test_date, 'YYYY')
ORDER BY Year ASC;

Calculate substraction by a specific quarter of year

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