Switch SQL result columns to rows and include a summary row beneath - sql

I'd like the below columns to populate in the place of rows and then include a summary row beneath it:
table1
ID NAME Value Group
001 Bob 100 A
002 Don 200 A
003 Fay 300 B
Below is an example of the desired output:
GROUP NO SUM
Group A 2 300
Group B 1 300
Total 3 600

select coalesce('Group '+ [group], 'Total') [Group], count([group]) No, sum(value) SUM
from table1
group by [group] with rollup
http://sqlfiddle.com/#!6/655f4/3

Declare #YourTable Table ([ID] varchar(50),[NAME] varchar(50),[Value] int,[Group] varchar(50))
Insert Into #YourTable Values
('001','Bob',100,'A')
,('002','Don',200,'A')
,('003','Fay',300,'B')
Select [Group] = IsNull('Group '+[Group],'Total')
,[No] = count(*)
,[Sum] = sum(Value)
From #YourTable
Group By rollup([Group])
Returns
Group No Sum
Group A 2 300
Group B 1 300
Total 3 600

Related

SQL Running Total Grouped By Limit

I am trying to determine how to group records together based the cumulative total of the Qty column so that the group size doesn't exceed 50. The desired group is given in the group column with sample data below.
Is there a way to accomplish this in SQL (specifically SQL Server 2012)?
Thank you for any assistance.
ID Qty Group
1 10 1
2 20 1
3 30 2 <- 60 greater than 50 so new group
4 40 3
5 2 3
6 3 3
7 10 4
8 25 4
9 15 4
10 5 5
You can use CTE to achieve the goal.
If one of the item exceeds Qty 50, a group still assign for it
DECLARE #Data TABLE (ID int identity(1,1) primary key, Qty int)
INSERT #Data VALUES (10), (20), (30), (40), (2), (3), (10), (25), (15), (5)
;WITH cte AS
(
SELECT ID, Qty, 1 AS [Group], Qty AS RunningTotal FROM #Data WHERE ID = 1
UNION ALL
SELECT data.ID, data.Qty,
-- The group limits to 50 Qty
CASE WHEN cte.RunningTotal + data.Qty > 50 THEN cte.[Group] + 1 ELSE cte.[Group] END,
-- Reset the running total for each new group
data.Qty + CASE WHEN cte.RunningTotal + data.Qty > 50 THEN 0 ELSE cte.RunningTotal END
FROM #Data data INNER JOIN cte ON data.ID = cte.ID + 1
)
SELECT ID, Qty, [Group] FROM cte
The following query gives you most of what you want. One more self-join of the result would compute the group sizes:
select a.ID, G, sum(b.Qty) as Total
from (
select max(ID) as ID, G
from (
select a.ID, sum(b.Qty) / 50 as G
from T as a join T as b
where a.ID >= b.ID
group by a.ID
) as A
group by G
) as a join T as b
where a.ID >= b.ID
group by a.ID
ID G Total
---------- ---------- ----------
2 0 30
3 1 60
8 2 140
10 3 160
The two important tricks:
Use a self-join with an inequality to get running totals
Use integer division to calculate group numbers.
I discuss this and other techniques on my canonical SQL page.
You need to create a stored procedure for this.
If you have Group column in your database then you have to take care about it while inserting a new record by fetching the max Group value and its sum of Qty column otherwise if you want Group column as computed in select statement then you have to code stored procedure accordingly.

How to maintain cumulative sum for each User in SQL server

I had a table like
ID UserID rupees time
1 1 200 2014-01-05
---------------------------------
2 1 500 2014-04-06
----------------------------------
3 2 10 2014-05-05
----------------------------------
4 2 20 2014-05-06
----------------------------------
I want the output lie
ID UserID Rupees time CumulativeSum
1 1 200 2014-01-05 200
-------------------------------------------------
2 1 500 2014-04-06 700
-------------------------------------------------
3 2 10 2014-05-06 10
-------------------------------------------------
4 2 20 2014-05-06 30
---------------------------------------------------
How can i get this table as purput
Please try using CTE:
;With T as(
select
*,
ROW_NUMBER() over(partition by UserId order by [time]) RN
from tbl
)
select
UserID,
rupees,
[time],
(select SUM(rupees)
from T b
where b.UserID=a.UserID and b.RN<=a.RN) CumulativeSum
from T a
For records with column value time increasing, try the below query:
select
UserID,
rupees,
[time],
(select SUM(rupees)
from tbl b
where b.UserID=a.UserID and b.[time]<=a.[time]) CumulativeSum
from tbl a
For SQL Server 2012 or later, you can use SUM() with an OVER clause that specifies a ROW clause:
declare #t table (ID int,UserID int,rupees int,[time] date)
insert into #t(ID,UserID,rupees,[time]) values
(1,1,200,'20140105'),
(2,1,500,'20140406'),
(3,2, 10,'20140505'),
(4,2, 20,'20140506')
select
*,
SUM(rupees) OVER (
PARTITION BY UserID
ORDER BY id /* or time? */
ROWS BETWEEN
UNBOUNDED PRECEDING AND
CURRENT ROW)
as total
from #t
Result:
ID UserID rupees time total
----------- ----------- ----------- ---------- -----------
1 1 200 2014-01-05 200
2 1 500 2014-04-06 700
3 2 10 2014-05-05 10
4 2 20 2014-05-06 30
DECLARE #t table (UserID INT,rupees INT,DateKey Date )
INSERT INTO #t VALUES
(1,200,'2014-01-05'),
(2,300,'2014-01-06'),
(2,800,'2014-03-06')
select UserID,
rupees,
DateKey,
(SELECT SUM(rupees)from #t t
where t.rupees <= tt.rupees) from #t tt
GROUP BY UserID,rupees,DateKey
Hope this too helps you.
DECLARE #tab TABLE (id INT,userId INT,rupees INT,[time] Date)
INSERT INTO #tab VALUES
(1,1,200 ,'2014-01-05'),
(2,1,500 ,'2014-04-06'),
(3,2,10 ,'2014-05-05'),
(4,2,20 ,'2014-05-06')
SELECT LU.id,LU.userId,LU.rupees,LU.time,SUM(b.rupees) CumulativeSum
FROM (SELECT *,ROW_NUMBER() OVER (PARTITION BY userId ORDER BY [time]) R FROM #tab) B
JOIN (SELECT *,ROW_NUMBER() OVER (PARTITION BY userId ORDER BY [time]) R FROM #tab) LU
ON B.userId = LU.userId AND B.R <= LU.R
GROUP BY LU.id,LU.userId,LU.rupees,LU.time
Result
I am assuming that you are not using SQL Server 2012, which provides the cumulative sum function. The other answers use some form of the row_number() function, but these seems totally unnecessary. I usually approach cumulative sums using correlated subqueries:
select ID, UserID, rupees, [time],
(select sum(rupees)
from table t2
where t2.UserId = t.UserId and
t2.ID <= t.ID
) as CumulativeSum
from table t;
This requires having a column that uniquely identifies each row, and that seems to be the purpose of id. For performance, I would want to have an index on table(UserId, ID, rupees).
select *, SUM(rupees) OVER (
PARTITION BY UserID
ORDER BY id) as CumSum from #tbl

SQL Counting Duplicates in a Column

I have been stuck on this problem for a while and have searched over the net for an answer..
My problem is:
I have duplicates in one column. I want to count how many duplicates there are in the one column and then I want to divide the a field by that count. I want to be able to do this for each record in the column as well.
Basically I want the script to behave like this
Count number of duplicates -> divide field A by count of duplicates.
Sample data:
t1.Invoiceno | t2.Amount | t2.orderno
-------------------------------------
201412 200 P202
201412 200 P205
302142 500 P232
201412 300 P211
450402 250 P102
450402 250 P142
450402 250 P512
Desired Result:
Invoiceno | Amount | orderno| duplicates|amount_new
-------------------------------------------------
201412 200 P202 2 100
201412 200 P205 2 100
302142 500 P232 1 500
201552 300 P211 1 300
450402 1200 P102 3 400
450402 1200 P142 3 400
450402 1200 P512 3 400
I do not want to insert new columns into the table, I just want the results to show the two new columns.
Here is one way:
select A / dups.dups
from t cross join
(select count(*) as dups
from (select onecol
from t
group by onecol
having count(*) > 1
) o
) dups
EDIT:
Well, now that the problem is clarified to something more reasonable. You can user a similar approach to the above, but the dups subquery needs to be aggregated by invoice and amount:
select amount / dups.dups as new_amount
from table t join
(select invoice, amount, count(*) as dups
from table t
) dups
on t.invoice = dups.invoice and t.amount = dups.amount;
Here is another way:
Declare #tempTable Table ( ID int , A int)
INSERT INTO #tempTable VALUES (1, 12)
INSERT INTO #tempTable VALUES (1, 12)
INSERT INTO #tempTable VALUES (2, 20)
INSERT INTO #tempTable VALUES (2, 24)
INSERT INTO #tempTable VALUES (2, 15)
INSERT INTO #tempTable VALUES (3, 10)
INSERT INTO #tempTable VALUES (5, 12)
-------------------------------------------
;WITH DupsCTE (ID, DuplicateCount) AS
(
SELECT ID, COUNT(*) AS DuplicateCount FROM #tempTable GROUP BY ID
)
SELECT t.ID, t.A,
c.DuplicateCount, t.A / c.DuplicateCount AS ModifiedA
FROM
#tempTable t
INNER JOIN DupsCTE c ON c.ID = t.ID

How to select Remain values after subtract with one Fixed value

Need To select Data From One Table After Minus With One Value
this is the question i already asked and this solution for one value input to table and result. but i need this with more input values for different categories and each categories output
for eg(based of previous question)
Table 1
SNo Amount categories
1 100 type1
2 500 type1
3 400 type1
4 100 type1
5 100 type2
6 200 type2
7 300 type2
8 500 type3
9 100 type3
and
values for type1 - 800
values for type2 - 200
values for type3 - 100
and the output need is
for type-1
800 - 100 (Record1) = 700
700 - 500 (record2) = 200
200 - 400 (record3) = -200
The table records starts from record 3 with Balance Values Balance 200
Table-Output
SNo Amount
1 200
2 100
that means if minus 800 in first table the first 2 records will be removed and in third record 200 is Balance
same operation for remain types also and how to do it?
SQLFiddle demo
with T1 as
(
select t.*,
SUM(Amount) OVER (PARTITION BY [Type] ORDER BY [SNo])
-
CASE WHEN Type='Type1' then 800
WHEN Type='Type2' then 200
WHEN Type='Type3' then 100
END as Total
from t
)select Sno,Type,
CASE WHEN Amount>Total then Total
Else Amount
end as Amount
from T1 where Total>0
order by Sno
UPD: If types are not fixed then you should create a table for them, for example:
CREATE TABLE types
([Type] varchar(5), [Value] int);
insert into types
values
('type1',800),
('type2',200),
('type3',100);
and use the following query:
with T1 as
(
select t.*,
SUM(Amount) OVER (PARTITION BY t.[Type] ORDER BY [SNo])
-
ISNULL(types.Value,0) as Total
from t
left join types on (t.type=types.type)
)select Sno,Type,
CASE WHEN Amount>Total then Total
Else Amount
end as Amount
from T1 where Total>0
order by Sno
SQLFiddle demo
UPDATE: For MSSQL 2005 just replace SUM(Amount) OVER (PARTITION BY t.[Type] ORDER BY [SNo]) with (select SUM(Amount) from t as t1
where t1.Type=t.Type
and t1.SNo<=t.SNo)
with T1 as
(
select t.*,
(select SUM(Amount) from t as t1
where t1.Type=t.Type
and t1.SNo<=t.SNo)
-
ISNULL(types.Value,0) as Total
from t
left join types on (t.type=types.type)
)select Sno,Type,
CASE WHEN Amount>Total then Total
Else Amount
end as Amount
from T1 where Total>0
order by Sno
SQLFiddle demo

SUM() data in a column based on another column data

I have a sql table
Project ID Employee ID Total Days
1 100 1
1 100 1
1 100 2
1 100 6
1 200 8
1 200 2
Now i need this table to look like
Project ID Employee ID Total Days
1 100 10
1 200 10
As iam new to sql,i am little confuse to use SUM() based on above condition.
This query below produces two columns: EmployeeID, totalDays.
SELECT EmployeeID, SUM(totalDays) totalDays
FROM tableName
GROUP BY EmployeeID
follow-up question: why is in your desired result the projectId is 1 and 2?
Here are two approaches
Declare #t Table(ProjectId Int, EmployeeId Int,TotalDays Int)
Insert Into #t Values(1,100,1),(1,100,1),(1,100,2),(1,100,6),(1,200,8),(1,200,2)
Approach1:
Select ProjectId,EmployeeId,TotalDays = Sum(TotalDays)
From #t
Group By ProjectId,EmployeeId
Approach2:
;With Cte As(
Select
ProjectId
,EmployeeId
,TotalDays = Sum(TotalDays) Over(Partition By EmployeeId)
,Rn = Row_Number() Over(Partition By EmployeeId Order By EmployeeId)
From #t )
Select ProjectId,EmployeeId,TotalDays
From Cte Where Rn = 1
Result
ProjectId EmployeeId TotalDays
1 100 10
1 200 10
select min("Project ID")as 'Project ID',"Employee ID"
, SUM("Total Days") as 'Total Days'
from table1
group by "Employee ID"