How to use SQL Server 2005 Pivot based on lookup table - sql

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))

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

SQL Server (2012): Pivot (or Group by?) on Multiple Columns

I have the following table:
month seating_id dept_id
Jan 1 5
Jan 8 9
Jan 5 3
Jan 7 2
Jan 1 5
Feb 1 9
Feb 8 9
Feb 5 3
Feb 7 2
Feb 7 1
I want to count each type of seating_id and dept_id for each month, so the result would look something like this:
month seating_id_1 seating_id_5 seating_id_7 seating_id_8 dept_id_1 dept_id_2 dept_id_3 dept_id_5 dept_id_9
Jan 2 1 1 1 0 1 1 2 1
Feb 0 1 2 1 1 1 1 0 2
I've experimented with unpivot/pivot and GROUP BY but haven't been able to achieve the desired results. Note, I would like to perform this as a SELECT statement, and not PROCEDURE call, if possible.
Let me know if you need any other info.
In case you need to go Dynamic
Example
Declare #SQL varchar(max) = '
Select *
From (
Select month,B.*
From YourTable A
Cross Apply (values (concat(''seating_id_'',seating_id),seating_id)
,(concat(''dept_id_'',dept_id),dept_id)
) b(item,value)
) A
Pivot (count([Value]) For [Item] in (' + Stuff((Select Distinct concat(',[seating_id_',seating_id,']') from YourTable For XML Path('')),1,1,'')
+','+
Stuff((Select Distinct concat(',[dept_id_',dept_id,']') from YourTable For XML Path('')),1,1,'')
+ ') ) p'
Exec(#SQL);
Returns
The Generated SQL Looks like this
Select *
From (
Select month,B.*
From YourTable A
Cross Apply (values (concat('seating_id_',seating_id),seating_id)
,(concat('dept_id_',dept_id),dept_id)
) b(item,value)
) A
Pivot (count([Value]) For [Item] in ([seating_id_1],[seating_id_5],[seating_id_7],[seating_id_8],[dept_id_1],[dept_id_2],[dept_id_3],[dept_id_5],[dept_id_9]) ) p
i have an answer you might not like but it does it:
select month
, sum(case seating_id when 1 then 1 else 0 end) as seating_id_1
, sum(case seating_id when 2 then 1 else 0 end) as seating_id_2
...
, sum(case dept_id when 1 then 1 else 0 end) as dept_id_1
, sum(case dept_id when 2 then 1 else 0 end) as dept_id_2
...
from YourTable
group by month

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;

Getting PIVOT using GroupBy

I have this table:
Person Job Day EndDay
1 101 1 12
1 102 2 11
1 103 3 11
3 101 1 11
3 102 2 11
3 JobOff 3 11
2 101 1 11
2 102 2 11
2 103 3 11
2 JobOff 4 11
...
PS: My days are upto 'N', which I don't know. So, static SQL is not feasible here.
And I need to change it to this form:
Day (Columns)
Person (Rows)
Person 1 2 3 4
1 101 102 103 N/A
2 101 102 103 JobOff
3 101 102 JobOff N/A
Any help would be appreciated. I am very new in SQL.
Thanks to Roman, I have this S.Procedure:
CREATE PROCEDURE [dbo].[MyStoredProcedure] AS
BEGIN
declare #stmt nvarchar(max)
select #stmt =
isnull(#stmt + ', ', '') +
'isnull(max(case when [Day] = ' + cast([Day_I] as nvarchar(max)) +
' then Job end), ''N/A'') as [' + cast([Day_I] as nvarchar(max)) + ']'
from (select distinct [Day_I] from dbo.Solution) as t
order by [Day_I]
select #stmt = '
select
Person_I, ' + #stmt +'
from dbo.Solution_Format
group by Person_I'
END
But when I am executing this stored procedure like this:
exec MyStoredProcedure
Its showing me NO results. No errors!
select
Person,
isnull(max(case when [Day] = 1 then Job end), 'N/A') as [1],
isnull(max(case when [Day] = 2 then Job end), 'N/A') as [2],
isnull(max(case when [Day] = 3 then Job end), 'N/A') as [3],
isnull(max(case when [Day] = 4 then Job end), 'N/A') as [4]
from Table1
group by Person
sql fiddle demo
it's also possible to do this with dynamic SQL if you need this to arbitrary number of columns:
create procedure [dbo].[MyStoredProcedure]
as
begin
declare #stmt nvarchar(max)
select #stmt =
isnull(#stmt + ', ', '') +
'isnull(max(case when [Day] = ' + cast([Day] as nvarchar(max)) +
' then Job end), ''N/A'') as [' + cast([Day] as nvarchar(max)) + ']'
from (select distinct [Day] from Table1) as t
order by [Day]
select #stmt = '
select
Person, ' + #stmt +'
from Table1
group by Person'
exec sp_executesql
#stmt = #stmt
end
sql fiddle demo
One way to do this is to use multiple self joins
Select
t1.Person,
COALESCE(t1.Job, 'N/A') [1],
COALESCE(t2.Job, 'N/A' )[2],
COALESCE(t3.Job, 'N/A') [3],
COALESCE(t4.Job, 'N/A') [4]
FROM
table1 t1
LEFT JOIN table1 t2
ON t1.Person = t2.Person
and t2.Day = 2
LEFT JOIN table1 t3
ON t1.Person = t3.Person
and t3.Day = 3
LEFT JOIN table1 t4
ON t1.Person = t4.Person
and t4.Day = 4
WHERE
t1.Day = 1
ORDER BY
t1.Person
DEMO
Another ways is to use MAX(CASE however I'm not a fan of using aggregates like this.
Select
t1.Person,
COALESCE(MAX(CASE WHEN t1.Day = 1 then t1.Job END), 'N/A') [1],
COALESCE(MAX(CASE WHEN t1.Day = 2 then t1.Job END), 'N/A') [2],
COALESCE(MAX(CASE WHEN t1.Day = 3 then t1.Job END), 'N/A') [3],
COALESCE(MAX(CASE WHEN t1.Day = 4 then t1.Job END), 'N/A') [4]
FROM
table1 t1
GROUP BY
t1.Person
DEMO
And lastly there's the pivot clause which I don't really like because of the MAX here
SELECT person,
COALESCE([1], 'N/A') [1],
COALESCE([2], 'N/A') [2],
COALESCE([3], 'N/A') [3],
COALESCE([4], 'N/A') [4]
FROM (SELECT t1.person,
t1.day,
t1.job
FROM table1 t1) p
PIVOT (Max (job)
FOR day IN ( [1],
[2],
[3],
[4]) ) AS pvt
DEMO

SQL Multiple count on same row with dynamic column

I need to alter view that show user count(ScheduleID) by period on same row. Now the Period table content can grow and contain more than 3 periods.
The actual SQL is:
SELECT r.Code,
SUM(CASE WHEN s.PeriodID=1 THEN 1 ELSE 0 END) AS PeriodID1,
SUM(CASE WHEN s.PeriodID=2 THEN 1 ELSE 0 END) AS PeriodID2,
SUM(CASE WHEN s.PeriodID=3 THEN 1 ELSE 0 END) AS PeriodID3,
SUM(CASE WHEN s.PeriodID IN (1,2,3) THEN 1 ELSE 0 END) AS Total
FROM Schedules s
JOIN Periods p ON p.PeriodID = s.PeriodID
JOIN Resources r ON r.ResourceID = s.ResourceID
GROUP BY r.Code;
Example data:
Table Schedules
ScheduleID(int) ResourceID(int) ResourceCode(varchar 4) PeriodID(int)
1 1 AA 1
2 1 AA 3
3 1 AA 3
4 2 BB 1
5 3 CC 1
6 1 AA 1
7 3 CC 2
8 3 CC 3
9 2 BB 1
10 2 BB 2
11 2 BB 3
12 1 AA 3
Table Periods
PeriodID(int) Code (varchar 4)
1 P1
2 P2
3 P3
4 P4
5 P5
6 P6
7 P7
8 P8
The result I need is:
ResourceCode PeriodID1 PeriodID2 PeriodID3 ... PeriodID8 TOTAL
AA 2 0 3 0 5
BB 2 1 1 0 4
CC 1 1 1 0 3
The Periods table content is now dynamic.
The database version is an Microsoft SQL 2008
I like to know if is possible to do that without create stored procedure...and doing this in one query like this:
SELECT *
FROM (
SELECT R.Code, P.PeriodID, COUNT(S.ScheduleID) AS RPCount
FROM Schedules S INNER JOIN Periods P ON S.PeriodID = P.PeriodID
JOIN Resources R ON S.ResourceID = R.ResourceID
WHERE S.ResourceID is not null
GROUP BY R.Code, P.PeriodID
) as data
PIVOT
(
SUM(RPCount)
--FOR PeriodID IN ([1],[2],[3])
FOR PeriodID IN (SELECT PeriodID From Periods)
)AS pvt
ORDER BY Code
Since you are using SQL Server then you can implement the PIVOT function and if you have an unknown number of period values, then you will need to use dynamic SQL:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME('PeriodId'+cast(periodid as varchar(10)))
from Periods
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT resourcecode, ' + #cols + ' , Total
from
(
select s.resourcecode,
''PeriodId''+cast(p.periodid as varchar(10)) period,
count(*) over(partition by s.resourcecode) Total
from periods p
left join schedules s
on p.periodid = s.periodid
) x
pivot
(
count(period)
for period in (' + #cols + ')
) p
where resourcecode is not null
order by resourcecode'
execute(#query)
See SQL Fiddle with Demo. This gives a result:
| RESOURCECODE | PERIODID1 | PERIODID2 | PERIODID3 | PERIODID4 | PERIODID5 | PERIODID6 | PERIODID7 | PERIODID8 | TOTAL |
------------------------------------------------------------------------------------------------------------------------
| AA | 2 | 0 | 3 | 0 | 0 | 0 | 0 | 0 | 5 |
| BB | 2 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 4 |
| CC | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 3 |
Based on your previous question that was tagged with MySQL, I am assuming you are using MySQL as the database. If so, then you do not have a PIVOT function so you will have to use an aggregate function with a CASE expression to transform the rows of data into columns.
If your column values are known, then you can hard-code the query:
select resourcecode,
sum(case when period = 'PeriodId1' then 1 else 0 end) PeriodId1,
sum(case when period = 'PeriodId2' then 1 else 0 end) PeriodId2,
sum(case when period = 'PeriodId3' then 1 else 0 end) PeriodId3,
sum(case when period = 'PeriodId4' then 1 else 0 end) PeriodId4,
sum(case when period = 'PeriodId5' then 1 else 0 end) PeriodId5,
sum(case when period = 'PeriodId6' then 1 else 0 end) PeriodId6,
sum(case when period = 'PeriodId7' then 1 else 0 end) PeriodId7,
sum(case when period = 'PeriodId8' then 1 else 0 end) PeriodId8,
count(*) Total
from
(
select concat('PeriodId', p.periodid) Period,
s.resourcecode
from periods p
left join schedules s
on p.periodid = s.periodid
) d
where resourcecode is not null
group by resourcecode;
See SQL Fiddle with Demo. But if the values will be unknown or dynamic then you will need to use a prepared statement to generate a sql string to execute:
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'sum(CASE WHEN period = ''',
concat('PeriodId', periodid),
''' THEN 1 else 0 END) AS `',
concat('PeriodId', periodid), '`'
)
) INTO #sql
FROM periods;
SET #sql
= CONCAT('SELECT resourcecode, ', #sql, ' , count(*) Total
from
(
select concat(''PeriodId'', p.periodid) Period,
s.resourcecode
from periods p
left join schedules s
on p.periodid = s.periodid
) d
where resourcecode is not null
group by resourcecode');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
See SQL Fiddle with Demo.
Use PIVOT
try this
SELECT *
FROM (
SELECT
S.ResourceCode ,
P.PeriodID AS period,
COUNT(*) AS PCount
FROM Schedules S INNER JOIN Periods P ON S.PeriodID =P.PeriodID
GROUP BY S.ResourceCode ,P.PeriodID
) as s
PIVOT
(
PCount,
FOR [period] IN (SELECT DISTINCT PeriodID From Periods)
)AS pivot
Please try below code for MS Sql server:
DECLARE #column VARCHAR(MAX), #SumQuery VARCHAR(MAX)
SELECT
#column = COALESCE(#column + '], [', '')+ CAST(PeriodID as nvarchar(10)),
#SumQuery = COALESCE(#SumQuery + ']+[', '')+ CAST(PeriodID as nvarchar(10))
FROM
Periods
GROUP BY PeriodID
EXEC ('select *, ['+#SumQuery+'] as [Total] From
(
select * From Schedules
)up
pivot (count(ScheduleID) for PeriodID in (['+#column+'])) as pvt')