Convert single column to single row - sql-server

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

Related

Convert row to column and apply sorting using sql or c#

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

MS Sql Server Pivot two columns

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

simple SQL Pivot Query

I apologize for my ignorance. I just am not familiar with pivot queries AT ALL and all the examples I find seem about as clear as mud. I have table that returns GroupName and ID Numbers.
For Example:
SELECT GroupName, IDnumber FROM do.Table_1
Returns
GroupName IDnumber
1 8395
1 A660
1 8396
1 A661
2 8398
2 A662
2 8399
What I want is something more like this:
GroupName ID1 ID2 ID3 ID4
1 8395 A660 8396 A661
2 8398 A662 8399 NULL
How can I do this? Pivot query? Some other method?
I am open to suggestion and appreciate any help you could provide.
Yes, you can do it using PIVOT but not in this shape, you have firstly to generate a row number to use it to format the data in the way you want. Something like this:
WITH Ranked
AS
(
SELECT GroupName, IDnumber,
ROW_NUMBER() OVER(PARTITION BY GroupName ORDER BY GroupName) AS RN
FROM Table1
)
SELECT GroupName,
[1] AS ID1, [2] AS ID2, [3] AS ID3, [4] AS ID4
FROM Ranked AS r
PIVOT
(
MAX(IDnumber)
FOR RN IN([1], [2], [3], [4])
) AS p;
SQL Fiddle Demo
This will give you:
| GROUPNAME | ID1 | ID2 | ID3 | ID4 |
|-----------|------|------|------|--------|
| 1 | 8395 | A660 | 8396 | A661 |
| 2 | 8398 | A662 | 8399 | (null) |
If you want to do it dynamically and not to write the row number by hand in the pivot table operator, you have to do it using dynamic SQL, something like:
DECLARE #cols AS NVARCHAR(MAX);
DECLARE #colnames AS NVARCHAR(MAX);
DECLARE #query AS NVARCHAR(MAX);
SELECT #cols = STUFF((SELECT distinct ',' +
QUOTENAME(RN)
FROM
(
SELECT ROW_NUMBER() OVER(PARTITION BY GroupName ORDER BY GroupName) AS RN
FROM Table1
) AS t
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '');
SELECT #colnames = STUFF((SELECT distinct ',' +
QUOTENAME(RN) + 'AS' +
QUOTENAME('ID' + CAST(RN AS NVARCHAR(5)))
FROM
(
SELECT ROW_NUMBER() OVER(PARTITION BY GroupName ORDER BY GroupName) AS RN
FROM Table1
) AS t
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '');
SELECT #query = 'WITH Ranked
AS
(
SELECT GroupName, IDnumber,
ROW_NUMBER() OVER(PARTITION BY GroupName ORDER BY GroupName) AS RN
FROM Table1
)
SELECT GroupName, ' + #colnames +
' FROM Ranked AS r
PIVOT
(
MAX(IDnumber)
FOR RN IN(' + #cols + ')' +
') p';
execute(#query);
SQL Fiddle Demo
This should give you the same result:
| GROUPNAME | ID1 | ID2 | ID3 | ID4 |
|-----------|------|------|------|--------|
| 1 | 8395 | A660 | 8396 | A661 |
| 2 | 8398 | A662 | 8399 | (null) |
You may need to use dynamic pivoting since the Id will be dynamic. Here is your sample table
SELECT * INTO #TEMP
FROM
(
SELECT 1 GroupName, '8395' IDnumber
UNION ALL
SELECT 1, 'A660'
UNION ALL
SELECT 1, '8396'
UNION ALL
SELECT 1, 'A661'
UNION ALL
SELECT 2, '8398'
UNION ALL
SELECT 2, 'A662'
UNION ALL
SELECT 2, '8399'
)TAB
Select row number over each Groupname and insert into a temporary table so that it can be used for both selecting the columns for pivoting and inside the pivot
SELECT *,
'ID' + CAST(ROW_NUMBER() OVER(PARTITION BY GroupName ORDER BY GROUPNAME) AS VARCHAR(10)) IDS
INTO #NEWTABLE
FROM #TEMP
Select columns for pivot
DECLARE #cols NVARCHAR (MAX)
SELECT #cols = COALESCE (#cols + ',[' + IDS + ']',
'[' + IDS + ']')
FROM (SELECT DISTINCT IDS FROM #NEWTABLE) PV
ORDER BY IDS
Now pivot dynamically
DECLARE #query NVARCHAR(MAX)
SET #query = '
SELECT * FROM
(
SELECT * FROM #NEWTABLE
) x
PIVOT
(
MAX(IDnumber)
FOR IDS IN (' + #cols + ')
) p
'
EXEC SP_EXECUTESQL #query
Click here to view the result (incase an error is occured on loading page press RUNSQL, it works)
RESULT

PIVOT with multiple columns

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

How to convert row values to columns with dynamic columns count?

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

Resources