How to do Sum Operation in SQl Pivot Query - sql

Here i have a simple table Name Cust With CustName,PurshaseItem,Price.I wrote a Simple PIVOT query its Pivoting the data but i wana to show sum of the amot
Here i need Grand total
Pivot Query
[![select custName,
\[Shoes\] as Shoes,
\[Colgate\] as Colgate,
\[Cloths\] as Cloths
FROM
(select custName,Price,PurchaseItem FROM Cust
) AS PIVOTData
PIVOT(
sum(Price) FOR PurchaseItem
IN (Shoes,Colgate,Cloths)
)AS PIVOTING][1]][1]
custname Shoes Colgate GrandTotal
xyz 12 10 22
lmn 1 2 3

You can try this:
CREATE TABLE CUST (custName VARCHAR(10),
price INT,
PurchaseItem VARCHAR(10)
)
INSERT INTO CUST VALUES ('aaaa', 1,'Colgate')
INSERT INTO CUST VALUES ('aaaa', 2,'Shoes')
INSERT INTO CUST VALUES ('aaaa', 3,'Cloths')
INSERT INTO CUST VALUES ('bbbb', 4,'Colgate')
INSERT INTO CUST VALUES ('bbbb', 5,'Shoes')
INSERT INTO CUST VALUES ('bbbb', 6,'Cloths')
select *
FROM
(select custName, SUM(Price) AS Price ,
CASE WHEN GROUPING(PurchaseItem)=1 THEN 'TOT_PRICE' ELSE PurchaseItem END AS PurchaseItem
FROM Cust
group by rollup(PurchaseItem), custName
) AS PIVOTData
PIVOT(sum(Price) FOR PurchaseItem IN (Shoes,Colgate,Cloths,TOT_PRICE)) AS PIVOTING
Output:
custName Shoes Colgate Cloths TOT_PRICE
---------- ----------- ----------- ----------- -----------
aaaa 2 1 3 6
bbbb 5 4 6 15

You can just add this to your select
coalesce([Shoes], 0) + coalesce([Colgate], 0) + coalesce([Cloths], 0) as GranTotal
The coalesce is needed to avoid weird behaviours when one of the results is null.

Related

MSSQL distribute negative amount to positive amount rows

i think this is possible, but i stuck in the cumulative sums. I have a positive amounts and a single negative amount joined at the top. Any possible way to distribute this negative amount to collapse positive amount table?
ROW_NUM AMOUNT N_AMOUNT NEEDED_RESULT
------- ------ -------- -------------
1 100.00 NULL 100
2 300.00 NULL 200
3 300.00 -400.00 0
table example
DECLARE #T TABLE (ROW_NUM INT, AMOUNT MONEY,N_AMOUNT MONEY, NEEDED_RESULT MONEY)
INSERT INTO #T
SELECT * FROM (VALUES
(1, 100.00, NULL ,100),
(2, 300.00, NULL ,200),
(3, 300.00, -400.00 ,0 )
) a(ROW_NUM , AMOUNT ,N_AMOUNT , NEEDED_RESULT )
;WITH x AS
(
SELECT
*,
[R] = SUM(Amount) OVER (ORDER BY ROW_NUM DESC) + SUM(N_Amount) OVER ()
FROM #T
)
SELECT ROW_NUM,AMOUNT,N_AMOUNT, NEEDED_RESULT,
CASE
WHEN R < 0 THEN 0
WHEN R > Amount THEN Amount
ELSE R
END
FROM x
ORDER BY ROW_NUM

Select Max value For Each Entity

In Microsoft SQL Server, I have a table with columns EmployeeID, Category and Amount. How can I show just the Category with the highest Amount for each Employee?
Data Example:
EmployeeID Category Amount
11111 Vacation 4
11111 Personal 2
11111 Holiday 3
22222 Vacation 1
22222 Personal 3
22222 Holiday 2
33333 Personal 5
33333 Holiday 1
33333 Vacation 3
33333 Unspecified 3
Results:
EmployeeID Category Amount
11111 Vacation 4
22222 Personal 3
33333 Personal 5
Another option is the WITH TIES clause
Example
Select Top 1 with Ties *
From YourTable
Order By Row_Number() over (Partition By EmployeeID Order By Amount Desc)
Returns
Or using Row_Number() and a CTE
;with cte as (
Select *
,RN = Row_Number() over (Partition By EmployeeID Order By Amount Desc)
From YourTable
)
Select EmployeeID
,Category
,Amount
From cte where RN=1
Here are a few different options. Any of them can perform better than the other depending available indexes, so all three are worth testing in your environment.
SET NOCOUNT ON;
IF OBJECT_ID('tempdb..#TestData', 'U') IS NOT NULL
DROP TABLE #TestData;
CREATE TABLE #TestData (
EmployeeID INT NOT NULL,
Category VARCHAR(20) NOT NULL,
Amount MONEY NOT NULL
);
INSERT #TestData (EmployeeID, Category, Amount) VALUES
(11111, 'Vacation ', 4),
(11111, 'Personal ', 2),
(11111, 'Holiday ', 3),
(22222, 'Vacation ', 1),
(22222, 'Personal ', 3),
(22222, 'Holiday ', 2),
(33333, 'Personal ', 5),
(33333, 'Holiday ', 1),
(33333, 'Vacation ', 3),
(33333, 'Unspecified', 3);
-- add a covering index to improve performance of the various options
CREATE NONCLUSTERED INDEX ix_TestData ON #TestData (EmployeeID, Amount DESC) INCLUDE (Category);
------------------------------------------
IF OBJECT_ID('tempdb..#Employee', 'U') IS NOT NULL
DROP TABLE #Employee;
CREATE TABLE #Employee (
EmployeeID INT NOT NULL
);
INSERT #Employee (EmployeeID) VALUES
(11111), (22222), (33333);
-- SELECT * FROM #TestData td;
--============================================================
--============================================================
SELECT TOP 1 WITH TIES
td.EmployeeID, td.Category, td.Amount
FROM
#TestData td
ORDER BY
ROW_NUMBER() OVER (PARTITION BY td.EmployeeID ORDER BY td.Amount DESC);
--============================================================
SELECT
tdt.EmployeeID, tdt.Category, tdt.Amount
FROM
#Employee e
CROSS APPLY (
SELECT TOP 1
td.EmployeeID, td.Category, td.Amount
FROM
#TestData td
WHERE
e.EmployeeID = td.EmployeeID
ORDER BY
td.Amount DESC
) tdt;
--============================================================
WITH
cte_AddRN AS (
SELECT
td.EmployeeID, td.Category, td.Amount,
RN = ROW_NUMBER() OVER (PARTITION BY td.EmployeeID ORDER BY td.Amount DESC)
FROM
#TestData td
)
SELECT
ar.EmployeeID, ar.Category, ar.Amount
FROM
cte_AddRN ar
WHERE
ar.RN = 1;
All 3 produce the same results...
EmployeeID Category Amount
----------- -------------------- ---------------------
11111 Vacation 4.00
22222 Personal 3.00
33333 Personal 5.00
EmployeeID Category Amount
----------- -------------------- ---------------------
11111 Vacation 4.00
22222 Personal 3.00
33333 Personal 5.00
EmployeeID Category Amount
----------- -------------------- ---------------------
11111 Vacation 4.00
22222 Personal 3.00
33333 Personal 5.00
Use EXISTS/NOT EXISTS:
select *
from Employees
where not exists
(select * from Employees as E
where E.EmployeeID=Employees.EmployeeID -- join condition
and E.Amount>Employees.Amount) -- filter condition
SELECT EmployeeID, Category, Amount
FROM (select *, ROW_NUMBER() OVER (PARTITION BY EmployeeID
ORDER BY EmployeeID as rn ,Amount desc)
FROM getmaxdata) x WHERE x.rn =1

Transpose SQL table results using Pivot

I am trying to transpose column results in my table into row results. Here is the query that generates the table results:
CREATE TABLE Zone
([Zone] varchar(9), [CompanyID] int, [SubCount] int);
CREATE TABLE Company
([UniqueIdentifier]int, [Name] varchar(50));
--Adding Values into the table
INSERT INTO Company
([UniqueIdentifier], [Name])
VALUES
( 1, 'CompanyA'),
( 2, 'CompanyB'),
( 3, 'CompanyC'),
( 4, 'CompanyD'),
( 5, 'CompanyE');
--Adding Values to the table
INSERT INTO Zone
([Zone], [CompanyID], [SubCount])
VALUES
( 'Zone1', 1, 100),
( 'Zone2', 1, 200),
( 'Zone3', 2, 1250),
( 'Zone4', 3, 1440),
( 'Zone5', 4, 1445),
( 'Zone6', 4, 3250),
( 'Zone7', 5, 4440);
--Getting TOTALS
SELECT
CASE WHEN GROUPING(dbo.Company.Name)=1 THEN 'Grand Total' else dbo.Company.Name end as Company,
SUM(dbo.Zone.SubCount) as Subs
FROM dbo.Company INNER JOIN dbo.Zone ON dbo.Company.UniqueIdentifier = dbo.Zone.CompanyID
WHERE (dbo.Zone.SubCount IS NOT NULL) AND (dbo.Zone.SubCount > 0)
Group by ROLLUP(dbo.Company.Name)
ORDER BY Subs DESC;
HERE ARE THE RESULTS OF THE QUERY:
Company | Subs
------------------------
1 Grand Total | 12125
2 CompanyD | 4695
3 CompanyE | 4440
4 CompanyC | 1440
5 CompanyB | 1250
6 CompanyA | 300
DESIRED RESULT WOULD LOOK LIKE:
Company |CompanyA|CompanyB|CompanyC|CompanyE|CompanyD|Grand Total
---------------------------------------------------------------------
Subs | 300 | 1250 | 1440 | 4440 | 4695 | 12125
ANY HELP IS GREATLY APPRECIATED!
All you need to do is use pivot function for converting these rows into columns
SELECT 'Subs' AS Company
,[CompanyA]
,[CompanyB]
,[CompanyC]
,[CompanyD]
,[CompanyE]
,[Grand Total]
FROM (
SELECT CASE
WHEN GROUPING(dbo.Company.NAME) = 1
THEN 'Grand Total'
ELSE dbo.Company.NAME
END AS Company
,SUM(dbo.Zone.SubCount) AS Subs
FROM dbo.Company
INNER JOIN dbo.Zone ON dbo.Company.UNIQUEIDENTIFIER = dbo.Zone.CompanyID
WHERE (dbo.Zone.SubCount IS NOT NULL)
AND (dbo.Zone.SubCount > 0)
GROUP BY ROLLUP(dbo.Company.NAME)
) a
pivot(max(subs) FOR Company IN (
[CompanyA]
,[CompanyB]
,[CompanyC]
,[CompanyD]
,[CompanyE]
,[Grand Total]
)) piv;

Join get wrong information on query

I'm having some problems with join query. What I want is select rows of two tables that have some similar states, others no. And I have a state-equivalence view
Table Sales:
ID ..... StateID
1 1
2 1
3 6
Table Orders
ID ..... StateID
11 2
12 2
15 3
Table StatesEquivalence
ID SalesState OrdersState StateName
1 1 2 Attended
2 2 3 Declined
I've made a union of both tables(Sales and Orders) and I want a column with the EquivalentStateID and the StateName by doing something like this:
SELECT sales.ID as ID,equivalence.ID as State,equivalence.StateName
FROM Sales
INNER JOIN StatesEquivalence as equivalence
ON sales.StateID = equivalence.SalesState
WHERE sales.ID = 1
UNION
SELECT orders.ID as ID,equivalence.ID as State,equivalence.StateName
FROM Orders as orders
INNER JOIN StatesEquivalence as equivalence
ON orders.StateID = equivalence.OrdersState
Somehow I am obtaining wrong information on the equivalentID
ID State StateName
1 2 Attended
2 2 Attended
11 2 Attended
...
I don't know what happend.. because the statename is correct, but the StateID is showing wrong information
The table might show this:
ID State StateName
1 1 Attended
2 1 Attended
11 1 Attended
...
I don't think you are showing us the real problem code. I did the following:
begin transaction
create table sales (id int, stateid int)
create table orders (id int, stateid int)
create table statesequivalence (id int, salesstate int, ordersstate int, statename varchar(20))
insert into sales values (1, 1)
insert into sales values (2, 1)
insert into sales values (3, 6)
insert into orders values (11, 2)
insert into orders values (12, 2)
insert into orders values (15, 3)
insert into statesequivalence values (1, 1, 2, 'Attended')
insert into statesequivalence values (2, 2, 3, 'Declined')
SELECT sales.ID as ID,equivalence.ID as State,equivalence.statename
FROM Sales
INNER JOIN statesequivalence as equivalence
ON sales.StateID = equivalence.SalesState
WHERE sales.ID = 1
UNION
SELECT orders.ID as ID,equivalence.ID as State,equivalence.StateName
FROM Orders as orders
INNER JOIN statesequivalence as equivalence
ON orders.StateID = equivalence.OrdersState
rollback
Result:
ID State statename
----------- ----------- --------------------
1 1 Attended
11 1 Attended
12 1 Attended
15 2 Declined
So, it works. Can you try the above code?
Following is a testscript and the output from your statement.
Perhaps it's easier to work from here and you tell us what is wrong with this output.
The testscript can be adjusted and executed here
Output
ID State StateName
----------- ----------- ---------
1 1 Attended
11 1 Attended
12 1 Attended
15 2 Declined
Test Script
;WITH Sales AS (
SELECT * FROM (VALUES
(1, 1)
, (2, 1)
, (3, 6)
) AS Sales (ID, StateID)
)
, Orders AS (
SELECT * FROM (VALUES
(11, 2)
, (12, 2)
, (15, 3)
) AS Orders (ID, StateID)
)
, StatesEquivalence AS (
SELECT * FROM (VALUES
(1, 1, 2, 'Attended')
, (2, 2, 3, 'Declined')
) AS StatesEquivalence (ID, SalesState, OrdersState, StateName)
)
SELECT sales.ID as ID
, equivalence.ID as State
, equivalence.StateName
FROM Sales
INNER JOIN StatesEquivalence as equivalence ON sales.StateID = equivalence.SalesState
WHERE sales.ID = 1
UNION
SELECT orders.ID as ID
, equivalence.ID as State
, equivalence.StateName
FROM Orders as orders
INNER JOIN StatesEquivalence as equivalence ON orders.StateID = equivalence.OrdersState
I didn't understand what you need exactly, but maybe is this?
select *
from StatesEquivalence EQ
full join Sales S on EQ.SalesState = S.StateId
full join Orders O on EQ.OrdersState = O.StateId
This is a starting point maybe...

group by clause with rollup

I'm trying to use group by with rollup clause within sql server 2005 but I'm having some problem.
This is a simple dump
create table group_roll (
id int identity,
id_name int,
fname varchar(50),
surname varchar(50),
qty int
)
go
insert into group_roll (id_name,fname,surname,qty) values (1,'john','smith',10)
insert into group_roll (id_name,fname,surname,qty) values (1,'john','smith',30)
insert into group_roll (id_name,fname,surname,qty) values (2,'frank','white',5)
insert into group_roll (id_name,fname,surname,qty) values (1,'john','smith',8)
insert into group_roll (id_name,fname,surname,qty) values (2,'frank','white',10)
insert into group_roll (id_name,fname,surname,qty) values (3,'rick','black',10)
go
If I run this simple query
select id_name,fname,surname,sum(qty) as tot
from group_roll
group by id_name,fname,surname
I get
1 john smith 48
2 frank white 15
3 rick black 10
I'd like to have
1 john smith 48
2 frank white 15
3 rick black 10
Total 73
This is what I've tried to reach my goal
select
case when grouping(id_name) = 1 then 'My total' else cast(id_name as char) end as Name_id ,
fname,surname,sum(qty) as tot
from group_roll
group by id_name,fname,surname
with rollup
order by case when id_name is null then 1 else 0 end, tot desc
but my result is
1 john smith 48
1 john NULL 48
1 NULL NULL 48
2 frank white 15
2 frank NULL 15
2 NULL NULL 15
3 rick black 10
3 rick NULL 10
3 NULL NULL 10
My total NULL NULL 73
Where is my mistake?
EDIT.
I could solve my problem making
select * from (
select cast(id_name as char) as id_name,fname,surname,sum(qty) as tot
from group_roll
group by id_name,fname,surname
union
select 'Total',null,null,sum(qty) from group_roll ) as t
order by case when id_name = 'Total' then 1 else 0 end,tot desc
but I'd like to understand if rollup can solve my problem.
You cannot do it within the statement itself, however you can filter the ROLLUP set excluding the intermediate rollups, i.e. where any one but not all rows are being grouped:
select
case when grouping(id_name) = 1 then 'My total' else cast(id_name as char) end as Name_id,
fname,
surname,
sum(qty) as tot
from group_roll
group by id_name, fname, surname
with rollup
having grouping(id_name) + grouping(fname) + grouping(surname) in (0 , 3)
Or similar to your solution but referencing the original query;
;with T as (
select cast(id_name as varchar(128)) as id_name,fname,surname,sum(qty) as tot
from group_roll
group by id_name,fname,surname
) select * from T union all select 'Total:',null,null, SUM(tot) from T
FWIW SQL 2008 allows;
select
case when grouping(id_name) = 1 then 'My total' else cast(id_name as char) end as Name_id,
fname,
surname,
sum(qty) as tot
from group_roll
group by grouping sets((id_name, fname, surname), ())