Subtract value from group of same table - sql

Year Month Code Value
2021 January 201 100.00
2021 February 201 250.00
2021 January 202 300.00
2021 February 202 200.00
2021 March 201 50.00
2021 March 202 150.00
Need to subtract code 201 from 202 , grouping both code by month.
Output :
Year Month Value
2021 january 200
2021 february -50
2021 March 100
I was trying to get desired output but its not working..
SELECT Code,Value
,
(
SELECT sum(Value) as Value
FROM [TestD]
Where Code = '202'
GROUP BY Code
)-(
SELECT sum(Value) as Value
FROM [TestD]
where Code = '201'
GROUP BY Code
) AS item_total
FROM [TestD] group by Code,Value

You can use a case expression inside sum(...), with group by Year, Month:
select Year,
Month,
sum(case when Code = '201' then -Value when Code = '202' then Value end) as Value
from TestD
group by Year, Month
Fiddle

You can try this provided only 2 values are going to be subtracted
declare #tbl table (year int, [month] varchar(50),code int, [value] int)
insert into #tbl values(2021, 'January', 201, 100.00),(2021, 'Feb', 201, 250.00)
,(2021, 'January', 202, 300.00),(2021, 'Feb', 202, 200.00)
select year,[month],[value], row_number()over(partition by [month] order by [value] desc) rownum
into #temp
from #tbl
select year,
month
,case when rownum = 1 then value else (
(select value from #temp t1 where t1.[month] = t.[month] and t1.rownum = 1) -
value) end as diff
from #temp t
where t.rownum = 2
order by month desc
drop table #temp

Related

DB2, group and sum by year where the year is a column value

I currently have a db2 query like so:
SELECT
NUMBER ,
YEAR(newDate),
dollarAMT - CAST ( coupon * dollarAMT AS DECIMAL ( 12 , 2 ) ) AS TOTAL
FROM ORDERING A
LEFT JOIN table.clients CL ON CL . clientNUmber = A . clientNumber
WHERE
newDate >= '2019-01-01'
order by newDate asc;
and this gives values like this:
123 2019 100.00
123 2019 200.00
123 2020 500.00
123 2020 800.00
123 2021 1000.00
I'm trying to get this result though:
Number 2019 2020 2021
123 300.00 1300.00 1000.00
How can I alter this in DB2 so that the years become column names and I group the sales by year?
you need to pivot your data, but DB2 doesn't support PIVOT , so you can do something like this:
select
number,
sum(case when YEAR(newDate) = 2019 then (dollarAMT - CAST ( coupon * dollarAMT AS DECIMAL ( 12 , 2 ) ) ) end) as 2019,
sum(case when YEAR(newDate) = 2020 then (dollarAMT - CAST ( coupon * dollarAMT AS DECIMAL ( 12 , 2 ) ) ) end) as 2020,
sum(case when YEAR(newDate) = 2021 then (dollarAMT - CAST ( coupon * dollarAMT AS DECIMAL ( 12 , 2 ) ) ) end) as 2021,
FROM ORDERING A
LEFT JOIN table.clients CL ON CL.clientNUmber = A.clientNumber
WHERE newDate >= '2019-01-01'
group by number
order by number asc;
but if you can show them in rows instead of columns , it make the query much simpler and it doesn't need to be modified for each upcoming year:
SELECT
NUMBER,
YEAR(newDate),
sum(dollarAMT - CAST (coupon * dollarAMT AS DECIMAL (12, 2))) AS TOTAL
FROM ORDERING A
LEFT JOIN table.clients CL ON CL.clientNUmber = A.clientNumber
WHERE newDate >= '2019-01-01'
group by NUMBER, YEAR(newDate)
order by NUMBER, YEAR(newDate) asc;

Return the YTD value if nothing for ABC

The records for timeframe for orderyear 2019 field will not bring ABC until we receive the first entry for that year starting Jan of next year (2020). As a workaround, I do not want to leave with no value. Is there anyways until we start to see the numbers flow through for the ABC for orderyear 2019, I want YTD numbers to show up instead replacing the YTD value with ABC. I am using SSMS 2016. Can someone please provide assistance.
My Query:
select
orderyear, TimeFrame, min(fiscaldate) minimumdate,
max(fiscaldate) maximumdate, sum(fiscalrevenue) revenue
from mytable
group by orderyear, TimeFrame
order by 1
orderyear timeframe minimumdate maximumdate revenue
2017 ABC 2018-01-01 2018-04-30 1056.38
2017 YTD 2017-01-04 2017-09-28 159.54
2018 ABC 2019-01-01 2019-04-30 1026.10
2018 YTD 2018-04-24 2018-09-27 2461.11
2019 YTD 2019-04-25 2019-09-25 3494.06
Requested:
orderyear timeframe minimumdate maximumdate revenue
2017 ABC 2018-01-01 2018-04-30 1056.38
2017 YTD 2017-01-04 2017-09-28 159.54
2018 ABC 2019-01-01 2019-04-30 1026.10
2018 YTD 2018-04-24 2018-09-27 2461.11
2019 YTD 2019-04-25 2019-09-25 3494.06
**2019 ABC 2019-04-25 2019-09-25 3494.06**
I think you just want union all:
with ot as (
select orderyear, TimeFrame, min(fiscaldate) as minimumdate,
max(fiscaldate) as maximumdate, sum(fiscalrevenue) as revenue
from mytable
group by orderyear, TimeFrame
)
select ot.*
from ot
union all
select ot.orderyear, 'ABC', ot.minimumdate, ot.maximumdate, ot.revenue
from ot
where ot.orderyear = year(getdate()) and ot.TimeFrame = 'YTD' and
not exists (select 1
from ot ot2
where ot2.ot.orderyear = year(getdate()) and ot.TimeFrame = 'ABC'
)
order by 1, 2;
EDIT:
If you want this with one reference to the underlying table, I would suggest:
select orderyear, timeframe, minimumdate, maximumdate, revenue
from (select orderyear, TimeFrame, min(fiscaldate) as minimumdate,
max(fiscaldate) as maximumdate, sum(fiscalrevenue) as revenue,
max(orderyear) over () as max_year,
max(case when TimeFrame = 'ABC' then orderyear end) over () as max_abc_year
from mytable
group by orderyear, TimeFrame
) t cross apply
(values (1, orderyear, timeframe, minimumdate, maximumdate, revenue),
(2, orderyear, 'ABC', minimumdate, maximumdate, revenue)
) v(which, orderyear, timeframe, minimumdate, maximumdate, revenue)
where which = 1 or
(orderyear = max_year) and max_abc_year < max_abc_year);
This runs the query and uses cross apply to duplicate the rows. It selects the duplicated row only for the maximum year when there is no "ABC" record for that year.
With a window function, you can identify which yearly records don't have an 'abc'. Then in a cross apply you can double up on those records. Then proceed as usual from there:
select orderyear,
ap.TimeFrame,
minimumDate = min(minimumDate),
maximumDate = max(maximumDate),
sum(revenue) revenue
from (
select *,
hasABC =
max(iif(timeFrame = 'abc', 1, 0))
over (partition by orderYear)
from mytable
) t
cross apply (
select timeframe union all
select 'ABC' where t.hasABC = 0
) ap
group by orderyear,
ap.TimeFrame
order by orderYear, timeframe

My Sql PIVOT Query Is Not Working As Intended

I'm using the following SQL query to return a table with 4 columns Year, Month, Quantity Sold, Stock_Code,
SELECT yr, mon, sum(Quantity) as Quantity, STOCK_CODE
FROM [All Stock Purchased]
group by yr, mon, stock_code
order by yr, mon, stock_code
This is an example of some of the data BUT I have about 3000 Stock_Codes and approx 40 x yr/mon combinations.
yr mon Quantity STOCK_CODE
2015 4 42 100105
2015 4 220 100135
2015 4 1 100237
2015 4 2 100252
2015 4 1 100277
I want to pivot this into a table which has a row for each SKU and columns for every Year/Month combination.
I have never used Pivot before so have done some research and have created a SQL query that I believe should work.
select * from
(SELECT yr,
mon, Quantity,
STOCK_CODE
FROM [All Stock Purchased]) AS BaseData
pivot (
sum(Quantity)
For Stock_Code
in ([4 2015],[5 2015] ...........
) as PivotTable
This query returns a table with Yr as col1, Mon as col2 and then 4 2015 etc as subsequent columns. Whereas I want col1 to be Stock_Code and col2 to show the quantity of that stock code sold in 4 2015.
Would really like to understand what is wrong with my code above please.
The following query using dynamic PIVOT should do what you want:
CREATE TABLE #temp (Yr INT,Mnt INT,Quantity INT, Stock_Code INT)
INSERT INTO #temp VALUES
(2015,4,42,100105),
(2015,4,100,100105),
(2015,5,220,100135),
(2015,4,1,100237),
(2015,4,2,100252),
(2015,7,1,100277)
DECLARE #pvt NVARCHAR(MAX) = '';
SET #pvt = STUFF(
(SELECT DISTINCT N', ' + QUOTENAME(CONVERT(VARCHAR(10),Mnt) +' '+ CONVERT(VARCHAR(10),Yr)) FROM #temp FOR XML PATH('')),1,2,N'');
EXEC (N'
SELECT pvt.* FROM (
SELECT Stock_Code
,CONVERT(VARCHAR(10),Mnt) +'' ''+ CONVERT(VARCHAR(10),Yr) AS [Tag]
,Quantity
FROM #temp )a
PIVOT (SUM(Quantity) FOR [Tag] IN ('+#pvt+')) pvt');
Result is as below,
Stock_Code 4 2015 5 2015 7 2015
100105 142 NULL NULL
100135 NULL 220 NULL
100237 1 NULL NULL
100252 2 NULL NULL
100277 NULL NULL 1
You can achieve this without using pivoting.
SELECT P.`STOCK_CODE`,
SUM(
CASE
WHEN P.`yr`=2015 AND P.`mon` = '1'
THEN P.`Quantity`
ELSE 0
END
) AS '1 2015',
SUM(
CASE
WHEN P.`yr`=2015 AND P.`mon` = '2'
THEN P.`Quantity`
ELSE 0
END
) AS '2 2015',
SUM(
CASE
WHEN P.`yr`=2015 AND P.`mon` = '3'
THEN P.`Quantity`
ELSE 0
END
) AS '3 2015',
FROM [All Stock Purchased] P
GROUP BY P.`STOCK_CODE`;

db2 compare year and month side by side

I need to compare side by side the companies values by current year vs last year and current month with same month of the previous year.
I use this query to get the values
SELECT STORE, SUM(TOTAL) as VAL, DATE FROM MYTABLE
WHERE DATE=CURRENT_DATE GROUP BY STORE ORDER BY STORE
below the results
STORE | VAL | DATE
1 10 CURRENT_DATE (2018-27-03)
1 20 2018-26-03
1 30 2018-25-03
2 20 CURRENT_DATE (2018-27-03)
2 20 2018-26-02
and i need this
STORE | VALUE CURRENT YEAR | VALUE LAST YEAR
1 60 30 (CALCULATED)
2 40 50 (CALCULATED)
STORE | VALUE CURRENT MONTH | VALUE SAME MONTH OF LAST YEAR
1 60 30 (CALCULATED)
2 20 50 (CALCULATED)
Thank you
You could just join two sub-selects together.
E.g with this DDL and Data
CREATE TABLE MYTABLE (STORE int, VAL int, D DATE);
INSERT INTO MYTABLE VALUES
( 1, 10, '2018-03-27')
,( 1, 20, '2018-03-26')
,( 1, 10, '2018-02-25')
,( 1, 35, '2017-03-25')
,( 2, 20, '2018-03-27')
,( 2, 15, '2017-03-26');
This will get you current month and last month last year values
SELECT C.*, LY.VAL_CURR_MONTH_LY
FROM (
SELECT STORE, SUM(VAL) as VAL_CURR_MONTH
FROM MYTABLE WHERE INT(D)/100=INT(CURRENT_DATE)/100
GROUP BY STORE ) AS C
LEFT JOIN
(SELECT STORE
, SUM(VAL) AS VAL_CURR_MONTH_LY
FROM MYTABLE
WHERE INT(D)/100 = INT(CURRENT_DATE)/100 -100
GROUP BY STORE ) LY
ON
C.STORE = LY.STORE
Then this for years
SELECT C.*, LY.VAL_LY
FROM (
SELECT STORE, SUM(VAL) as VAL_CURR_YEAR
FROM MYTABLE WHERE INT(D)/10000=INT(CURRENT_DATE)/10000
GROUP BY STORE ) AS C
LEFT JOIN
(SELECT STORE
, SUM(VAL) AS VAL_LY
FROM MYTABLE
WHERE INT(D)/10000 = INT(CURRENT_DATE)/10000 -1
GROUP BY STORE ) LY
ON
C.STORE = LY.STORE
P.S. there are many other ways to manipulate dates, but casting to INT is maybe one of the easier ways
Also, here is a more flexible way to get the "Same Month of Last Year" value. A similar method can get "last Year" values.
SELECT T.*
, AVG(VAL) OVER(
PARTITION BY STORE
ORDER BY YEAR_MONTH
RANGE BETWEEN 101 PRECEDING AND 100 PRECEDING
) AS SAME_MONTH_PREV_YEAR
FROM
( SELECT STORE
, INTEGER(D)/100 AS YEAR_MONTH
, SUM(VAL) AS VAL
FROM
MYTABLE T
GROUP BY
STORE
, INTEGER(D)/100
) AS T
;
Gives
STORE YEAR_MONTH VAL SAME_MONTH_PREV_YEAR
----- ---------- --- --------------------
1 201703 35 NULL
1 201802 10 NULL
1 201803 30 35
2 201703 15 NULL
2 201803 20 15
It is better to avoid functions on table columns in where clauses. Check following SQLs which are based on P. Vernon sample table.
Note: These SQLs are for DB2 LUW 11.1
For month:
SELECT STORE,
SUM(CASE WHEN YEAR(D) = year(current date) THEN val
ELSE 0 END) as VAL_CURR_MONTH,
SUM(CASE WHEN YEAR(D) = year(current date) - 1 THEN vaL
ELSE 0 END) as VAL_CURR_MONTH_LY
FROM MYTABLE
WHERE D between first_day(current date) and last_day(current date)
or D between first_day(current date - 1 year) and last_day(current date - 1 year)
GROUP BY STORE
ORDER BY STORE
For year:
SELECT STORE, SUM(CASE WHEN YEAR(D) = year(current date) THEN val
ELSE 0 END) as VAL_CY,
SUM(CASE WHEN YEAR(D) = year(current date) - 1 THEN vaL
ELSE 0 END) as VAL_LY
FROM MYTABLE
WHERE D between first_day(current date - (month(current date) - 1) months)
and last_day(current date + (12 - month(current date)) months)
or D between first_day(current date - (month(current date) - 1) months - 1 year)
and last_day(current date + (12 - month(current date)) months - 1 year)
GROUP BY STORE
ORDER BY STORE

in sql how to arrange rows of data into colum using Pivot

Here i have a simple table i want to display all rows respected that year display in single column
Year Month Amt
1999 Jan 520
1999 Feb 100
199 Mar 200
2000 Jan 500
2000 Feb 200
I want to display these table as
Year Jan Feb Mar
1999 520 100 200
2000 500 200 null
I had Written query as invoice its my table name
select
[Jan] as January,
[Feb] as Feburary,
[March] as Feburary,
from(
select Year,month,amount from invoice)x
PIVOT(
sum(amount)
for month in([jan],[Feb],[March])
)p
Please Try This Query
create table #Invoice
(
ID int identity(1,1),
Year varchar(4),
Month varchar(3),
Amount int
)
insert into #Invoice (Year, Month, Amount) values ('1999','Jan',520),('1999','Feb',100),('1999','Mar',200),
('2000','Jan',500),('2000','Feb',200)
select Year, [Jan], [Feb],[Mar],[Grand Total]
from (
select Year, Month, Amount
from #Invoice
Union all
select Year, 'Grand Total', SUM(Amount)
from #Invoice
group by year
)dd
pivot (
sum(Amount) for Month in ([Jan], [Feb],[Mar],[Grand Total])
) piv
drop table #Invoice
You can achieve it using CTE. You can't GROUP BY YEAR within the PIVOT table operator, the PIVOT operator infers the grouped columns automatically. This seems to work
WITH Pivoted
AS
(
SELECT *
FROM table1
PIVOT
(
sum([Amt]) FOR [month] IN ( [jan],[Feb],[Mar])
) AS p
)
SELECT
Year,
sum([Jan]) as January,
sum([Feb]) as Feburary,
sum([Mar]) as March
FROM Pivoted
GROUP BY Year;
REXTESTER DEMO