Aggregate function within inner select in Pivot query using SQL Server - sql

I have the following table:
select * from product;
slno item
---------------
1 HDD
2 PenDrive
3 RAM
4 DVD
5 RAM
6 HDD
7 RAM
7 RAM
7 RAM
Now I need to do pivoting for this table for which i am using following query:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(item)
from product
group by item
order by item
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT slno,TotalProduct ,' + #cols + '
from
(
select slno,Count(*) as TotalProduct,item
from product
group by slno,item
) x
pivot
(
count(item)
for item in (' + #cols + ')
) p '
exec(#query)
Result:
slno TotalProducts DVD HDD PenDrive RAM
---------------------------------------------
1 1 0 1 0 0
2 1 0 0 1 0
3 1 0 0 0 1
4 1 1 0 0 0
5 1 0 0 0 1
6 1 0 1 0 0
7 3 0 0 0 1
Note The total of product RAM is 3 but in Column RAM showing only 1. I have used COUNT(*) aggregate function within the inner select statement in #query. How can i show actual count?

You only need to group by slno, not by the combination of slno and item. Therefore, you need to change the query which provides a source for your pivot as follows:
set #query = 'SELECT slno,totalproduct,' + #cols + '
from
(
select p.slno slno, c.count as totalproduct, p.item
from product p
inner join
(select slno, count(item) count
from product
group by slno) c on p.slno = c.slno
) x
pivot
(
count(item)
for item in (' + #cols + ')
) p '
Demo

Use following sub query instead of your sub query:
select slno,Count(*) OVER (PARTITION BY slno) as TotalProduct,item
from product
Edit: Count(*) Over(Partition by ...) supported in SQL Server 2012 and above versions.

Related

What is the SQL code for aggregating values?

I have the following table:
GR WORD NO.
1 A 4
2 B 5
3 C 6
1 G 5
2 H 5
3 I 5
I would like to get the following table:
GR 4 5 6
1 1 1 0
2 0 2 0
3 0 1 1
For each GR column value I count the NO. values.
Here's a dynamic solution:
--Sample data
--CREATE TABLE tbl (GR int, WORD char(1), [NO] int)
--INSERT INTO tbl values
--(1,'A',4),
--(2,'B',5),
--(3,'C',6),
--(1,'G',5),
--(2,'H',5),
--(3,'I',5)
DECLARE #sql NVARCHAR(MAX)
SELECT #sql = '
SELECT *
FROM tbl
PIVOT(
COUNT(WORD) FOR [NO] IN (' +
(SELECT STUFF(
(
SELECT DISTINCT ',' + QUOTENAME(CAST([NO] AS VARCHAR(10)))
FROM tbl
FOR XML PATH('')
)
, 1, 1, ''))
+ ')
) p
'
EXEC sp_executesql #sql
This is a conditional aggregation
select
GR
,[4] = count(case when NO. = 4 then WORD end)
,[5] = count(case when NO. = 5 then WORD end)
,[6] = count(case when NO. = 6 then WORD end)
from YourTable
group by GR
Or a pivot
select *
from YourTable
pivot(
count(WORD) for NO. in ([4],[5],[6])
) p

How to create columns from a list of values

I have the following data:
Table 1
Row ID Value Cost
1 1 Priority 1 10,000
2 2 Priority 2 9,000
3 3 Priority 3 8,000
4 4 Priority 4 6,000
Table 2
Row Name Priority Cost
1 Jon 1 10,000
2 Bob 3 8,000
3 Dan 4 7,000
4 Steve 2 9,000
5 Bill 3 8,000
...
I want the table to look like this:
Table 3
Row Name Priotity 1 Priority 2 Priority 3 Priority 4
1 Jon 10,000
2 Bob 8,000
3 Dan 7,000
4 Steve 9,000
5 Bill 8,000
...
How can I create rows from Table 1 as columns, and fill in the output as shown in Table 3.
I am hoping this is not as basic as it sounds, but my SQL is terrible!
you can try this for dynamic pivot table.
DECLARE #columns VARCHAR(8000)
SELECT #columns = COALESCE(#columns + ',[' + cast(Value as varchar) + ']',
'[' + cast(Value as varchar)+ ']')
FROM Table1
GROUP BY Value
DECLARE #query VARCHAR(8000)
SET #query = 'with Priorites as
(select a.Name,b.Value,b.Cost from Table2 a left join Table1 b on a.Priority =b.id)
SELECT *
FROM Priorites
PIVOT
(
MAX(Cost)
FOR [Value]
IN (' + #columns + ')
)
AS p'
EXECUTE(#query)
Here is the link for more details http://www.tsqltutorials.com/pivot.php
Pivot is always useful in this sort of scenario, but if the actual data is as simple as it's in question (like there are only 4 unique Priority and/or only 1 Priority is assigned to a particular user),then you can achieve this task with following query:
select t.row,t.name
(case when t.priority = 1 then t.cost
else ' '
end
) as Priority1,
(case when t.priority = 2 then t.cost
else ' '
end
) as Priority2,
(case when t.priority = 3 then t.cost
else ' '
end
) as Priority3,
(case when t.priority = 4 then t.cost
else ' '
end
) as Priority4
From
(select Row,name,priority,cost
from Table2
group by name) t
group by t.name;

SQL Multiple dynamic queries to one query

I am working in a SQL Server 2008 environment with SQL Server Management Studio 2012.
I have written 3 separate queries
QUERY 1
SQL query SUMS all the STOCK ON HAND from an Inventories table
SELECT StockCode,
Sum(QtyOnHand) AS 'SOH'
FROM InvWarehouse
WHERE StockCode NOT LIKE '%DEM%' AND StockCode NOT LIKE '%REF%' AND StockCode NOT LIKE 'Z%'
GROUP BY InvWarehouse.StockCode
QUERY 2
This query looks at future orders from a Purchase Orders Table and dynamically returns the next/following 12 months
DECLARE
#cols AS NVARCHAR(MAX),
#cols1 AS NVARCHAR(MAX),
#cols2 AS NVARCHAR(MAX),
#cols3 AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
SELECT #cols = STUFF((SELECT ',' + QUOTENAME(YearMonth)
FROM
-- Selecting Using the Destinct --
(SELECT DISTINCT CAST(YEAR([OrderDueDate]) AS NVARCHAR(4)) + RIGHT('00' + CAST(MONTH([OrderDueDate]) AS NVARCHAR(2)),2) AS YearMonth
FROM PorMasterHdr
JOIN PorMasterDetail
ON PorMasterDetail.PurchaseOrder = PorMasterHdr.PurchaseOrder
WHERE DATEDIFF(MONTH, OrderDueDate, DATEADD(m,12,GETDATE())) <= 12 ) sub
ORDER BY YearMonth
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),1,1,''),
#cols2 = STUFF((SELECT ',ISNULL(' + QUOTENAME(YearMonth) + ',0) AS ' + QUOTENAME(YearMonth)
FROM
-- Selecting Using the Destinct --
(SELECT DISTINCT CAST(YEAR([OrderDueDate]) AS NVARCHAR(4)) + RIGHT('00' + CAST(MONTH([OrderDueDate]) AS NVARCHAR(2)),2) AS YearMonth
FROM PorMasterHdr
JOIN PorMasterDetail
ON PorMasterDetail.PurchaseOrder = PorMasterHdr.PurchaseOrder
WHERE DATEDIFF(MONTH, OrderDueDate, DATEADD(m,12,GETDATE())) <= 12) sub
ORDER BY YearMonth
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),1,1,'')
SET #query = '
SELECT MStockCode, ' + #cols2 + '
FROM (
SELECT MStockCode,
MOrderQty,
CAST(YEAR([OrderDueDate]) AS NVARCHAR(4))+RIGHT(''00''+CAST(MONTH([OrderDueDate]) AS NVARCHAR(2)),2) AS YearMonth
FROM PorMasterHdr
JOIN PorMasterDetail
ON PorMasterDetail.PurchaseOrder = PorMasterHdr.PurchaseOrder
WHERE MStockCode NOT LIKE ''%DEM%'' AND MStockCode NOT LIKE ''%REF%'' AND MStockCode NOT LIKE ''Z%''
) AS X
PIVOT (
SUM(MOrderQty)
FOR YearMonth in (' + #cols + ')
) AS PT'
EXECUTE (#query)
QUERY 3
This query looks at the past 12 month of sales data from a Sales table and dynamically returns the last/previous 12 months
DECLARE
#cols AS NVARCHAR(MAX),
#cols1 AS NVARCHAR(MAX),
#cols2 AS NVARCHAR(MAX),
#cols3 AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
SELECT #cols = STUFF((SELECT ',' + QUOTENAME(YearMonth)
FROM
-- Selecting Using the Destinct --
(SELECT DISTINCT CAST([TrnYear] AS NVARCHAR(4)) + RIGHT('00' + CAST([TrnMonth] AS NVARCHAR(2)),2) AS YearMonth
FROM ArTrnDetail
WHERE DATEDIFF(MONTH, InvoiceDate, GETDATE()) <= 12 ) sub
ORDER BY YearMonth
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),1,1,''),
#cols2 = STUFF((SELECT ',ISNULL(' + QUOTENAME(YearMonth) + ',0) AS ' + QUOTENAME(YearMonth)
FROM
-- Selecting Using the Destinct --
(SELECT DISTINCT CAST([TrnYear] AS NVARCHAR(4)) + RIGHT('00' + CAST([TrnMonth] AS NVARCHAR(2)),2) AS YearMonth
FROM ArTrnDetail
WHERE DATEDIFF(MONTH, InvoiceDate, GETDATE()) <= 12) sub
ORDER BY YearMonth
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),1,1,'')
SET #query = '
SELECT StockCode, ' + #cols2 + '
FROM (
SELECT StockCode,
QtyInvoiced,
CAST([TrnYear] AS NVARCHAR(4))+RIGHT(''00''+CAST([TrnMonth] AS NVARCHAR(2)),2) AS YearMonth
FROM ArTrnDetail
WHERE StockCode NOT LIKE ''%DEM%'' AND StockCode NOT LIKE ''%REF%'' AND StockCode NOT LIKE ''Z%''
) AS X
PIVOT (
SUM(QtyInvoiced)
FOR YearMonth in (' + #cols + ')
) AS PT'
EXECUTE (#query)
The results for each query are correct. Now how do I combine them into one query. So that they return
STOCKCODE | Past 12 Month Sales Per Month | Stock On Hand | Future Purchases
Helicopters | 1 4 5 2 3 4 6 1 3 2 3 2| 15 | 2 3 5 4 6 7 8 4 3 2 8 5
Jam | 2 5 6 4 8 5 8 5 7 2 1 2| 30 | 4 5 6 5 8 7 0 1 2 1 1 4
Frogs | 2 3 2 4 8 5 4 6 8 2 1 3| 7 | 5 7 8 8 6 7 4 0 1 2 1 2
STOCK CODE for the above is the same information from the different tables eg. Helicopters in Inventory is the same as Helicopters in Purchase Orders.
I would suggest the following:
Rewrite #query2 to result in two columns: StockCode and Sales. Instead of selecting each month as a seperate column, concatenate each month in a VARCHAR. You already wrote a variable for #cols for selecting the columns seperately. Keep that for pivoting. Write a variable (#SelSales) to concatenate the results for each month in a VARCHAR and use that in your selection for the Sales column.
Rewrite #query3 to result in two columns: StockCode and Purchases (similar to 1.)
Put your #query1 in a NVARCHAR(MAX) variable (the one selecting the stock).
Write a #query to combine them all.
TSQL outline for #query:
DECLARE #query NVARCHAR(MAX);
SET #query=N'
SELECT
COALESCE(stock.StockCode,sales.StockCode,purchases.StockCode) AS StockCode,
COALESCE(sales.Sales,''0 0 0 0 0 0 0 0 0 0 0 0'') AS Sales,
COALESCE(stock.SOH,0) AS Stock,
COALESCE(purchases.Purchases,''0 0 0 0 0 0 0 0 0 0 0 0'') AS Purchases
FROM
('+#query1+') AS stock
FULL JOIN ('+#query2+') AS sales ON sales.StockCode=stock.StockCode
FULL JOIN ('+#query3+') AS purchases ON purchases.StockCode=stock.StockCode';
EXEC(#query);

How to use SQL Server 2005 Pivot based on lookup table

table [Status] has the following data:
ID Status
1 PaymentPending
2 Pending
3 Paid
4 Cancelled
5 Error
====================================
Data Table has the following structure:
ID WeekNumber StatusID
1 1 1
2 1 2
3 1 3
4 2 1
5 2 2
6 2 2
7 2 3
Looking for a Pivot
Week # PaymentPending Pending Paid Cancelled
Week 1 1 1 1 0
Week 2 1 2 1 0
SELECT 'Week '+CAST(coun.WeekNumber AS VARCHAR(10)) [Week #],[PaymentPending],[Pending],[Paid],[Cancelled],[Error] FROM
(SELECT [WeekNumber],[Status] FROM dbo.WeekDetails
INNER JOIN [dbo].[Status] AS s
ON [dbo].[WeekDetails].[StatusID] = [s].[ID]) AS wee
PIVOT (COUNT(wee.[Status]) FOR wee.[Status]
IN ([PaymentPending],[Pending],[Paid],[Cancelled],[Error])) AS Coun
A pivot might look like this:
SELECT * FROM
(SELECT
'Week ' + CAST(D.WeekNumber AS varchar(2)) [Week #],
S.Status
FROM DataTbl D
INNER JOIN Status S ON D.StatusID = S.ID
) Derived
PIVOT
(
COUNT(Status) FOR Status IN
([PaymentPending], [Pending], [Paid], [Cancelled]) -- add [Error] if needed
) Pvt
If you expect the number of items in theStatustable to change you might want to consider using a dynamic pivot to generate the column headings. Something like this:
DECLARE #sql AS NVARCHAR(MAX)
DECLARE #cols AS NVARCHAR(MAX)
SELECT #cols = ISNULL(#cols + ',','') + QUOTENAME(Status)
FROM (SELECT ID, Status FROM Status) AS Statuses ORDER BY ID
SET #sql =
N'SELECT * FROM
(SELECT ''Week '' + CAST(D.WeekNumber AS varchar(2)) [Week #], S.Status
FROM Datatbl D
INNER JOIN Status S ON D.StatusID = S.ID) Q
PIVOT (
COUNT(Status)
FOR Status IN (' + #cols + ')
) AS Pvt'
EXEC sp_executesql #sql;
Sample SQL Fiddle
You can use CASE based aggregation with GROUP BY
SELECT 'Week ' + cast(WeekNumber as varchar(10)) as 'Week#',
SUM ( CASE WHEN StatusId =1 THEN 1 else 0 end) as 'PaymentPending',
SUM ( CASE WHEN StatusId =2 THEN 1 else 0 end) as 'Pending',
SUM ( CASE WHEN StatusId =3 THEN 1 else 0 end) as 'Paid',
SUM ( CASE WHEN StatusId =4 THEN 1 else 0 end) as 'Cancelled'
FROM DataTbl D
GROUP BY 'Week ' + cast(WeekNumber as varchar(10))

How to write a query table value as column name?

My Sql table is similar like below
Code Value ID
A 100 1
A 200 2
A 300 3
B 200 1
B 500 2
B 600 3
C 800 1
C 700 2
C 200 3
How I can write query in sql server 2008 to get values in below format.
ID A B C
1 100 200 800
2 200 500 700
3 300 600 200
You can use SUM function for that:
SELECT ID,
SUM(CASE Code when 'A' then Value else 0 end)as A,
SUM(CASE Code when 'B' then Value else 0 end)as B,
SUM(CASE Code when 'C' then Value else 0 end)as C
FROM myTable
GROUP BY ID;
See this SQLFiddle
Use PIVOT
select ID,[A],[B],[C]
from your_table T
PIVOT (MAX(Value) FOR Code in ([A],[B],[C]) )P
IF the number if Codes are not fixed you could use dynamic pivot
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(Code)
from your_table
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT ID, ' + #cols + '
from your_table
pivot
(
MAX([Value])
for Code in (' + #cols + ')
) p '
print(#query)
execute(#query)
The answer is PIVOT
DECLARE #t TABLE (Code varchar(10), Value int, Id int)
INSERT INTO #t VALUES
('A',100,1),
('A',200,2),
('A',300,3),
('B',200,1),
('B',500,2),
('B',600,3),
('C',800,1),
('C',700,2),
('C',200,3);
SELECT ID,[A],[B],[C]
FROM #t
PIVOT (SUM(Value) FOR Code IN ([A],[B],[C]))P
Result
ID A B C
1 100 200 800
2 200 500 700
3 300 600 200