Issue Using PIVOT in SQL - sql

I am trying to use POVIT while looking at this example This
Here is the Code:
CREATE DATABASE DEMO
GO
 
USE DEMO
GO
 
-- Creating table for demo
IF (object_id('TblPivot','U') > 0)
DROP TABLE TblPivot
 
CREATE TABLE TblPivot
(
ItemCode int,
ItemName varchar(100),
ItemColour varchar(50)
)
GO
 
-- Inerting some sample records
INSERT INTO TblPivot
SELECT 1,'Samsung Mobile','Red'
UNION ALL
SELECT 2,'Nokia Mobile','Blue'
UNION ALL
SELECT 3,'Nokia Mobile','Green'
UNION ALL
SELECT 4,'Motorola Mobile','Red'
UNION ALL
SELECT 5,'Samsung Mobile','Green'
UNION ALL
SELECT 2,'Nokia Mobile','Blue'
UNION ALL
SELECT 1,'Samsung Mobile','Red'
UNION ALL
SELECT 2,'Nokia Mobile','Blue'
GO
And here is the PIVOT selection
-- Getting table data
SELECT
ItemCode,
ItemName,
ItemColour
from TblPivot
GO
-- Getting agreegated data using Pivot and converted rows to column
SELECT
*
FROM
(
SELECT
ItemCode,
ItemName,
ItemColour
FROM TblPivot
) AS P
PIVOT
(
Count(ItemName) FOR ItemColour IN (Red, Blue, Green)// Here is the Issue
where it knows what words to give it like Red,Blue,Green what I want is to use
what ever the ItemColur it could be 100s What ever you get
from the data base use that for the `IN(ItemColur)`
) AS pv
GO
It keeps saying that The column name "ItemColur" specified in the PIVOT operator conflicts with the existing column name in the PIVOT argument.
How can I get this working with that I even tried to make a Temp Table didn't work

This is the code for dynamic PIVOT:
DECLARE #columns NVARCHAR(MAX), #sql NVARCHAR(MAX);
SET #columns = N'';
SELECT #columns += N', p.' + QUOTENAME(ItemColour)
FROM (SELECT distinct p.ItemColour FROM dbo.TblPivot AS p
) AS x;
SET #sql = N'
SELECT itemcode, ' + STUFF(#columns, 1, 2, '') + '
FROM
(
SELECT p.itemcode, p.ItemName, p.ItemColour
FROM dbo.TblPivot AS p
) AS j
PIVOT
(
COUNT(ItemName) FOR ItemColour IN ('
+ STUFF(REPLACE(#columns, ', p.[', ',['), 1, 1, '')
+ ')
) AS p;';
PRINT #sql;
EXEC sp_executesql #sql;
The output for above query looks like this:

Related

Pivot table from horizontal to vertical

i have a following table of results:
What i want is a resulted table with following structure:
Is any posibility to do that with SQL?
Thanks in advance.
EDIT (SQL query with exampled temp table):
CREATE TABLE #FINAL_STACK
(
FB_DATE datetime,
FB_DESC VARCHAR(200)
)
INSERT INTO #FINAL_STACK(FB_DATE, FB_DESC)
SELECT '2017-03-09', 'D - FIZ: 1'
UNION
SELECT '2017-03-09', 'D - PRI: 1'
UNION
SELECT '2017-03-10', 'D - FIZ: 1'
UNION
SELECT '2017-03-10', 'D - PRI: 1'
UNION
SELECT '2017-03-13', 'D - FIZ: 2'
UNION
SELECT '2017-03-13', 'D - PRI: 1'
UNION
SELECT '2017-03-13', 'D - TEPAP: 1'
SELECT * FROM #FINAL_STACK
Try this:
DECLARE #DynammicTSQLStatement NVARCHAR(MAX)
,#DynamicPIVOTColumns NVARCHAR(MAX);
SET #DynamicPIVOTColumns = STUFF
(
(
SELECT ',[' + CAST([FB_DATE] AS VARCHAR(12)) + ']'
FROM #FINAL_STACK
GROUP BY [FB_DATE]
ORDER BY [FB_DATE]
FOR XML PATH('') ,TYPE
).value('.', 'NVARCHAR(MAX)')
,1
,1
,''
);
SET #DynammicTSQLStatement = N'
SELECT *
FROM
(
SELECT *
,ROW_NUMBER() OVER (PARTITION BY FB_DATE ORDER BY (SELECT 1)) AS RID
FROM #FINAL_STACK
) DS
PIVOT
(
MAX([FB_DESC]) FOR [FB_DATE] IN (' + #DynamicPIVOTColumns + ')
) PVT';
EXEC sp_executesql #DynammicTSQLStatement;
We need to perform dynamic pivot in order to be sure it will always work for different days. Also, note we are creating a row ID column using ROW_NUMBER in order to ensure all records for particular date are displayed. Otherwise, you will get only one value (for example the min or the max) depending on the PIVOT aggregate function.

Pivot multiple rows in initial table

I have a table which i would like to pivot to show how many categories a person is affiliated with...
I would like to pivot this to show:
There are a lot more members and categories but the theory i believe should be the same.
I have attempted this however it only shows the first line for each.
Thanks in advance
Will
#Will, this is the logic you need. Basically a pivot function on the column of interest.
DECLARE #tbl TABLE (RegNo varchar(20), Category varchar(20), Number int)
INSERT INTO #tbl
SELECT 'R1050162', 'Gym', 1 UNION ALL
SELECT 'R1050162', 'Personal Trainer', 1 UNION ALL
SELECT 'R0093126', 'Group Exercise', 1 UNION ALL
SELECT 'R0143614', 'Yoga Teacher', 1
SELECT *
FROM
#tbl
PIVOT
(
SUM(Number)
FOR Category IN ([Gym], [Personal Trainer], [Group Exercise], [Yoga Teacher]
)
) AS PivotTable;
Output is below:
You need to use just sum the Number field in PIVOT function and for numerous categories get a category list:
DECLARE #categories AS NVARCHAR(MAX),
#your_query AS NVARCHAR(MAX);
select #categories = STUFF((SELECT distinct ',' + QUOTENAME(Category)
FROM your_table
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT RegNo, ' + #categories + ' from
(
SELECT RegNo, Category, Number FROM your_table) tab
PIVOT
(
SUM(Number)
FOR Category IN (' + #categories + ')
) p iv
ORDER BY piv.RegNo'
execute(#your_query)

SQL Server - PIVOT using variable

I'm trying to do a pivot in SQL Server using a column with a lot of levels. Currently I'm doing a
select '[' + category_1 + '],', count(*)
from myTable
group by '[' + category_1 + '],'
and copying and pasting the output into the text editor.
I was wondering if it's possible to declare a variable and use that as the IN ([],[],[]) information in the pivot command. Has anyone been able to do that?
I think it would look something like this:
#factorLevels = (select '[' + category_1 + '],' from (select category_1, count(*) from myTable) t1)
SELECT *
FROM
(select category_1, item_count from myTable) as t1
PIVOT
(
SUM(item_count)
FOR category_1 IN (#factorLevels)
) as PivotTable;
If I'm understanding you correctly, I think I have a solution. You can use dynamic SQL for this.
You will need to set a variable that contains all the values for the field by which you want to pivot, and then assign the query itself into a variable and execute it to get results:
DECLARE
#values VARCHAR(50),
#querytext VARCHAR(MAX)
;
SET #values = (SELECT '('+(SELECT STUFF(( SELECT ',[' + category_1 +']' FROM
<your table> FOR XML PATH('')), 1, 1, '') )+')')
;
SELECT #querytext =
'SELECT *
FROM (
SELECT
<your fields>
FROM
<your table>
WHERE
<your criteria>
GROUP BY
<your grouping>
) AS Main
PIVOT (
SUM(item_count)
FOR [category_1] IN ' + #values + '
) AS PivotSum'
EXEC sp_executesql #querytext

Dynamic SELECT statement, generate columns based on present and future values

Currently building a SELECT statement in SQL Server 2008 but would like to make this SELECT statement dynamic, so the columns can be defined based on values in a table. I heard about pivot table and cursors, but seems kind of hard to understand at my current level, here is the code;
DECLARE #date DATE = null
IF #date is null
set # date = GETDATE() as DATE
SELECT
Name,
value1,
value2,
value3,
value4
FROM ref_Table a
FULL OUTER JOIN (
SELECT
PK_ID ID,
sum(case when FK_ContainerType_ID = 1 then 1 else null) Box,
sum(case when FK_ContainerType_ID = 2 then 1 else null) Pallet,
sum(case when FK_ContainerType_ID = 3 then 1 else null) Bag,
sum(case when FK_ContainerType_ID = 4 then 1 else null) Drum
from
Packages
WHERE
#date between PackageStart AND PackageEnd
group by PK_ID ) b on a.Name = b.ID
where
Group = 0
The following works great for me , but PK_Type_ID and the name of the column(PackageNameX,..) are hard coded, I need to be dynamic and it can build itself based on present or futures values in the Package table.
Any help or guidance on the right direction would be greatly appreciated...,
As requested
ref_Table (PK_ID, Name)
1, John
2, Mary
3, Albert
4, Jane
Packages (PK_ID, FK_ref_Table_ID, FK_ContainerType_ID, PackageStartDate, PackageEndDate)
1 , 1, 4, 1JAN2014, 30JAN2014
2 , 2, 3, 1JAN2014, 30JAN2014
3 , 3, 2, 1JAN2014, 30JAN2014
4 , 4, 1, 1JAN2014, 30JAN2014
ContainerType (PK_ID, Type)
1, Box
2, Pallet
3, Bag
4, Drum
and the result should look like this;
Name Box Pallet Bag Drum
---------------------------------------
John 1
Mary 1
Albert 1
Jane 1
The following code like I said works great, the issue is the Container table is going to grow and I need to replicated the same report without hard coding the columns.
What you need to build is called a dynamic pivot. There are plenty of good references on Stack if you search out that term.
Here is a solution to your scenario:
IF OBJECT_ID('tempdb..##ref_Table') IS NOT NULL
DROP TABLE ##ref_Table
IF OBJECT_ID('tempdb..##Packages') IS NOT NULL
DROP TABLE ##Packages
IF OBJECT_ID('tempdb..##ContainerType') IS NOT NULL
DROP TABLE ##ContainerType
SET NOCOUNT ON
CREATE TABLE ##ref_Table (PK_ID INT, NAME NVARCHAR(50))
CREATE TABLE ##Packages (PK_ID INT, FK_ref_Table_ID INT, FK_ContainerType_ID INT, PackageStartDate DATE, PackageEndDate DATE)
CREATE TABLE ##ContainerType (PK_ID INT, [Type] NVARCHAR(50))
INSERT INTO ##ref_Table (PK_ID,NAME)
SELECT 1,'John' UNION
SELECT 2,'Mary' UNION
SELECT 3,'Albert' UNION
SELECT 4,'Jane'
INSERT INTO ##Packages (PK_ID, FK_ref_Table_ID, FK_ContainerType_ID, PackageStartDate, PackageEndDate)
SELECT 1,1,4,'2014-01-01','2014-01-30' UNION
SELECT 2,2,3,'2014-01-01','2014-01-30' UNION
SELECT 3,3,2,'2014-01-01','2014-01-30' UNION
SELECT 4,4,1,'2014-01-01','2014-01-30'
INSERT INTO ##ContainerType (PK_ID, [Type])
SELECT 1,'Box' UNION
SELECT 2,'Pallet' UNION
SELECT 3,'Bag' UNION
SELECT 4,'Drum'
DECLARE #DATE DATE, #PARAMDEF NVARCHAR(MAX), #COLS NVARCHAR(MAX), #SQL NVARCHAR(MAX)
SET #DATE = '2014-01-15'
SET #COLS = STUFF((SELECT DISTINCT ',' + QUOTENAME(T.[Type])
FROM ##ContainerType T
FOR XML PATH, TYPE).value('.', 'NVARCHAR(MAX)'),1,1,'')
SET #SQL = 'SELECT [Name], ' + #COLS + '
FROM (SELECT [Name], [Type], 1 AS Value
FROM ##ref_Table R
JOIN ##Packages P ON R.PK_ID = P.FK_ref_Table_ID
JOIN ##ContainerType T ON P.FK_ContainerType_ID = T.PK_ID
WHERE #DATE BETWEEN P.PackageStartDate AND P.PackageEndDate) X
PIVOT (COUNT(Value) FOR [Type] IN (' + #COLS + ')) P
'
PRINT #COLS
PRINT #SQL
SET #PARAMDEF = '#DATE DATE'
EXEC SP_EXECUTESQL #SQL, #PARAMDEF, #DATE=#DATE
Output:
Name Bag Box Drum Pallet
Albert 0 0 0 1
Jane 0 1 0 0
John 0 0 1 0
Mary 1 0 0 0
Static Query:
SELECT [Name],[Box],[Pallet],[Bag],[Drum] FROM
(
SELECT *
FROM
(
SELECT rf.Name,cnt.[Type], pk.PK_ID AS PKID, rf.PK_ID AS RFID
FROM ref_Table rf INNER JOIN Packages pk ON rf.PK_ID = pk.FK_ref_Table_ID
INNER JOIN ContanerType cnt ON cnt.PK_ID = pk.FK_ContainerType_ID
) AS SourceTable
PIVOT
(
COUNT(PKID )
FOR [Type]
IN ( [Box],[Pallet],[Bag],[Drum])
) AS PivotTable
) AS Main
ORDER BY RFID
Dynamic Query:
DECLARE #columnList nvarchar (MAX)
DECLARE #pivotsql nvarchar (MAX)
SELECT #columnList = STUFF(
(
SELECT ',' + '[' + [Type] + ']'
FROM ContanerType
FOR XML PATH( '')
)
,1, 1,'' )
SET #pivotsql =
N'SELECT [Name],' + #columnList + ' FROM
(
SELECT *
FROM
(
SELECT rf.Name,cnt.[Type], pk.PK_ID AS PKID, rf.PK_ID AS RFID
FROM ref_Table rf INNER JOIN Packages pk ON rf.PK_ID = pk.FK_ref_Table_ID
INNER JOIN ContanerType cnt ON cnt.PK_ID = pk.FK_ContainerType_ID
) AS SourceTable
PIVOT
(
COUNT(PKID )
FOR [Type]
IN ( ' + #columnList + ')
) AS PivotTable
) AS Main
ORDER BY RFID;'
EXEC sp_executesql #pivotsql
Following my tutorial below will help you to understand the PIVOT functionality:
We write sql queries in order to get different result sets like full, partial, calculated, grouped, sorted etc from the database tables. However sometimes we have requirements that we have to rotate our tables. Sounds confusing?
Let's keep it simple and consider the following two screen grabs.
SQL Table:
Expected Results:
Wow, that's look like a lot of work! That is a combination of tricky sql, temporary tables, loops, aggregation......, blah blah blah
Don't worry let's keep it simple, stupid(KISS).
MS SQL Server 2005 and above has a function called PIVOT. It s very simple to use and powerful. With the help of this function we will be able to rotate sql tables and result sets.
Simple steps to make it happen:
Identify all the columns those will be part of the desired result set.
Find the column on which we will apply aggregation(sum,ave,max,min etc)
Identify the column which values will be the column header.
Specify the column values mentioned in step3 with comma separated and surrounded by square brackets.
So, if we now follow above four steps and extract information from the above sales table, it will be as below:
Year, Month, SalesAmount
SalesAmount
Month
[Jan],[Feb] ,[Mar] .... etc
We are nearly there if all the above steps made sense to you so far.
Now we have all the information we need. All we have to do now is to fill the below template with required information.
Template:
Our SQL query should look like below:
SELECT *
FROM
(
SELECT SalesYear, SalesMonth,Amount
FROM Sales
) AS SourceTable
PIVOT
(
SUM(Amount )
FOR SalesMonth
IN ( [Jan],[Feb] ,[Mar],
[Apr],[May],[Jun] ,[Jul],
[Aug],[Sep] ,[Oct],[Nov] ,[Dec])
) AS PivotTable;
In the above query we have hard coded the column names. Well it's not fun when you have to specify a number of columns.
However, there is a work arround as follows:
DECLARE #columnList nvarchar (MAX)
DECLARE #pivotsql nvarchar (MAX)
SELECT #columnList = STUFF(
(
SELECT ',' + '[' + SalesMonth + ']'
FROM Sales
GROUP BY SalesMonth
FOR XML PATH( '')
)
,1, 1,'' )
SET #pivotsql =
N'SELECT *
FROM
(
SELECT SalesYear, SalesMonth,Amount
FROM Sales
) AS SourceTable
PIVOT
(
SUM(Amount )
FOR SalesMonth
IN ( ' + #columnList +' )
) AS PivotTable;'
EXEC sp_executesql #pivotsql
Hopefully this tutorial will be a help to someone somewhere.
Enjoy coding.

Convert a row as column and merge two column as its value

I have stuck in a select statement, converting rows into columns. I have tried with PIVOT, i was able to convert the single column. But my requirement is little different. I have explained the requirement below.
I have a table structure as below,
I want to select the data as below,
The values in the table are dynamic, which is not a problem for me to deal with that. But i need a way to get the below result.
Could someone please give me a hint on doing it, may be a way to modify the PIVOT below.
select *
from
(
select TSID,AID,Count,BID
from tbl TS
WHERE TS.TPID = 1
) src
pivot
(
sum(Count)
for AID in (AID1,AID2,AID3)
) piv
Thank you..
You may check this fiddle
EDIT
This will work for not previously known column names
DECLARE #Columns AS VARCHAR(MAX)
DECLARE #SQL AS VARCHAR(MAX)
SELECT #Columns = STUFF(( SELECT DISTINCT ',' + AID
FROM Table1
FOR
XML PATH('')
), 1, 1, '')
SET #SQL = '
;WITH MyCTE AS
(
SELECT TSID,
AID,
STUFF(( SELECT '','' + CONVERT(VARCHAR,[Count] )
FROM Table1 I Where I.TSID = O.TSID
FOR
XML PATH('''')
), 1, 1, '''') AS CountList
FROM Table1 O
GROUP BY TSID,
AID
)
SELECT *
FROM MyCTE
PIVOT
(
MAX(CountList)
FOR AID IN
(
' + #Columns + '
)
) AS PivotTable'
EXEC(#SQL)