SQL server Query for SUM group - sql-server

I have table:
-------------------------------------
| Code | Name | Type | Qty |
-------------------------------------
| 0001 | Mouse | Purchase | 5 |
| 0002 | KeyBrd | Purchase | 8 |
| 0003 | Mouse | Sale | 2 |
-------------------------------------
i want to select them like this:
------------------------------------------------------------
| Code | Name | Total Purchase | Total Sale | Stock |
------------------------------------------------------------
| 0001 | Mouse | 5 | 2 | 3 |
| 0002 | KeyBrd | 8 | 0 | 8 |
------------------------------------------------------------
Im so confused, please help me with the query, thank you in advance

Try This
SELECT
Code,
Name,
TotalPurchase = SUM(CASE WHEN [Type] = 'Purchase' THEN Qty ELSE 0 END),
TotalSale = SUM(CASE WHEN [Type] = 'Sale' THEN Qty ELSE 0 END),
Stock = SUM(CASE WHEN [Type] = 'Purchase' THEN Qty ELSE 0 END) - SUM(CASE WHEN [Type] = 'Sale' THEN Qty ELSE 0 END)
FROM YourTable
group by Code,
Name

Conditional aggregation with the help of case expression is one approach
select min(Code) Code,
Name,
sum(case when Type = 'Purchase' then Qty else 0 end) [Total Purchase],
sum(case when Type = 'Sale' then Qty else 0 end) [Total Sale],
sum(case when Type = 'Purchase' then -Qty else Qty end) Stock
from table t
group by Name

We can also use like below.(Using Pivot) Please try this.
Data Generation
CREATE TABLE TableCase
(
Code VARCHAR(10)
,[Name] VARCHAR(10)
,[Type] VARCHAR(20)
,Qty INT
)
GO
INSERT INTO TableCase VALUES
('0001','Mouse' ,'Purchase',5),
('0002','KeyBrd' ,'Purchase',8),
('0003','Mouse' ,'Sale',2 )
GO
SOLUTION
SELECT MIN(Code) Code,[Name]
, ISNULL(SUM([Purchase]),0) [TotalPurchase]
, ISNULL(SUM([Sale]),0) TotalSale
, ISNULL(SUM([Purchase]),0) - ISNULL(SUM([Sale]),0) Stock
FROM TableCase
PIVOT
(
MAX(Qty) FOR [Type] IN ([Purchase],[Sale])
)t
GROUP By [Name]
ORDER BY [code]
OUTPUT
Code Name TotalPurchase TotalSale Stock
---------- ---------- ------------- ----------- -----------
0001 Mouse 5 2 3
0002 KeyBrd 8 0 8
(2 rows affected)

Related

Pivot table with custom values

i have table say FIDDLE HERE
+----+------+------+-----+-----+
| id | year | sell | buy | own |
+----+------+------+-----+-----+
| 1 | 2016 | 9 | 2 | 10 |
| 1 | 2017 | 9 | | 10 |
| 1 | 2018 | | 2 | 10 |
| 2 | 2016 | 7 | 2 | 11 |
| 2 | 2017 | 2 | | |
| 2 | 2018 | | | 18 |
+----+------+------+-----+-----+
create table test(id varchar(20), year varchar(20),
sell varchar(20), buy varchar(20),
own varchar(20));
insert into test values('1', '2016','9','2','10' )
insert into test values('1', '2017','9',NULL,'10' )
insert into test values('1', '2018',NULL,'2','10' )
insert into test values('2', '2016','7','2','11' )
insert into test values('2', '2017','2',NULL,'17' )
insert into test values('2', '2018','5','2','18' )
I'm trying to PIVOT but instead of aggregate the values, i wanted to keep some letters if it is not null (S-Sell,B-Buy,O-Own). If there are values for all columns for particular year then i need S_B_O for that year. If there are values only for sell and buy then S_B etc., so Expected output is
+----+-------+------+------+
| ID | 2016 | 2017 | 2018 |
+----+-------+------+------+
| 1 | S_B_O | S_O | B_O |
+----+-------+------+------+
| 2 | S_B_O | S | O |
+----+-------+------+------+
The closest i have got is using conditional aggrgarion( MAX and concat) instead of PIVOT but this is also giving null if any one is NULL. Please suggest a solution.
select ID,
MAX(CASE WHEN Year = '2016' AND sell is not null THEN 'S_' END +
CASE WHEN Year = '2016' AND buy is not null THEN 'B_' END +
CASE WHEN Year = '2016' AND own is not null THEN 'O' END)
AS [2016],
MAX(CASE WHEN Year = '2017' AND sell is not null THEN 'S_' END +
CASE WHEN Year = '2017' AND buy is not null THEN 'B_' END +
CASE WHEN Year = '2017' AND own is not null THEN 'O' END)
AS [2017]
/* ......for all year */
from test
group by id
FIDDLE HERE
You can use CONCAT function, which will handle NULLs automatically.
select ID,
CONCAT(MAX(CASE WHEN Year = '2016' AND sell is not null THEN 'S_' END) ,
MAX(CASE WHEN Year = '2016' AND buy is not null THEN 'B_' END) ,
MAX(CASE WHEN Year = '2016' AND buy is not null THEN 'O' END))
AS [2016],
CONCAT(MAX(CASE WHEN Year = '2017' AND sell is not null THEN 'S_' END) ,
MAX(CASE WHEN Year = '2017' AND buy is not null THEN 'B_' END) ,
MAX(CASE WHEN Year = '2017' AND buy is not null THEN 'O' END))
AS [2017]
from test
group by id
+----+-------+------+
| ID | 2016 | 2017 |
+----+-------+------+
| 1 | S_B_O | S_ |
| 2 | S_B_O | S_ |
+----+-------+------+
UPDATE Dynamic query. As #Larnu told, You should have asked this as separate question. You should not change the requirement.
DECLARE #lst_Years NVARCHAR(MAX) , #query NVARCHAR(MAX)
SET #lst_Years = STUFF((SELECT distinct ',' + QUOTENAME([Year])
FROM test
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #query = 'SELECT * FROM
(
select ID, [Year],
CONCAT(MAX(CASE WHEN sell is not null THEN ''S_'' END) ,
MAX(CASE WHEN buy is not null THEN ''B_'' END) ,
MAX(CASE WHEN buy is not null THEN ''O'' END))
AS [Value]
from test
group by id, [year]) as t
pivot
(
max(value) FOR YEAR IN (' + #lst_Years + ')
) as pvt'
EXEC(#query)
+----+-------+------+-------+
| ID | 2016 | 2017 | 2018 |
+----+-------+------+-------+
| 1 | S_B_O | S_ | B_O |
| 2 | S_B_O | S_ | S_B_O |
+----+-------+------+-------+

SQL Query to get in/out time

I need to create a report when the user entering and exiting time. So far I only manage to get the min and max time. Here, the example of table:
ID | Flag_Location (bit) | Time
----------------------------
1001 | 1 | 8:00
1001 | 1 | 9:00
1001 | 1 | 10:00
1001 | 0 | 11:00
1001 | 0 | 12:00
1001 | 1 | 13:00
1001 | 1 | 14:00
The output that I need for the report is like this :
ID | ENTERTIME | EXITTIME
-------------------------
1001 | 8:00 | 10:00
1001 | 13:00 | 14:00
So far I only manage to get 1 row of result :
ID | ENTERTIME | EXITTIME
-------------------------
1001 | 8:00 | 14:00
You can use the window function to create an ad-hoc Grp
Example
Select ID
,TimeIn = min(Time)
,TimeOut = max(Time)
From (
Select *
,Grp = sum(case when flag_location=0 then 1 else 0 end ) over (partition by id order by time)
From YourTable
) A
Where Flag_Location=1
Group By ID,Grp
Returns
ID TimeIn TimeOut
1001 08:00:00.0000000 10:00:00.0000000
1001 13:00:00.0000000 14:00:00.0000000
If it helps with the visualization, the nested query generates the following:
You can just bucket the to identify group by and do group by as below:
;with cte as (select *, bucket = sum(case when flag_location = 0 then 1 when flag_location = 1 and nextflag = 0 then 2 else 0 end) over (partition by id order by [time]),
[time] as endtime from
(
select *,
lag(flag_location) over(partition by id order by [time]) nextflag
from #table4
) a
)
select id, min([time]), max([time]) from cte
where flag_location = 1
group by id, bucket
Query results:
+------+------------------+------------------+
| id | Entertime | ExitTime |
+------+------------------+------------------+
| 1001 | 08:00:00.0000000 | 10:00:00.0000000 |
| 1001 | 13:00:00.0000000 | 14:00:00.0000000 |
+------+------------------+------------------+
Try below query (explanations in code)
declare #tbl table (ID int, Flag_Location bit, Time varchar(5));
insert into #tbl values
(1001,1,'8:00'),
(1001,1,'9:00'),
(1001,1,'10:00'),
(1001,0,'11:00'),
(1001,0,'12:00'),
(1001,1,'13:00'),
(1001,1,'14:00');
select ID,
cast(max(ts) as varchar(10)),
cast(min(ts) as varchar(10))
from (
select ID, ts, Flag_Location,
row_number() over (order by ts) -
row_number() over (partition by Flag_Location order by ts) grp
from (
select *,
-- add 0 at the beginning for correct cast and cast it to timestamp for correct ordering
cast(right('00000' + time, 5) as timestamp) ts
from #tbl
) a
) a where Flag_Location = 1
group by ID, grp

how to subtract values in consecutive rows?

I have following table with some data.
CREATE TABLE #NetProfit (ID int, [Name] varchar(50),[Class] varchar(50), Balance money)
go
--Populate Sample records
INSERT INTO #NetProfit VALUES(4,'Income','No Class',303386.8462)
INSERT INTO #NetProfit VALUES(6,'Expenses','No Class',22443.5317)
INSERT INTO #NetProfit VALUES(4,'Income','2 TestUser3',0.00)
INSERT INTO #NetProfit VALUES(5,'Cost','2 TestUser3',0.3875)
INSERT INTO #NetProfit VALUES(6,'Expenses','2 TestUser3',6439.2129)
INSERT INTO #NetProfit VALUES(5,'Cost','3 TestUser3',0.1395)
INSERT INTO #NetProfit VALUES(6,'Expenses','3 TestUser3',6451.6129)
INSERT INTO #NetProfit VALUES(5,'Cost','38 Code#1012',3.0225)
INSERT INTO #NetProfit VALUES(6,'Expenses','38 Code#1012',30.225)
go
select * from #NetProfit
drop table #NetProfit
+----+----------+--------------+-------------+
| ID | Name | Class | Balance |
+----+----------+--------------+-------------+
| 4 | Income | No Class | 303386.8462 |
| 6 | Expenses | No Class | 22443.5317 |
| 4 | Income | 2 TestUser3 | 0 |
| 5 | Cost | 2 TestUser3 | 0.3875 |
| 6 | Expenses | 2 TestUser3 | 6439.2129 |
| 5 | Cost | 3 TestUser3 | 0.1395 |
| 6 | Expenses | 3 TestUser3 | 6451.6129 |
| 5 | Cost | 38 Code#1012 | 3.0225 |
| 6 | Expenses | 38 Code#1012 | 30.225 |
+----+----------+--------------+-------------+
I want to subtract [Balance] column row wise group by [Class] column.
For ex. NetProfit = (Income - Cost - Expenses) for each [Class] column with group by [Class].
Here is the output I am expecting.
+-------------+-------------+-------------+--------------+
| No Class | 2 TestUser3 | 3 TestUser3 | 38 Code#1012 |
+-------------+-------------+-------------+--------------+
| 280943.3145 | 6439.6004 | 6451.7524 | 33.2475 |
+-------------+-------------+-------------+--------------+
Any help would be appreciated. Thanks!
This could be the first step to achieve desired result:
SELECT NP.Class, ABS(ISNULL(SUM(CASE NP.Name WHEN 'Income' THEN NP.Balance END), 0) - SUM(CASE NP.Name WHEN 'Income' THEN NULL ELSE NP.Balance END)) AS Balance
FROM #NetProfit AS NP
GROUP BY NP.Class;
Then you would have to dynamically pivot these results. Statically, your query should look like that:
SELECT *
FROM (
SELECT NP.Class, ABS(ISNULL(SUM(CASE NP.Name WHEN 'Income' THEN NP.Balance END), 0) - SUM(CASE NP.Name WHEN 'Income' THEN NULL ELSE NP.Balance END)) AS Balance
FROM #NetProfit AS NP
GROUP BY NP.Class) AS SourceTable
PIVOT (MAX(Balance) FOR Class IN ([2 TestUser3], [3 TestUser3], [38 Code#1012], [No Class])) AS PivotTable;
So by plugging in dynamic SQL, This is the result:
DECLARE #SQL NVARCHAR(MAX);
DECLARE #Col NVARCHAR(MAX);
SET #Col = STUFF(( SELECT DISTINCT ',' + QUOTENAME(NP.Class)
FROM #NetProfit AS NP
FOR XML PATH('')), 1, 1, '');
SET #SQL = N'
SELECT *
FROM (
SELECT NP.Class, ABS(ISNULL(SUM(CASE NP.Name WHEN ''Income'' THEN NP.Balance END), 0) - SUM(CASE NP.Name WHEN ''Income'' THEN NULL ELSE NP.Balance END)) AS Balance
FROM #NetProfit AS NP
GROUP BY NP.Class) AS SourceTable
PIVOT (MAX(Balance) FOR Class IN (' + #Col + N')) AS PivotTable';
EXECUTE sys.sp_executesql #SQL;
That's the output:
+-------------+-------------+--------------+-------------+
| 2 TestUser3 | 3 TestUser3 | 38 Code#1012 | No Class |
+-------------+-------------+--------------+-------------+
| 6439,6004 | 6451,7524 | 33,2475 | 280943,3145 |
+-------------+-------------+--------------+-------------+
This query return calculation in rows.
With pivot table you can turn rows to columns
select isnull(c1, c2), isnull(i, 0)-isnull(e,0) from (
select * from
(select class c1, sum(balance) i
from #NetProfit
where Name = 'income'
group by class) i
full join
(
select class c2, sum(balance) e
from #NetProfit
where Name != 'income'
group by class) e on i.c1 = e.c2) t
Try this (assuming ID 4, for income, is a positive whereas the rest should be treated as negatives;
select
[Class No]=SUM(case when [Class]='No Class' then (case when ID=4 then Balance else Balance*-1 end) else 0 end),
[TestUser2]=SUM(case when [Class]='2 TestUser3' then (case when ID=4 then Balance else Balance*-1 end) else 0 end),
[TestUser3]=SUM(case when [Class]='3 TestUser3' then (case when ID=4 then Balance else Balance*-1 end) else 0 end),
[Code#1012]=SUM(case when [Class]='Code#1012' then (case when ID=4 then Balance else Balance*-1 end) else 0 end)
from
#NetProfit

Rearranging a table in tsql

So I have a table with the following columns:
Type Test Min Max
-----------------------------
1 a 1 2
1 b Null Null
2 a 0 Null
2 b Null 1
Trying to get all of them like this
Type Test1 Test1 min Test1max Test2 Test2min Test2max
------------------------------------------------------------
1 a 1 2 b Null Null
2 a 0 Null b Null 1
Tried using unpivot first before I use pivot but it's still giving duplicate tests and removing null any help with this is much appreciated
Need null values to show up as well
Select type, result
(
From
Select type, min
From table t
)
Unpivot
(result for minmax in (min1)
)
Select
Thanks
using row_number() and conditional aggregation:
select
[Type]
, Test_1 = max(case when rn = 1 then Test end)
, Test_1_Min = max(case when rn = 1 then Min end)
, Test_1_Max = max(case when rn = 1 then Max end)
, Test_2 = max(case when rn = 2 then Test end)
, Test_2_Min = max(case when rn = 2 then Min end)
, Test_2_Max = max(case when rn = 2 then Max end)
from (
select *
, rn = row_number() over (partition by [Type] order by [Test])
from t
) as s
group by s.Type
rextester demo: http://rextester.com/BKL48976
returns:
+------+--------+------------+------------+--------+------------+------------+
| Type | Test_1 | Test_1_Min | Test_1_Max | Test_2 | Test_2_Min | Test_2_Max |
+------+--------+------------+------------+--------+------------+------------+
| 1 | a | 1 | 2 | b | NULL | NULL |
| 2 | a | 0 | NULL | b | NULL | 1 |
+------+--------+------------+------------+--------+------------+------------+
select *
from table t1
join table t2
on t1.type = t2.type
and t1.test < t2.test

How do convert row column values in to a column value

How do we convert row column values into column values.
for eg:-
I have 5 rows, each rows have 5 columns. I need to convert based on the ID.
My query is:
Select id,year,height,weight,date from user_det
---------------------------------------------------------
| Id | Year | height| weight| date |
---------------------------------------------------------
| 1 | 20082009 | 122 | 23 | 4/15/2009 |
---------------------------------------------------------
| 1 | 20092010 | 135 | 39 | 3/19/2010 |
---------------------------------------------------------
| 2 | 20082009 | 132 | 20 | 2/23/2009 |
---------------------------------------------------------
| 3 | 20142015 | 133 | 28 | 2/24/2015 |
---------------------------------------------------------
If I group by id maximum is 2. I need Result like below table
id | year1 | height1 |weight1 | date1 | year2 | height2|weight2|date2
-------------------------------------------------------------------------------
1 |20082009| 122 | 23 |4/15/2009| 20092010 | 135 | 39 |3/19/2010
--------------------------------------------------------------------------------
2 |20082009 | 135 | 20 |2/23/2009| | | |
--------------------------------------------------------------------------------
3 |20152015 | 133 | 28 |2/24/2015| | | |
You can do this with pivot or conditional aggregation. But, you need a column for the pivot:
select id,
max(case when seqnum = 1 then year end) as year_1,
max(case when seqnum = 1 then height end) as height_1,
max(case when seqnum = 1 then weight end) as weight_1,
max(case when seqnum = 1 then date end) as date_1,
max(case when seqnum = 2 then year end) as year_2,
max(case when seqnum = 2 then height end) as height_2,
max(case when seqnum = 2 then weight end) as weight_2,
max(case when seqnum = 2 then date end) as date_2
from (select t.*,
row_number() over (partition by id order by year) as seqnum
from user_det t
) t
group by id;

Resources