I have 3 columns : year, price, and day_type.
year day_type price
2016 0 10
2016 1 20
2016 2 5
2017 0 14
2017 1 6
2017 2 3
I want to keep only the lines where day_type = 1 or 2, but add to these lines the value when day_type = 0.
Expected Result :
year day_type price
2016 1 30
2016 2 15
2017 1 20
2017 2 17
How can I do that?
You can use a join:
select t.year, t.day_type, (t.price + coalesce(t0.price, 0)) as price
from t left join
t t0
on t.year = t0.year and t0.day_type = 0
where t.day_type <> 0;
This uses left join in case one of the years does not have a 0 price.
With sum() window function:
select * from (
select year, (2 * day_type) % 3 as day_type,
sum(price) over (partition by year) - price as price
from tablename
) t
where day_type <> 0
order by year, day_type
See the demo.
Results:
year | day_type | price
---: | -------: | ----:
2016 | 1 | 30
2016 | 2 | 15
2017 | 1 | 20
2017 | 2 | 17
Related
I am attempting to join two tables based on date ranges.
Table A format is:
ID CAT DATE_START DATE_END
1 10 2018-01-01 2020-12-31
2 15 2018-06-01 2018-07-01
Table B format is:
ID YEAR VALUE
1 2017 100
1 2018 110
1 2019 90
1 2020 30
2 2018 200
The resulting table should be merged if for a given ID, any of the days in B.YEAR are included in the date range from A.DATE_START to A.DATE_END, and should look like this:
ID YEAR CAT VALUE
1 2018 10 110
1 2019 10 90
1 2020 10 30
2 2018 15 200
I tried merging using extract(year from DATE_START) and extract(year from DATE_START), but I cannot manage to include the middle year 2019 in the interval, which means ID = 1 is missing its 2019 value.
I also tried merging using to_date(YEAR), 'YYYY'), but the generated date for YEAR = '2018' is '1.9.2018', which does not fall in the interval for ID = 2. Thanks a lot for help.
Join the tables like this:
select a.ID, b.YEAR, a.CAT, b.VALUE
from TableA a inner join TableB b
on b.ID = a.ID
and b.year between extract(year from a.DATE_START) and extract(year from a.DATE_END)
See the demo.
Results:
> ID | YEAR | CAT | VALUE
> -: | ---: | --: | ----:
> 1 | 2018 | 10 | 110
> 1 | 2019 | 10 | 90
> 1 | 2020 | 10 | 30
> 2 | 2018 | 15 | 200
First: I use Microsoft SQL Server, so apologies if this doesn't work in Oracle.
SELECT * FROM TableA
INNER JOIN TableB ON TableA.Id = TableB.Id AND TableB.Year BETWEEN YEAR(TableA.Date_Start) AND YEAR(TableA.Date_End)
I have a table with the following data:
Id Date Value
---------------------------
1 Dec-01-2019 10
1 Dec-03-2019 5
1 Dec-05-2019 8
1 Jan-03-2020 6
1 Jan-07-2020 3
1 Jan-08-2020 9
2 Dec-01-2019 4
2 Dec-03-2019 7
2 Dec-31-2019 9
2 Jan-04-2020 4
2 Jan-09-2020 6
I need to group it to the following format: 1 record per month per id. If month is closed, so date will be the last day of that month, if not, the last day available. Max and average are calculated using all data until that date.
Id Date Max_Value Average_Value
-----------------------------------------------
1 Dec-31-2019 10 7,6
1 Jan-08-2020 10 6,8
2 Dec-31-2019 9 6,6
2 Jan-09-2020 9 6,0
Any easy SQL to obtain this analysis?
Regards,
Hmmm . . . You want to aggregate by month and then just take the maximum date in the month:
select id, max(date), max(value), avg(value * 1.0)
from t
group by id, eomonth(date)
order by id, max(date);
If by closed month you mean that it's not the last month of the id then:
select id,
case
when year(Date) = year(maxDate) and month(Date) = month(maxDate) then maxDate
else eomonth(Date)
end Date,
max(maxValue) Max_Value,
round(avg(1.0 * Value), 1) Average_Value
from (
select *,
max(Date) over (partition by Id) maxDate,
max(Value) over (partition by Id) maxValue
from tablename
) t
group by id,
case
when year(Date) = year(maxDate) and month(Date) = month(maxDate) then maxDate
else eomonth(Date)
end
order by id, Date
See the demo.
Results:
> id | Date | Max_Value | Average_Value
> -: | :--------- | --------: | :------------
> 1 | 2019-12-31 | 10 | 7.7
> 1 | 2020-01-08 | 10 | 6.0
> 2 | 2019-12-31 | 9 | 6.7
> 2 | 2020-01-09 | 9 | 5.0
** Edited to add month
In our (ORACLE 11G) database we have a table that has a custom data type that is a VARRAY with 32 values (Integers). These numbers represent a value for each month of a year for 3 consecutive years. In the same record,there will be a work year. This work year will always be the middle year of the VARRAY's relative years. I'm trying to figure out a way to display these as such:
Current:
| ID | WORK_YEAR | PRODUCTION |
| 2127 | 2012 |[1..36] (<--I'm just doing 1 through 36 for simplicity's sake)
My desired output would be something like this:
| ID | WORK_YEAR | MONTH | PRODUCTION
| 2127 | 2011 | JANUARY | 1
| 2127 | 2011 | FEBRUARY | 2
| 2127 | 2011 | MARCH | 3
...
...
| 2127 | 2012 | JANUARY | 13
| 2127 | 2012 | FEBRUARY | 14
...
...
| 2127 | 2013 | JANUARY | 25
| 2127 | 2013 | FEBRUARY | 26
...
...
| 2127 | 2013 | DECEMBER | 36
My end goal would be to pivot it and have a column for each month and then a record for each ID, YEAR; however, I can't figure out how to get it to this format first.
So far I've only got:
SELECT
WP.ID,
WP.WORK_YEAR,
PRO.COLUMN_VALUE AS PRODUCTION
FROM
LINEWORK.WORK_PRODUCTION WP,
TABLE(WP.PRODUCTION) PRO
;
But this doesn't let me make the year relative on the index of PRODUCTION that I'm accessing. Any and all insight would be greatly appreciated.
I do not know Oracle very well, I googled some things about Oracle VARRAY and so try this >>
SELECT
WP.ID,
WP.WORK_YEAR,
WP.PRODUCTION.COUNT AS maxMonth,
PRO.X AS MonthNdx,
(TRUNC(PRO.X / 12, 0) - 1) AS adjYear,
WP.WORK_YEAR + (TRUNC(PRO.X / 12, 0) - 1) AS TrueYear,
PRO.COLUMN_VALUE AS PRODUCTION
FROM
LINEWORK.WORK_PRODUCTION WP,
TABLE(WP.PRODUCTION) PRO
;
Revised, remove PRO.X, use ROW_NUMBER() >>
SELECT
WP.ID,
WP.WORK_YEAR,
WP.PRODUCTION.COUNT AS maxMonth,
ROW_NUMBER() OVER(Partition By WP.ID Order By 1) AS ArrayNdx,
--PRO.X AS MonthNdx,
(TRUNC(ROW_NUMBER() OVER(Partition By WP.ID Order By 1) / 12, 0) - 1) AS adjYear,
WP.WORK_YEAR + (TRUNC(ROW_NUMBER() OVER(Partition By WP.ID Order By 1) / 12, 0) - 1) AS TrueYear,
PRO.COLUMN_VALUE AS PRODUCTION
FROM
LINEWORK.WORK_PRODUCTION WP,
TABLE(WP.PRODUCTION) PRO
;
I was able to get the correct results by suing case statements and then pivoting the data as seen below:
select * from
(
select
WP.ID CR_ID,
case
when row_number() over(partition by WP.ID order by 1) < 14 then to_char(to_number(WP.WORK_YEAR, '9999') - 1)
when row_number() over(partition by WP.ID order by 1) > 25 then to_char(to_number(WP.WORK_YEAR, '9999') + 1)
else to_char(to_number(WP.WORK_YEAR))
end WORK_YEAR,
PRODUCTION.COLUMN_VALUE PRODUCTION,
case
when row_number() over(partition by WP.ID order by 1) - 1 < 13 then row_number() over(partition by WP.ID order by 1) - 1
when row_number() over(partition by WP.ID order by 1) - 13 > 0 then
case
when row_number() over(partition by WP.ID order by 1) - 13 < 13 then row_number() over(partition by WP.ID order by 1) - 13
else row_number() over(partition by WP.ID order by 1) - 25
end
end MNTH
from
LINEWORK.WORK_PRODUCTION WP,
table(WP.PRODUCTION) PRO
)
pivot
(
sum(PRODUCTION)
for MNTH in
(
0 TOTAL,
1 JANUARY,
2 FEBRUARY,
3 MARCH,
4 APRIL,
5 MAY,
6 JUNE,
7 JULY,
8 AUGUST,
9 SEPTEMBER,
10 OCTOBER,
11 NOVEMBER,
12 DECEMBER
)
)
order by
ID,
WORK_YEAR
;
the table looks like this
num Year
1 | 2014
2 | 2014
3 | 2014
2 | 2015
4 | 2015
5 | 2015
6 | 2015
I would like my query to return
4 | 2014
5 | 2014
6 | 2014
1 | 2015
3 | 2015
from 1 to 6, the number that is not used in particular year.
Generate the all the combinations and then take out the ones that exist:
select n.num, y.year
from (select distinct num from t) n cross join
(select distinct year from t) y left join
t
on t.num = n.num and t.year = y.year
where t.num is null;
Note that year is a bad name for a column in SQL Server because it is the name of a function and a keyword (think datepart()).
I have a table that I need to use to build a result set from where certain rows from the table are columns in the result set. I started to chain LEFT JOINs together on the table multiple times but I need to eliminate results that are a different combination of another result already in the set:
For example, if I get 1, 21, 25 as result columns, I can't have ANY other combination of those numbers in the results.
My table definition is:
Table tblKPIDetails
Column Month int
Column Year int
Column Division varchar(3)
Column KPI int
Column Value decimal(18,4)
My current query is:
SELECT *
FROM tblKPIDetails J1
LEFT JOIN tblKPIDetails J2 ON J2.Month = J1.Month AND J2.Year = J1.Year AND J2.Division = J1.Division AND NOT(J2.KPI = J1.KPI ) AND (J2.KPI = 1 OR J2.KPI = 21 OR J2.KPI = 25)
LEFT JOIN tblKPIDetails J3 ON J3.Month = J1.Month AND J3.Year = J1.Year AND J3.Division = J1.Division AND NOT(J3.KPI = J1.KPI ) AND (J3.KPI = 1 OR J3.KPI = 21 OR J3.KPI = 25)
WHERE J1.KPI = 1 OR J1.KPI = 21 OR J1.KPI = 25
I know this is wrong, but it's a super-set of what I need. In the results from the query above, I can get J1.KPI, J2.KPI, J3.KPI or J1.KPI, J3.KPI, J2.KPI, or any other combination.
My expected result would be:
Division | Month | Year | KPIA | KPIAValue | KPIB | KPIBValue | KPIC | KPICValue
for each division, month, and year
where KPIA, KPIB, or KPIC = 1, 21, or 25 but only 1 combination of 1,21,25 exists per division|month|year
EDIT
To clarify the expected results a little more, using the above query, I'm getting the following results:
Division | Month | Year | KPIA | KPIAValue | KPIB | KPIBValue | KPIC | KPICValue
--------------------------------------------------------------------------------
000 1 2012 1 1000 21 2000 25 3000
000 1 2012 21 2000 1 1000 25 3000
000 1 2012 25 3000 21 2000 1 1000
111 1 2012 1 555 21 10000 25 5000
I need to make it so my results would only be ANY 1 of the first 3 results and then the last one...for example:
Division | Month | Year | KPIA | KPIAValue | KPIB | KPIBValue | KPIC | KPICValue
--------------------------------------------------------------------------------
000 1 2012 25 3000 21 2000 1 1000
111 1 2012 1 555 21 10000 25 5000
I think you are looking for the PIVOT table operator like so:
SELECT
Devision,
Month,
Year,
[1] AS KPIAValue,
[21] AS KPIBValue,
[25] AS KPICValue
FROM
(
SELECT t1.*
FROM tblKPIDetails t1
INNER JOIN
(
SELECT Month, Year, Devision
FROM tblKPIDetails
WHERE KPI IN(1, 21, 25)
GROUP BY Month, Year, Devision
HAVING COUNT(DISTINCT KPI) = 3
) t2 ON t1.Month = t2.Month AND t1.Year = t2.Year
AND t1.Devision = t2.Devision
) t
PIVOT
(
MAX(Value)
FOR KPI IN([1], [21], [25])) p;
SQL Fiddle Demo
This will give you the data in the form:
| DEVISION | MONTH | YEAR | KPIAVALUE | KPIBVALUE | KPICVALUE |
---------------------------------------------------------------
| A | 2 | 2012 | 16 | 16 | 16 |
| B | 10 | 2012 | 16 | 18 | 20 |
Note that: This will give you the only combination of the Year, Month, DEVISION that have all the values 1, 21 and 25, and that what this query do:
SELECT Month, Year, Devision
FROM tblKPIDetails
WHERE KPI IN(1, 21, 25)
GROUP BY Month, Year, Devision
HAVING COUNT(DISTINCT KPI) = 3
Update: If you are looking for those that had at least one of 1, 21 or 25, just remove the HAVING COUNT(DISTINCT KPI) = 3, but this will make you expect more values than these three, in this case it will ignore other values and return only those three. Also it will return NULL for any of the missing values of them like so:
SELECT
Devision,
Month,
Year,
[1] AS KPIAValue,
[21] AS KPIBValue,
[25] AS KPICValue
FROM
(
SELECT t1.*
FROM tblKPIDetails t1
INNER JOIN
(
SELECT Month, Year, Devision
FROM tblKPIDetails
WHERE KPI IN(1, 21, 25)
GROUP BY Month, Year, Devision
) t2 ON t1.Month = t2.Month AND t1.Year = t2.Year
AND t1.Devision = t2.Devision
) t
PIVOT
(
MAX(Value)
FOR KPI IN([1], [21], [25])) p;
Updated SQL Fiddle Demo
| DIVISION | MONTH | YEAR | KPIAVALUE | KPIBVALUE | KPICVALUE |
---------------------------------------------------------------
| A | 2 | 2012 | 15.5 | 15.5 | 15.5 |
| B | 10 | 2012 | 15.5 | 17.5 | 20.24 |
| C | 12 | 2012 | 15.5 | (null) | 20.24 |
If you don't have a large number of "IDs", you could just transpose the values like this:
select
[Month],
[Year],
Division,
sum(case when KPI = 1 then Value else null end) as KPI1,
sum(case when KPI = 21 then Value else null end) as KPI21,
sum(case when KPI = 25 then Value else null end) as KPI25
from tblKPIDetails
group by
[Month],
[Year],
Division
order by
[Month],
[Year],
Division
Or same thing by using the "OVER" clause.
I think you want a conditional aggregation. But it is still not clear to me how the results are being defined. This might help you on your way:
SELECT Division, Month, Year,
1, max(case when kpi = 1 then value end) as kpi1value,
21, max(case when kpi = 21 then value end) as kpi21value,
25, max(case when kpi = 25 then value end) as kpi25value,
FROM tblKPIDetails J1
maybe you can try the following:
SELECT DISTINCT
t.Division,
t.Month,
t.Year,
KA.Value AS KPIAValue,
KB.Value AS KPIBValue,
KC.Value AS KPICValue
FROM
tblKPIDetails t
LEFT JOIN tblKPIDetails KA ON t.Division = KA.Division and t.Month = KA.month and .year = KA.year and KA.KPI = 1
LEFT JOIN tblKPIDetails KB ON t.Division = KB.Division and t.Month = KB.month and t.year = KB.year and KB.KPI = 21
LEFT JOIN tblKPIDetails KC ON t.Division = KC.Division and t.Month = KC.month and t.year = KC.year and KC.KPI = 25
Then is one LEFT JOIN for each possible KPI value you want.