Pivot table in SQL (column results to rows) - sql

how do I pivot a results query?
currently it looks like this
Hours | Volume
8 | 1000
9 | 1012
10 | 1045
11 | 1598
12 | 1145
13 | 1147
into this
8 |9 |10 |11 |12 |13
1000|1012|1045|1598|1145|1147
I tried the following but its not working
Select #TEMP.volume,
pvt.volume,
#TEMP.volume
pvt.volume
FROM #TEMP
PIVOT (volume FOR [DialHour] IN ( "8", "9", "10", "11","12","13","14","15","16","17","18","19")) AS pvt

You can find a clear example on technet;
https://technet.microsoft.com/en-us/library/ms177410(v=sql.105).aspx
Your code should look more like the below;
Select volume, [8], [9], [10], [11],[12],[13],[14],[15],[16],[17],[18],[19]
FROM
(SELECT volume FROM #TEMP) AS SourceTable
PIVOT
(
hours
FOR volume IN ("8", "9", "10", "11","12","13","14","15","16","17","18","19")
) AS PivotTable;

I am creating this table in T-SQL for demonstration purposes.
CREATE TABLE #TEMP (HOURS INT, VOLUME INT)
INSERT INTO #TEMP VALUES
(8 ,1000),
(9 ,1012),
(10,1045),
(11,1598),
(12,1145),
(13,1147)
;
If you are not using T-SQL (SQL Server), you could run into issues with a Pivot example. The syntax is different in different platforms. Here is a more standard approach.
SELECT
MAX(CASE WHEN HOURS = 8 THEN VOLUME END) AS [8]
,MAX(CASE WHEN HOURS = 9 THEN VOLUME END) AS [9]
,MAX(CASE WHEN HOURS = 10 THEN VOLUME END) AS [10]
,MAX(CASE WHEN HOURS = 11 THEN VOLUME END) AS [11]
,MAX(CASE WHEN HOURS = 12 THEN VOLUME END) AS [12]
,MAX(CASE WHEN HOURS = 13 THEN VOLUME END) AS [13]
FROM #TEMP
;
It is better to label integers/numbers with an alphabet character instead of relying on brackets. This approach should work on any platform.
SELECT
MAX(CASE WHEN HOURS = 8 THEN VOLUME END) AS COL_8
,MAX(CASE WHEN HOURS = 9 THEN VOLUME END) AS COL_9
,MAX(CASE WHEN HOURS = 10 THEN VOLUME END) AS COL_10
,MAX(CASE WHEN HOURS = 11 THEN VOLUME END) AS COL_11
,MAX(CASE WHEN HOURS = 12 THEN VOLUME END) AS COL_12
,MAX(CASE WHEN HOURS = 13 THEN VOLUME END) AS COL_13
FROM #TEMP
;

This assumes you are working with SQL Server , then it is always better to use dynamic PIVOT approach
declare #col nvarchar(max), #query nvarchar(max)
select #col = stuff(
(select ','+quotename(Hours) from #tm for xml path('')),
1,1, '')
set #query = N'select * from
(
select * from #tm
)a
PIVOT
(
MAX(Volume) for Hours in ('+#col+')
)pvt'
EXECUTE sp_executesql #query
Result :
8 9 10 11 12 13
1000 1012 1045 1598 1145 1147

It is always good to use pivot query in addition to an ID column , to simplify u can get result set this way
WITH cte
AS (
SELECT *
FROM #TEMP
PIVOT(max(volume) FOR [DialHour] IN (
"8"
,"9"
,"10"
,"11"
,"12"
,"13"
,"14"
,"15"
,"16"
,"17"
,"18"
,"19"
)) AS pvt
)
SELECT 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19...
FROM cte

Related

Pivoting unique users for each month, each year

I'm learning about PIVOT function and I want to try it in my DB, in the table DDOT I have events (rows) made by users during X month Y year in the YYYYMM format.
id_ev iddate id_user ...
------------------------
1 201901 321
2 201902 654
3 201903 987
4 201901 321
5 201903 987
I'm basing my query on the MS Documentation and I'm not getting errors but I'm not able to fill it with the SUM of those unique events (users). In simple words I want to know how many users (unique) checked up each month (x axis) in the year (y axis). However, I'm getting NULL as result
YYYY jan feb mar
----------------------------
2019 NULL NULL NULL
I'm expecting a full table with what I mentionted before.
YYYY jan feb mar
----------------------------
2019 2 1 1
In the code I've tried with different aggregate functions but this block is the closest to a result from SQL.
CREATE TABLE ddot
(
id_ev int NOT NULL ,
iddate int NOT NULL ,
id_user int NOT NULL
);
INSERT INTO DDOT
(
[id_ev], [iddate], [id_user]
)
VALUES
(
1, 201901, 321
),
(
2, 201902, 654
),
(
3, 201903, 987
),
(
4, 201901, 321
),
(
5, 201903, 987
)
GO
SELECT *
FROM (
SELECT COUNT(DISTINCT id_user) [TOT],
DATENAME(YEAR, CAST(iddate+'01' AS DATETIME)) [YYYY], --concat iddate 01 to get full date
DATENAME(MONTH, CAST(iddate+'01' AS DATETIME)) [MMM]
FROM DDOT
GROUP BY DATENAME(YEAR, CAST(iddate+'01' AS DATETIME)),
DATENAME(MONTH, CAST(iddate+'01' AS DATETIME))
) AS DOT_COUNT
PIVOT(
SUM([TOT])
FOR MMM IN (jan, feb, mar)
) AS PVT
Ideally you should be using an actual date in the iddate column, and not a string (number?). We can workaround this using the string functions:
SELECT
CONVERT(varchar(4), LEFT(iddate, 4)) AS YYYY,
COUNT(CASE WHEN CONVERT(varchar(2), RIGHT(iddate, 2)) = '01' THEN 1 END) AS jan,
COUNT(CASE WHEN CONVERT(varchar(2), RIGHT(iddate, 2)) = '02' THEN 1 END) AS feb,
COUNT(CASE WHEN CONVERT(varchar(2), RIGHT(iddate, 2)) = '03' THEN 1 END) AS mar,
...
FROM DDOT
GROUP BY
CONVERT(varchar(4), LEFT(iddate, 4));
Note that if the iddate column already be text, then we can remove all the ugly calls to CONVERT above:
SELECT
LEFT(iddate, 4) AS YYYY,
COUNT(CASE WHEN RIGHT(iddate, 2) = '01' THEN 1 END) AS jan,
COUNT(CASE WHEN RIGHT(iddate, 2) = '02' THEN 1 END) AS feb,
COUNT(CASE WHEN RIGHT(iddate, 2) = '03' THEN 1 END) AS mar,
...
FROM DDOT
GROUP BY
LEFT(iddate, 4);

Date in SQL Pivot to be transformed from yyyy-mm-dd to mm-yy

I have a pivot query IN SQL Server 2016 that works somewhat good:
select *
FROM (select Analyst,Task_Type,Load,Completed,Date_Of_Assignment
from mytable where Task_Type IN ('Loans') ) as TEST PIVOT
(
SUM(Completed)
FOR
[Date_Of_Assignment] IN ([2018-05-22],[2018-06-22],[2018-07-22])
) as P
it returns as:
Agent_Name Task_Type Total_Received 2018-05-22 2018-06-22 2018-07-22
Steve Loans 20 15 15 15
Cathe Loans 20 15 15 15
Adam Loans 20 15 15 15
Klaus Loans 20 15 15 15
which is cool, but I need it more like this
Agent_Name Task_Type Total_Received May 18 June 18 July 18
Steve Loans 20 15 15 15
Cathe Loans 20 15 15 15
Adam Loans 20 15 15 15
Klaus Loans 20 15 15 15
Considering that it might be more than one day of data per Load, actually it should be every workday.
I tried by grouping by Date_ but it returns errors.
If I do like:
select *
FROM (select Analyst,Task_Type,Load,Completed,Date_Of_Assigment
CASE WHEN FORMAT(Date_Of_Assigment,'MM-yy') = '05-18' THEN 'May 18'
WHEN FORMAT(Date_Of_Assigment,'MM-yy') = '06-18' THEN 'Jun 18'
WHEN FORMAT(Date_Of_Assigment,'MM-yy') = '07-18' THEN 'Jul 18'
END AS MonthT
from mytable where Task_Type IN ('Loans') as TEST PIVOT
(
SUM(Completed)
FOR
[MonthT] IN (['May 18'],['Jun 18'],['Jul 18'])
) as P
I receive:
Analyst Task Total_Received 'May 18' 'Jun 18' 'Jul 18'
Steve Billing 20 NULL NULL NULL
Cathe Billing 20 NULL NULL NULL
Adam Billing 20 NULL NULL NULL
Klaus Billing 20 NULL NULL NULL
It does turn Dates into columns but with NULLS only.
Data types are: Date for Date_Of_Assigment, nvarchar for Task_Type and Analyst and integer for the rest of columns.
What am I doing wrong?
Thank you very much
I would do this as :
select Analyst, Task,
sum(case when MonthT = '05-18' then Completed else 0 end) [May 18],
sum(case when MonthT = '06 18' then Completed else 0 end) [Jun 18],
sum(case when MonthT = '05-18' then Completed else 0 end) [Jul 18]
from table t cross apply
( values format (Date_Of_Assigment, 'MM-yy')
) tt (MonthT)
where Task_Type = 'Loans'
group by Analyst, Task;
Try this:
SELECT
*
FROM (
SELECT
Analyst
, Task_Type
, [Load]
, Completed
, DATENAME( MM, Date_Of_Assignment ) + ' ' + CAST( ( YEAR( Date_Of_Assignment ) % 100 ) AS VARCHAR(2) ) AS Date_Of_Assignment
FROM mytable WHERE Task_Type IN ( 'Loans' )
) AS TEST
PIVOT (
SUM(Completed)
FOR
[Date_Of_Assignment] IN ( [May 18], [June 18], [July 18] )
) AS P
I don't have access to your data to test this, but in theory, it should do what you're looking for.
Here's a simple proof of concept you can run in SSMS:
DECLARE #mytable TABLE (
Analyst VARCHAR(10), Task_Type VARCHAR(10) DEFAULT 'Loans', [Load] INT, Completed INT, Date_Of_Assignment DATETIME
)
INSERT INTO #mytable ( Analyst, [Load], Completed, Date_Of_Assignment )
VALUES
( 'Steve', 3, 2, '05/22/2018' )
, ( 'Steve', 1, 1, '06/22/2018' )
, ( 'Steve', 4, 3, '07/22/2018' );
SELECT
Analyst, Task_Type, SUM( [Load] ) AS Total_Recieved, SUM( [May 18] ) AS [May 18], SUM( [June 18] ) AS [June 18], SUM( [July 18] ) AS [July 18]
FROM (
SELECT
Analyst
, Task_Type
, [Load]
, Completed
, DATENAME( MM, Date_Of_Assignment ) + ' ' + CAST( ( YEAR( Date_Of_Assignment ) % 100 ) AS VARCHAR(2) ) AS Date_Of_Assignment
FROM #mytable
WHERE Task_Type IN ( 'Loans' )
) AS TEST
PIVOT (
SUM( Completed ) FOR [Date_Of_Assignment] IN ( [May 18], [June 18], [July 18] )
) AS P
GROUP BY Analyst, Task_Type;
Returns
+---------+-----------+----------------+--------+---------+---------+
| Analyst | Task_Type | Total_Recieved | May 18 | June 18 | July 18 |
+---------+-----------+----------------+--------+---------+---------+
| Steve | Loans | 8 | 2 | 1 | 3 |
+---------+-----------+----------------+--------+---------+---------+

How to convert columns to rows in SQL Server without pivot and value is dynamic

I have some values in rows like :
Month | Product | SalesQty
-------+---------+---------
Jan-17 | ABC | 3
Feb-17 | ABC | 6
Apr-17 | ABC | 19
But i want to show the some values in columns like:
Model| Apr-17 | May-17 | Jun-17 | Jul-17
ABC 1 2 12 0
BCS 212 12 12 112
Months must be generated dynamically. Static month will not help me in this situation.
Why not Use pivot? it is simpler than other solutions like case expression:
SELECT *
FROM table
PIVOT
(
SUM(SalesQty)
FOR Month IN([Apr-17] ,[May-17], [Jun-17], [Jul-17])
) AS p;
To do it dynamically you can use the same query with dynamic sql like this:
DECLARE #cols AS NVARCHAR(MAX);
DECLARE #query AS NVARCHAR(MAX);
select #cols = STUFF((SELECT distinct ',' +QUOTENAME(CONCAT(LEFT(datename(month, Month), 3),
CAST(DATEPART(day, month) AS NVARCHAR(2))))
FROM table1
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '');
SELECT #query = ' SELECT *
FROM
(
SELECT product, SalesQty,
CONCAT(LEFT(datename(month, Month), 3),
CAST(DATEPART(day, month) AS NVARCHAR(2))) AS Month
FROM table1
) AS t
PIVOT
(
SUM(SalesQty)
FOR Month IN( ' + #cols + ' )
) AS p';
execute(#query);
dynamic demo
If you don't want to use PIVOT then you can use CASE expression like this:
SELECT product,
SUM(CASE WHEN month = 'Jan17' THEN SalesQty ELSE 0 END) AS Jan17,
SUM(CASE WHEN month = 'Jan17' THEN SalesQty ELSE 0 END) AS Jun17,
SUM(CASE WHEN month = 'Jan17' THEN SalesQty ELSE 0 END) AS Jul17
FROM
(
SELECT product, SalesQty,
CONCAT(LEFT(datename(month, Month), 3),
CAST(DATEPART(day, month) AS NVARCHAR(2))) AS Month
FROM table1
) AS t
GROUP BY Product;
Then to do this dynamically, you just need to replace the case expression part to by dynamic in the cols names variable.

SQL query get count item for report per day of the month?

i need sql query for report from table item per day(fix day1-day31 as column) of the month when i input month and year.
This is my table (item)
ID | NAME | DATE
---------------------------------------------------
1 | ITEM A | 2015-2-25 13:37:49
2 | ITEM A | 2015-2-25 14:37:49
3 | ITEM A | 2015-2-26 13:30:55
4 | ITEM B | 2015-2-26 15:37:49
5 | ITEM B | 2015-2-26 17:57:49
6 | ITEM C | 2015-2-27 13:00:33
(input month=02 and year=2015)
What I need to achieve with a view is the following:
NAME | 1| 2| 3|…|25|26|27|28|29|30|31|Total
------------------------------------------------------
ITEM A| 0| 0| 0|…| 2 | 1 | 0 | 0 | 0 | 0 | 0 | 3
ITEM B| 0| 0| 0|…| 0 | 2 | 0 | 0 | 0 | 0 | 0 | 2
ITEM C| 0| 0| 0|…| 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1
Any ideas would be very much appreciated.
Thanks in advance.
Sorry this is my first post.
You can do this using a PIVOT in your query
SELECT name,
[1],
[2],
[3],
[4],
[5],
[6],
[7],
[8],
[9],
[10],
[11],
[12],
[13],
[14],
[15],
[16],
[17],
[18],
[19],
[20],
[21],
[22],
[23],
[24],
[25],
[26],
[27],
[28],
[29],
[30],
[31],
([1] + [2] + [3] + [4] + [5] + [6] + [7] + [8] + [9] + [10] + [11] + [12] + [13] + [14] + [15] + [16] + [17] + [18] + [19] + [20] + [21] + [22] + [23] + [24] + [25] + [26] + [27] + [28] + [29] + [30] + [31]) as total
FROM
(
SELECT Name,
id,
Datepart(day, [date]) day
FROM item
WHERE MONTH([date]) = 2 AND YEAR([date]) = 2015
) x
PIVOT
(
count(id)
FOR day IN ([1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12], [13], [14], [15], [16], [17], [18], [19], [20], [21], [22], [23], [24], [25], [26], [27], [28], [29], [30], [31])
) p
This will do it for you. First test data:
CREATE TABLE data ([ID] int, [Name] varchar(30), [Date] datetime)
INSERT INTO data ([ID], [Name], [Date])
SELECT 1,'ITEM A','2015-2-25 13:37:49'
UNION ALL SELECT 2,'ITEM A','2015-2-25 14:37:49'
UNION ALL SELECT 3,'ITEM A','2015-2-26 13:30:55'
UNION ALL SELECT 4,'ITEM B','2015-2-26 15:37:49'
UNION ALL SELECT 5,'ITEM B','2015-2-26 17:57:49'
UNION ALL SELECT 6,'ITEM C','2015-2-27 13:00:33'
Then the query. Note you can use any data range, so if you want a full month just calculate that and put it in the #startDate and #endDate
DECLARE #startDate DATETIME='25-Feb-2015'
DECLARE #endDate DATETIME='28-Feb-2015'
DECLARE #numberOfDays INT = DATEDIFF(DAY, #startDate, #endDate)
declare #dayColumns TABLE (delta int, colName varchar(12))
-- Produce 1 row for each day in the report. Note that this is limited by the
-- number of objects in sysobjects (which is about 2000 so its a high limit)
-- Each row contains a delta date offset, #startDate+delta gives each date to report
-- which is converted to a valid SQL column name in the format colYYYYMMDD
INSERT INTO #dayColumns (delta, colName)
SELECT delta, 'col'+CONVERT(varchar(12),DATEADD(day,delta,#startDate),112) as colName from (
select (ROW_NUMBER() OVER (ORDER BY sysobjects.id))-1 as delta FROM sysobjects
) daysAhead
WHERE delta<=#numberOfDays
-- Create a comma seperated list of columns to report
DECLARE #cols AS NVARCHAR(MAX)= ''
SELECT #cols=CASE WHEN #cols='' THEN #cols ELSE #cols+',' END + colName FROM #dayColumns ORDER BY delta
DECLARE #totalCount AS NVARCHAR(MAX)= ''
SELECT #totalCount=CASE WHEN #totalCount='' THEN '' ELSE #totalCount+' + ' END + 'ISNULL(' + colName +',0)' FROM #dayColumns ORDER BY delta
-- Produce a SQL statement which outputs a variable number of pivoted columns
DECLARE #query AS NVARCHAR(MAX)
SELECT #query=
'declare #days TABLE (reportDay date, colName varchar(12))
INSERT INTO #days (reportDay, colName)
SELECT DATEADD(day,Delta,'''+CONVERT(varchar(22),#startDate,121)+'''), ''col''+CONVERT(varchar(12),DATEADD(day,delta,'''+CONVERT(varchar(22),#startDate,121)+'''),112) as colName from (
select (ROW_NUMBER() OVER (ORDER BY sysobjects.id))-1 as Delta FROM sysobjects
) daysAhead
WHERE Delta<='+CAST(#numberOfDays as varchar(10))+'
SELECT pivoted.*,'+#totalCount+' as total FROM (
SELECT * FROM (
select data.Name, d.colName, 1 as numRows
from #days d
LEFT OUTER JOIN data ON CAST(data.[Date] as DATE)=d.reportDay
) as s
PIVOT (
SUM(numRows) FOR colName in ('+#cols+')
) as pa
) as pivoted
WHERE Name is not null'
-- Run the query
EXEC (#query)
Output is:
Name col20150225 col20150226 col20150227 col20150228 total
------------------------------ ----------- ----------- ----------- ----------- -----------
ITEM A 2 1 NULL NULL 3
ITEM B NULL 2 NULL NULL 2
ITEM C NULL NULL 1 NULL 1
You can determine the date of each column by parsing the column header in your presentation code (it's format is colYYYYMMDD).
select convert(varchar,PaymentDate,103) AS date,
DatePart(MONTH,PaymentDate) as month,
CAST(SUM(Total_Amount) as INT) as Revenue
from Tbl_Name
where YEAR(PaymentDate) = YEAR(CURRENT_TIMESTAMP)
AND MONTH(PaymentDate) = MONTH(CURRENT_TIMESTAMP)
GROUP BY convert(varchar,PaymentDate,103),
DatePart(MONTH,PaymentDate)
order by date;

How would I write a SQL query to include monthly counts for all categories with all months and categories showing even if no records exist

I'm working with SSRS and I'm trying to populate a report to show monthly counts grouped by categories. The report should have the categories listed as row headers and all months of the year shown as column headers. The requirement is that I need to display all months and categories regardless if there is data that falls within those columns/rows.
My problem is constructing the SQL query to do just that.
Here are examples of the tables I'm working with:
Transaction table:
create table [Transaction] (
ContactID int Primary Key
, CategoryID int
, DateKey int
)
Calendar table:
Note that this table was originally created as a Date Dimension to be used with SSAS but I decided not to use SSAS as cube development was getting overwhelming for me. There are many other fields in this table but these are the fields of importance regarding this issue.
create table [Calendar] (
DateID int Primary Key
, Date datetime
, Year nchar(4)
, Month nvarchar(2)
)
Category table:
create table [Category] (
CategoryID int Primary Key
, CategoryName nvarchar
)
The query needs to return a dataset to be used in SSRS to populate a report similar to the following:
Category | Jan | Feb | Mar | Apr | May | June | etc...
--------------------------------------------------------------------
Category A | - | - | - | - | - | - | etc...
--------------------------------------------------------------------
Category B | - | - | - | - | - | - | etc...
--------------------------------------------------------------------
Category C | - | - | - | - | - | - | etc...
I know that it involved some combination of OUTER JOINS, GROUP BY, and subqueries. I just can't wrap my head around how to accomplish this.
I'd be very happy if someone could assist me in this issue.
Thanks
The following query gets all the categories in the transactions. It is pivoting on the month, by extracting the month from the date and counting the number of transactions. This will return a row for all categories in the data
select c.categoryName,
sum(case when extract(month from date) = 1 then 1 else 0 end) as Jan,
sum(case when extract(month from date) = 2 then 1 else 0 end) as Feb,
sum(case when extract(month from date) = 3 then 1 else 0 end) as Mar,
. . .
from transaction t join
category c
on t.categoryID = c.categoryID
group by categoryName
order by categoryName
If you actually need all categories, even when there are no transactions at all, then use a right outer join:
select c.categoryName,
sum(case when extract(month from date) = 1 then 1 else 0 end) as Jan,
sum(case when extract(month from date) = 2 then 1 else 0 end) as Feb,
sum(case when extract(month from date) = 3 then 1 else 0 end) as Mar,
. . .
from transaction t right outer join
category c
on t.categoryID = c.categoryID
group by categoryName
order by categoryName
You could use the PIVOT function:
WITH Data AS
( SELECT CategoryName,
Calendar.[Month],
ContactID
FROM Category
LEFT JOIN [Transaction]
ON [Transaction].CategoryID = Category.CategoryID
LEFT JOIN Calendar
AND [Transaction].DateKey = Calendar.DateID
)
SELECT CategoryName,
[1] AS [Jan],
[2] AS [Feb],
[3] AS [Mar],
[4] AS [Apr],
[5] AS [May],
[6] AS [Jun],
[7] AS [Jul],
[8] AS [Aug],
[9] AS [Sep],
[10] AS [Oct],
[11] AS [Nov],
[12] AS [Dec]
FROM Data
PIVOT
( COUNT(ContactID)
FOR [Month] IN ([1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12])
) pvt