I can not seem to find an example of this type of pivot if it is possible.
+---------+---------+
| column1 | column2 |
+---------+---------+
| 10 | A |
| 20 | B |
| 30 | C |
+---------+---------+
I would like my query to return
+----+----+----+
| A | B | C |
+----+----+----+
| 10 | 20 | 30 |
+----+----+----+
I would like to do it without creating a view or dynamic table.
I have been trying the pivot option but I am having a problem understanding how to use dynamic values from the query. My query is:
Select column1, column3 + '_' + column4 as column2
from table1
Now I want to use the column2 values as columns names with the associated column1 values.
You can use the PIVOT function:
select A, B, C
from yourtable
pivot
(
max(column1) -- column values
for column2 in (A, B, C) -- new column names
) p;
If you have dynamic columns then you will have to use dynamic SQL and implement it inside of a stored procedure. The code would be similar to:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
-- create the list of columns for the pivot
select #cols = STUFF((SELECT ',' + QUOTENAME(column2)
from
(
select column3 + '_' + column4 as column2
from table1
) d
group by column2
order by column2
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT ' + #cols + '
from
(
Select column1, column3 + ''_'' + column4 as column2
from table1
) x
pivot
(
max(column1)
for column2 in (' + #cols + ')
) p '
exec sp_executesql #query;
select case when column2 = 'A' then column1 end as A,
case when column2 = 'B' then column1 end as B,
case when column2 = 'C' then column1 end as C
from your_table
Related
i have a problem with pivot table in SQL Server:
I have a table with the next information (i dont know how much different values could have column1):
Column1 Value
---------------
PRODUCT_4 1
PRODUCT_4 2
PRODUCT_4 3
PRODUCT_6 10
PRODUCT_6 20
PRODUCT_6 30
PRODUCT_8 100
PRODUCT_8 200
PRODUCT_8 300
... ...
PRODUCT_X 1
PRODUCT_X 2
PRODUCT_X 3
I want to transform this into a pivot table to get the next output:
product_4 product_6 product_8 ... product_x
1 10 100 1
2 20 200 2
3 30 300 3
I was using the next query:
DECLARE
#cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(column1)
from dbo.Context_Table
group by column1
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
print #cols
set #query = N'SELECT ' + #cols + N' from
(
select value, Column1
from dbo.Context_Table
) x
pivot
(
max(value)
for Column1 in (' + #cols + N')
) p '
exec sp_executesql #query;
But the ouput is not that i expected...
PRODUCT_4 PRODUCT_6 PRODUCT_8 PRODUCT_X
3 30 300 3
My question is... how can i get all the values without using the aggregate function MAX?
Thanks all!
add ROW_NUMBER() to your dynamic pivot:
set #query = N'SELECT ' + #cols + N' from
(
select ROW_NUMBER() OVER (PARTITION BY Column1 ORDER BY value) RN , value, Column1
from ##T
) x
pivot
(
max([value])
for Column1 in (' + #cols + N')
) p '
exec sp_executesql #query;
You must add a changing column to your query, I do this with ROW_NUMBER():
Just try it out:
DECLARE #tbl TABLE(Column1 VARCHAR(100),[Value] INT)
INSERT INTO #tbl VALUES
('PRODUCT_4',1)
,('PRODUCT_4',2)
,('PRODUCT_4',3)
,('PRODUCT_6',10)
,('PRODUCT_6',20)
,('PRODUCT_6',30)
,('PRODUCT_8',100)
,('PRODUCT_8',200)
,('PRODUCT_8',300)
,('PRODUCT_X',1)
,('PRODUCT_X',2)
,('PRODUCT_X',3)
SELECT p.*
FROM
(
SELECT ROW_NUMBER() OVER(PARTITION BY Column1 ORDER BY [Value]) AS ValueInx
,*
FROM #tbl AS t
) AS tbl
PIVOT
(
MAX([Value]) FOR Column1 IN(PRODUCT_4,PRODUCT_6,PRODUCT_8,PRODUCT_X)
) AS p:
The result
+----------+-----------+-----------+-----------+-----------+
| ValueInx | PRODUCT_4 | PRODUCT_6 | PRODUCT_8 | PRODUCT_X |
+----------+-----------+-----------+-----------+-----------+
| 1 | 1 | 10 | 100 | 1 |
+----------+-----------+-----------+-----------+-----------+
| 2 | 2 | 20 | 200 | 2 |
+----------+-----------+-----------+-----------+-----------+
| 3 | 3 | 30 | 300 | 3 |
+----------+-----------+-----------+-----------+-----------+
You will easily integrate this into your dyanmic approach...
Try this/ Or something along these lines.
select PRODUCT_4,PRODUCT_6,PRODUCT_8
from
(select name,value,row_number() over (partition by name order by value) as id from product) p
pivot
(
max(value)
for name
IN( PRODUCT_4,PRODUCT_6,PRODUCT_8)
)
as a
Hope this helps.
Thanks.
I have a table with a single column like so:
+-------------+
|(Column Name)|
+-------------+
|Data1 |
+-------------+
|Data2 |
+-------------+
|Data3 |
+-------------+
|Data4 |
+-------------+
|Data5 |
+-------------+
What I want to do seems very simple, but I am not able to find any examples of it anywhere. All I want is to convert the above column into a single row like so:
+-------+-------+-------+-------+-------+
| (Col1)| (Col2)| (Col3)| (Col4)| (Col5)|
+-------+-------+-------+-------+-------+
| Data1 | Data2 | Data3 | Data4 | Data5 |
+-------+-------+-------+-------+-------+
I'm sure this is a very simple task, but I am extremely new to working with databases. I appreciate any help.
You can do pivot as below:
Select * from (
Select colname, RowN = Row_Number() over (order by colname) from #cols
) a
pivot (max(colname) for RowN in ([1],[2],[3],[4],[5])) p
For dynamic list of columns
Declare #cols nvarchar(max)
Declare #query nvarchar(max)
Select #cols = stuff((select ','+QuoteName(Row_Number() over (Order by (Select NULL))) from #cols for xml path('')),1,1,'')
Select #query = ' Select * from (
Select colname, RowN = Row_Number() over (order by colname) from #cols
) a
pivot (max(colname) for RowN in (' + #cols + ')) p '
Exec sp_executesql #query
Pivot Query:
CREATE TABLE Sales ([Month] VARCHAR(20) ,SaleAmount INT)
INSERT INTO Sales VALUES ('January', 100)
INSERT INTO Sales VALUES ('February', 200)
INSERT INTO Sales VALUES ('March', 300)
SELECT * FROM SALES
Example:
SELECT [January]
, [February]
, [March]
FROM ( SELECT [Month]
, SaleAmount
FROM Sales
) p PIVOT ( SUM(SaleAmount)
FOR [Month]
IN ([January],[February],[March])
) AS pvt
I have requirement of sorting dynamically made column from row.
I have following structure of data in SQL :
All attributes are treated as column but actually it stored in DB as row and their respective TextValue(If type is Text),DateValue (if type is date time or date)
Id | TextValue | DateValue | Attribute
--------------------------------------------
1 | abc | - | SiteLocation
2 | - | 1-1-2013 | Holiday date
3 | xyz | - | SiteLocation
4 | - | 2-2-2014 | Holiday date
5 | pqr | - | SiteLocation
6 | abc | - | SiteLocation
I want to apply sorting on SiteLocation and I am displaying it as column.
So how can i achieve this
SiteLocation | Holiday date
abc | -
- | 1-1-2013
xyz |-
- |2-2-2014
pqr |-
abc |-
I want to apply sorting on SiteLcoation or Holiday date in UI grid.
Please suggest me some way how can I do it?
Here is your table
CREATE TABLE #TEMP(Id INT,TextValue VARCHAR(100),DateValue DATE,Attribute VARCHAR(100))
INSERT INTO #TEMP
SELECT 1 Id, 'abc' TextValue ,NULL DateValue ,'SiteLocation' Attribute
UNION ALL
SELECT 2 ,NULL ,'1-1-2013' ,'Holiday date'
UNION ALL
SELECT 3 ,'xyz' ,NULL ,'SiteLocation'
UNION ALL
SELECT 4 , NULL ,'2-2-2014' ,'Holiday date'
UNION ALL
SELECT 5 ,'pqr' ,NULL ,'SiteLocation'
UNION ALL
SELECT 6 ,'abc' ,NULL ,'SiteLocation'
QUERY
SELECT [SiteLocation],[Holiday DATE]
FROM
(
SELECT ID,ISNULL(TextValue,DateValue) VALUE,Attribute
FROM #TEMP
)P
PIVOT
(
min(VALUE) FOR
Attribute IN ([SiteLocation],[Holiday DATE])
)
AS i
SQL FIDDLE
UPDATE
I am updating the query as you suggested.
Here you will select the columns for converting rows to columns
DECLARE #cols NVARCHAR (MAX)
SELECT #cols = COALESCE (#cols + ',[' + [Attribute] + ']',
'[' + [Attribute] + ']')
FROM (SELECT DISTINCT [Attribute] FROM #TEMP) PV
ORDER BY [Attribute]
Now you can pivot dynamically here.
DECLARE #query NVARCHAR(MAX)
SET #query = '
-- Your pivoted columns will be displayed
SELECT ' + #cols + ' FROM
(
-- Combine into single column
SELECT ID,ISNULL(TextValue,DateValue) VALUE,Attribute
FROM #TEMP
) x
PIVOT
(
MIN(VALUE)
FOR [Attribute] IN (' + #cols + ')
) p
'
EXEC SP_EXECUTESQL #query
SQL FIDDLE
I am trying to pivot on two columns in SQL Server 2008 on an invoice table. So I have data like the follows:
+--------------+--------+---------+------+
| Invoice Date | Item # | Dollars | Lbs. |
+--------------+--------+---------+------+
| 1/1/14 | A | 1 | 1 |
| 1/2/14 | B | 2 | 2 |
| 1/3/14 | A | 3 | 3 |
| 1/4/14 | B | 4 | 4 |
| 2/1/14 | A | 5 | 5 |
| 2/1/14 | B | 6 | 6 |
+--------------+--------+---------+------+
I would like to display it as
+--------+--------------+-----------------+--------------+-----------------+
| Item # | 1/31/14 Lbs. | 1/31/14 Dollars | 2/28/14 Lbs. | 2/28/14 Dollars |
+--------+--------------+-----------------+--------------+-----------------+
| A | 4 | 4 | 5 | 5 |
| B | 6 | 6 | 6 | 6 |
+--------+--------------+-----------------+--------------+-----------------+
Note the column name is the last day of that month and either dollars or pounds. I can do it just fine one column (either pounds or dollars) however I can't do it on both.
Here is my example code for just pounds:
DECLARE
#v_Columns VARCHAR(MAX),
#v_Query VARCHAR(MAX)
--pivot and delimit values
SELECT #v_Columns = COALESCE(#v_Columns,'[') + convert(varchar(8), InvoiceDate, 1) + ' Lbs.' + '],['
FROM
( SELECT DISTINCT dbo.ufn_GetLastDayOfMonth(InvoiceDate) As InvoiceDate
FROM Invoice
WHERE InvoiceDate BETWEEN #BEGIN_DATE AND #END_DATE
ORDER BY InvoiceDate
--delete last two chars of string (the ending ',[')
SET #v_Columns = SUBSTRING(#v_Columns, 1, LEN(#v_Columns)-2)
PRINT #v_Columns
--construct sql statement
SET #v_Query =
'WITH AllOrders (LastInvoiceDate, Item, Pounds) AS
(
SELECT
CONVERT(varchar(8), dbo.ufn_GetLastDayOfMonth(Invoice.InvoiceDate), 1) + ''' + ' Lbs.' + ''' As LastInvoiceDate,
Item,
Pounds
FROM INVOICE
WHERE InvoiceDate BETWEEN #BEGIN_DATE AND #END_DATE
)
SELECT *
FROM AllOrders
PIVOT
(
SUM(QuantityShipped)
FOR LastInvoiceDate IN (' + #v_Columns + ')
) AS pivotview'
Thank you all in advance!
In order to get the result you are going to have to either PIVOT twice or UNPIVOT the Dollars and Lbs columns into a single column and then apply the PIVOT once. My preference would be to unpivot and then pivot because I find it to be much easier.
Instead of working dynamically first, you should write the query as a static or hard-coded version to get the logic correct, then convert it to dynamic SQL. The example that I have uses your final dates 201-01-31, etc because you are using a function to create those dates and should be able to apply that as needed.
Since you are using SQL Server 2005+, you can use CROSS APPLY to unpivot Dollars and Lbs. The code will be similar to the following:
select
t.ItemNo,
new_col = convert(varchar(10), t.[invoice date], 120) + '_'+ c.col,
c.value
from yourtable t
cross apply
(
select 'Dollars', Dollars union all
select 'Lbs', Lbs
) c (col, value);
See SQL Fiddle with Demo. This converts your data to the following format:
| ITEMNO | NEW_COL | VALUE |
|--------|--------------------|-------|
| A | 2014-01-31_Dollars | 1 |
| A | 2014-01-31_Lbs | 1 |
| B | 2014-01-31_Dollars | 2 |
| B | 2014-01-31_Lbs | 2 |
| A | 2014-01-31_Dollars | 3 |
I've concatenated into new_col the final column names that you'll need. Again you can format the date in whatever format you need, I just used 2014-01-31 and added the Dollars or Lbs to the end of it. Once you've got the data, you will PIVOT the values into your final desired result:
select ItemNo,
[2014-01-31_Lbs], [2014-01-31_Dollars],
[2014-02-28_Lbs], [2014-02-28_Dollars]
from
(
select
t.ItemNo,
new_col = convert(varchar(10), t.[invoice date], 120) + '_'+ c.col,
c.value
from yourtable t
cross apply
(
select 'Dollars', Dollars union all
select 'Lbs', Lbs
) c (col, value)
) d
pivot
(
sum(value)
for new_col in ([2014-01-31_Lbs], [2014-01-31_Dollars],
[2014-02-28_Lbs], [2014-02-28_Dollars])
) p;
See SQL Fiddle with Demo. Now you've got the result you want, so simply convert it to dynamic SQL:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(convert(varchar(10), t.[invoice date], 120) + '_'+ c.col)
from yourtable t
cross apply
(
select 'Lbs', 0 union all
select 'Dollars', 1
) c (col, so)
group by [invoice date], col, so
order by [invoice date], so
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT ItemNo,' + #cols + '
from
(
select
t.ItemNo,
new_col = convert(varchar(10), t.[invoice date], 120) + ''_''+ c.col,
c.value
from yourtable t
cross apply
(
select ''Dollars'', Dollars union all
select ''Lbs'', Lbs
) c (col, value)
) d
pivot
(
sum(value)
for new_col in (' + #cols + ')
) p '
exec sp_executesql #query;
See SQL Fiddle with Demo. This give a final result of:
| ITEMNO | 2014-01-31_LBS | 2014-01-31_DOLLARS | 2014-02-28_LBS | 2014-02-28_DOLLARS |
|--------|----------------|--------------------|----------------|--------------------|
| A | 4 | 4 | 5 | 5 |
| B | 6 | 6 | 6 | 6 |
Here is your sample table
CREATE TABLE #TEMP([Invoice Date] DATE,[Item #] VARCHAR(10),[DollarS] NUMERIC(10,0),[Lbs.] NUMERIC(10,0))
INSERT INTO #TEMP VALUES ('1/1/14', 'A',1,1)
INSERT INTO #TEMP VALUES ('1/2/14', 'B',2,2)
INSERT INTO #TEMP VALUES ('1/3/14', 'A',3,3)
INSERT INTO #TEMP VALUES ('1/4/14', 'B',4,4)
INSERT INTO #TEMP VALUES ('2/1/14', 'A',5,5)
INSERT INTO #TEMP VALUES ('2/1/14', 'B',6,6)
Now you need to apply UNION ALL(instead of UNPIVOT) and bring columns to row and combine the columns, get the order of columns as Date+LBS/DOLLARS.
SELECT DISTINCT DENSE_RANK() OVER(ORDER BY CAST(LASTDAY AS DATE),UNIT DESC)RNO,*,
CAST(DATEPART(MONTH,LASTDAY)AS VARCHAR) +'/'+ CAST(DATEPART(DAY,LASTDAY)AS VARCHAR) +'/' +RIGHT(CAST(YEAR(LASTDAY)AS VARCHAR),2)+' ' +UNIT PIVOTCOL
INTO #NEWTABLE
FROM
(
SELECT [Item #],'DOLLARS' UNIT,
DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,[Invoice Date])+1,0))LASTDAY,
SUM([Dollars]) OVER(PARTITION BY [Item #],DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,[Invoice Date])+1,0))) VALUE
FROM #TEMP
UNION ALL
SELECT [Item #], 'LBS.',
DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,[Invoice Date])+1,0))LASTDAY,
SUM([Lbs.]) OVER(PARTITION BY [Item #],DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,[Invoice Date])+1,0))) DOLLARSUM
FROM #TEMP
)TAB
Now declare the query to get the columns dynamically and to set NULL to Zero
DECLARE #cols NVARCHAR (MAX)
DECLARE #NullToZeroCols NVARCHAR (MAX)
SELECT #cols = COALESCE (#cols + ',[' + PIVOTCOL + ']',
'[' + PIVOTCOL + ']')
FROM (SELECT DISTINCT RNO,PIVOTCOL FROM #NEWTABLE) PV
ORDER BY RNO
PRINT #COLS
SET #NullToZeroCols = SUBSTRING((SELECT ',ISNULL(['+PIVOTCOL+'],0) AS ['+PIVOTCOL+']'
FROM(SELECT DISTINCT RNO,PIVOTCOL FROM #NEWTABLE GROUP BY RNO,PIVOTCOL)TAB
ORDER BY RNO FOR XML PATH('')),2,8000)
Now pivot the query
DECLARE #query NVARCHAR(MAX)
SET #query = 'SELECT [Item #],' + #NullToZeroCols + ' FROM
(
SELECT [Item #],VALUE,PIVOTCOL FROM #NEWTABLE
) x
PIVOT
(
SUM(VALUE)
FOR PIVOTCOL IN (' + #cols + ')
) p
ORDER BY [Item #];'
EXEC SP_EXECUTESQL #query
SQL FIDDLE
RESULT
I have the following data:
DECLARE #DataSource TABLE
(
[ColumnA] INT
,[ColumnB] INT
,[ColumnC] INT
)
INSERT INTO #DataSource ([ColumnA], [ColumnB], [ColumnC])
VALUES (5060,1006,100118)
,(5060,1006,100119)
,(5060,1006,100120)
,(5060,1007,100121)
,(5060,1007,100122)
,(5060,1012,100123)
SELECT [ColumnA]
,[ColumnB]
,[ColumnC]
FROM #DataSource
and I need to converted like this:
The difficult part is that the data is dynamic (I do not know how many columns I will have) and I am not able to use a standard pivot here because the values in ColumnC are different and as a result I am going to have as many columns as values appears in ColumnC.
Is there any technique to achieve this?
Any kind of help (answers, articles, suggestions) will be appreciated.
My suggestion whenever you are working with PIVOT is to alway write the query first with the values hard-coded, then you can easily convert the query to a dynamic solution.
Since you are going to have multiple values of columnC that will be converted to columns, then you need to look at using the row_number() windowing function to generate a unique sequence for each columnc based on the values of columnA and columnB.
The starting point for your query will be:
select [ColumnA],
[ColumnB],
[ColumnC],
'SampleTitle'+
cast(row_number() over(partition by columna, columnb
order by columnc) as varchar(10)) seq
from DataSource;
See Demo. This query will generate the list of new columns names SampleTitle1, etc:
| COLUMNA | COLUMNB | COLUMNC | SEQ |
|---------|---------|---------|--------------|
| 5060 | 1006 | 100118 | SampleTitle1 |
| 5060 | 1006 | 100119 | SampleTitle2 |
| 5060 | 1006 | 100120 | SampleTitle3 |
You can then apply the pivot on columnC with the new column names listed in seq:
select columnA, columnB,
SampleTitle1, SampleTitle2, SampleTitle3
from
(
select [ColumnA],
[ColumnB],
[ColumnC],
'SampleTitle'+
cast(row_number() over(partition by columna, columnb
order by columnc) as varchar(10)) seq
from DataSource
) d
pivot
(
max(columnc)
for seq in (SampleTitle1, SampleTitle2, SampleTitle3)
) piv;
See SQL Fiddle with Demo.
Once you have the correct logic, you can convert the data to dynamic SQL. The key here is generating the list of new column names. I typically use FOR XML PATH for this similar to:
select STUFF((SELECT distinct ',' + QUOTENAME(seq)
from
(
select 'SampleTitle'+
cast(row_number() over(partition by columna, columnb
order by columnc) as varchar(10)) seq
from DataSource
) d
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
See Demo. Once you have the list of column names, then you will generate your sql string to execute, the full code will be:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(seq)
from
(
select 'SampleTitle'+
cast(row_number() over(partition by columna, columnb
order by columnc) as varchar(10)) seq
from DataSource
) d
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT columnA, ColumnB,' + #cols + '
from
(
select [ColumnA],
[ColumnB],
[ColumnC],
''SampleTitle''+
cast(row_number() over(partition by columna, columnb
order by columnc) as varchar(10)) seq
from DataSource
) x
pivot
(
max(columnc)
for seq in (' + #cols + ')
) p '
execute sp_executesql #query;
See SQL Fiddle with Demo. These give a result:
| COLUMNA | COLUMNB | SAMPLETITLE1 | SAMPLETITLE2 | SAMPLETITLE3 |
|---------|---------|--------------|--------------|--------------|
| 5060 | 1006 | 100118 | 100119 | 100120 |
| 5060 | 1007 | 100121 | 100122 | (null) |
| 5060 | 1012 | 100123 | (null) | (null) |