Reputation: 1591
I have a table as follows
fab_id x y z m
12 14 10 3 5
12 10 10 3 4
Here im using group by clause on id .Now i want to subtract those column values which have similar id. e.g group by on id (12). Now to subtract (14-10)X, (10-10)Y, (3-3)z, (5-4)m
I know there is a aggregate function sum for addition but is there any function which i can use to subtract this value.
Or is there any other method to achieve the results.
Note- There may be a change that value may come in -ve. So any function handle this?
one more example - (order by correction_date desc so result will show recent correction first)
fab_id x y z m correction_date
14 20 12 4 4 2014-05-05 09:03
14 24 12 4 3 2014-05-05 08:05
14 26 12 4 6 2014-05-05 07:12
so result to achieve group by on id (14). Now to subtract (26-20)X, (12-12)Y, (4-4)z, (6-4)m
Upvotes: 0
Views: 12298
Reputation: 542
;with Ordered as
(
select
fab_id,x,y,z,m,date,
row_Number() over (partition by fab_id order by date desc) as Latest,
row_Number() over (partition by fab_id order by date) as Oldest
from fab
)
select
O1.fab_id,
O1.x-O2.x,
O1.y-O2.y,
O1.z-O2.z,
O1.m-O2.m
from Ordered O1
join Ordered O2 on
O1.fab_id = O2.fab_id
where O1.latest = 1 and O2.oldest = 1
Upvotes: 1
Reputation: 95101
Now, that you have given more information on how to deal with more records and that you revealed that there is a time column involved, here is a possible solution. The query selects the first and last record per fab_id and subtracts the values:
select
fab_info.fab_id,
earliest_fab.x - latest_fab.x,
earliest_fab.y - latest_fab.y,
earliest_fab.z - latest_fab.z,
earliest_fab.m - latest_fab.m
from
(
select
fab_id,
min(correction_date) as min_correction_date,
max(correction_date) as max_correction_date
from fab
group by fab_id
) as fab_info
inner join fab as earliest_fab on
earliest_fab.fab_id = fab_info.fab_id and
earliest_fab.min_correction_date = fab_info.min_correction_date
inner join fab as latest_fab on
latest_fab.fab_id = fab_info.fab_id and
latest_fab.min_correction_date = fab_info.max_correction_date;
Upvotes: 2
Reputation: 13106
Unfortunately, it's SQL Server 2012 that has the handy FIRST_VALUE()
/LAST_VALUE()
OLAP functions, so in the case of more than 2 rows we have to do something a little different:
SELECT fab_id, SUM(CASE WHEN latest = 1 THEN -x ELSE x END) AS x,
SUM(CASE WHEN latest = 1 THEN -y ELSE y END) AS y,
SUM(CASE WHEN latest = 1 THEN -z ELSE z END) AS z,
SUM(CASE WHEN latest = 1 THEN -m ELSE m END) AS m
FROM (SELECT fab_id, x, y, z, m,
ROW_NUMBER() OVER(PARTITION BY fab_id
ORDER BY correction_date ASC) AS earliest,
ROW_NUMBER() OVER(PARTITION BY fab_id
ORDER BY correction_date DESC) AS latest
FROM myTable) fab
WHERE earliest = 1
OR latest = 1
GROUP BY fab_id
HAVING COUNT(*) >= 2
(and working fiddle. Thanks to @AK47 for the initial setup.)
Which yields the expected:
FAB_ID X Y Z M
12 4 0 0 1
14 6 0 0 2
Note that HAVING COUNT(*) >= 2
is so that only rows with changes are considered (you'd get some null result columns otherwise).
Upvotes: 1
Reputation: 9042
A CTE could help:
WITH cte AS (
SELECT
-- Get the row numbers per fab_id ordered by the correction date
ROW_NUMBER() OVER (PARTITION BY fab_id ORDER BY correction_date ASC) AS rid
, fab_id, x, y, z, m
FROM
YourTable
)
SELECT
fab_id
-- If the row number is 1 then, this is our base value
-- If the row number is not 1 then, we want to subtract it (or add the negative value)
, SUM(CASE WHEN rid = 1 THEN x ELSE x * -1 END) AS x
, SUM(CASE WHEN rid = 1 THEN y ELSE y * -1 END) AS y
, SUM(CASE WHEN rid = 1 THEN z ELSE z * -1 END) AS z
, SUM(CASE WHEN rid = 1 THEN m ELSE m * -1 END) AS m
FROM
cte
GROUP BY
fab_id
Remember, 40-10-20 equals to 40 + (-10) + (-20)
Upvotes: -1
Reputation: 3807
I think if you have consistent set or two rows, then following code should work for you.
select fab_id ,max(x) - min(x) as x
,max(y) - min(y) as y
,max(z) - min(z) as z
,max(m) - main(m) as m
from Mytable
group by fab_id
It will work, even if you get more than 2 rows in a group, but subtraction will be from max value of min value. hope it helps you.
EDIT : SQL Fiddle DEMO
Upvotes: -1
Reputation: 4091
Seeing as you say there will always be two rows, you can simply do a 'self join' and subtract the values from each other:
SELECT t1.fab_id, t1.x - t2.x as diffx, t1.y - t2.y as diffy, <remainder columns here>
from <table> t1
inner join <table> t2 on t1.fab_id = t2.fab_id and t1.correctiondate > t2.correctiondate
If you have more than two rows, then you'll need to make subqueries or use window ranking functions to figure out the largest and smallest correctiondate for each fab_id and then you can do the very same as above by joining those two subqueries together instead of
Upvotes: 1
Reputation: 95101
Provided you always want to subtract the least value from the greatest value:
select
fab_id,
max(x) - min(x),
max(y) - min(y),
max(z) - min(z),
max(m) - min(m)
from fab
group by fab_id;
Upvotes: 1