Retrieving multiple rows dynamically using Pivot in SQL - sql

I wish to add multiple rows for a particular pivot table.
Table name : MasterTable
FieldName | FieldValue
------------------------
Field1 | F1value1
Field2 | F2value1
Field3 | F3value1
Field1 | F1value2
Field2 | F2value2
Field3 | F3value2
Expected result:
Field1 | Field2 | Field3
---------------------------------
F1value1 | F2value1 | F3value1
F1value2 | F2value2 | F3value2
I tried this code but result me with only one row.
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(FieldName) from MasterTable FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)') ,1,1,'')
set #query = 'SELECT ' + #cols + ' from (select FieldName, FieldValue from MasterTable ) x
pivot
(
max(FieldValue)
for FieldName in (' + #cols + ')
) p '
execute(#query)
Output:
Field1 | Field2 | Field3
---------------------------------
F1value1 | F2value1 | F3value1
Could someone please help me to have multiple rows using pivote table.

For the subquery, use:
(select FieldName, FieldValue,
row_number() over (partition by FieldName order by FieldName) as seqnum
from MasterTable
)
pivot will take this into account in the pivoting.
Note: This will guarantee the number of rows, but the ordering of each column is arbitrary. You may want to replace the order by with a reasonable column for ordering.

Related

Column names in result and rotate part of table

The following a table structure:
'----ID-----'----NAME----'----FIELD1----'----FIELD2----'
' 1 ' val ' 123 ' 321 '
' 2 ' val2 ' 234 ' 212 '
Need to get the following result:
'----ID-----'----NAME----'----FIELDS----'----VALUES----'
' 1 ' val ' FIELD1 ' 123 '
' 1 ' val ' FIELD2 ' 321 '
' 2 ' val2 ' FIELD1 ' 234 '
' 2 ' val2 ' FIELD2 ' 212 '
How write this query? I can get column names from INFORMATION_SCHEMA.COLUMNS. But how to join table with INFORMATION_SCHEMA.COLUMNS? Also how can rotating a part of table?
As living example. Following is table:
On screenshot only several fields but in table there are a lot of fields. I wrote the following query:
Select p.GUID, p.myvalues, p.Fields
from myTable gz
unpivot( [myvalues] for Fields in ([area], [davlplastmax])) p
But this query doesn't return null values.
Also I want get columns from INFORMATION_SCHEMA.COLUMNS and past them in ([area], [davlplastmax]).
For example:
unpivot( [values] for Fields in (
SELECT [MyDb].INFORMATION_SCHEMA.COLUMNS.COLUMN_NAME
FROM [MyDb].INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = N'MyTable'
)
Unpivot?
select u.id, u.name, u.fields, u.values
from MyTable t
unpivot
(
values
for fields in (Field1, Field2)
) u;
You can use unpivot as below:
Select * from #data
unpivot( [values] for Fields in ([Field1],[Field2])) p
Output as below:
+----+------+--------+--------+
| Id | Name | values | Fields |
+----+------+--------+--------+
| 1 | val | 123 | Field1 |
| 1 | val | 321 | Field2 |
| 2 | val2 | 234 | Field1 |
| 2 | val2 | 212 | Field2 |
+----+------+--------+--------+
You can use dynamic query as below for getting columns from Information_Schemas
Declare #cols1 varchar(max)
Declare #query nvarchar(max)
Select #cols1 = stuff((select ','+QuoteName(Column_Name) from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = 'TestData'
and COLUMN_NAME not in ('Id','Name') for xml path('')),1,1,'')
Select #query = ' Select * from
(Select * from #data )a
unpivot( [values] for Fields in (' + #cols1+ ')) p '
Exec sp_executeSql #query

Concatenate columns in Pivot View

DECLARE #DynamicPivotQuery AS NVARCHAR(MAX)
DECLARE #ColumnName AS NVARCHAR(MAX)
--Get distinct values of the PIVOT Column
SELECT #ColumnName= ISNULL(#ColumnName + ',','')
+ QUOTENAME(ACT_DESC)
FROM (SELECT DISTINCT ACT_DESC FROM tpc) AS desc
--Prepare the PIVOT query using the dynamic
SET #DynamicPivotQuery =
N'SELECT MDCODE, ' + #ColumnName + '
FROM tpc
PIVOT(MAX(ACTUAL_DATE)
FOR tpcIN (' + #ColumnName + ')) AS PVTTable'
--Execute the Dynamic Pivot Query
EXEC sp_executesql #DynamicPivotQuery
This query displays:
MDCODE | sample1 | sample2
--------------------------
123 | 1/2014 |
123 | | 2/2014
123 | | 3/2014
What I want is this:
MDCODE | sample1 | sample2
--------------------------
123 | 1/2014 | 2/2014,3/2014
Does anyone have an idea how to concat the column data? Any tips?
This is the table where i get the data:
mdcode | act_desc | actual_date
--------------------------
1234 | sample1 | 1/2014
1234 | sample2 | 2/2014
1234 | sample2 | 3/2014
the actual_date is datetime
In SQL Server, the type of comma separated output is achieved with "for xml path", but is also usually associated with stuff()
Optionally via "cross apply" although it doesn't have to be done that way.
I would approach it like this. Don't use pivot now, just reduce MDCODE & ACT_DESC by distinct or group by, then using cross apply concatenate the string of "dates". If they really are dates then you will need to convert them to varchar e.g. convert(varchar,actual_date,112)
| MDCODE | ACT_DESC | ACTUAL_DATES |
|--------|----------|---------------|
| 1234 | sample1 | 1/2014 |
| 1234 | sample2 | 2/2014,3/2014 |
produced by:
SELECT
d.mdcode
, d.act_desc
, ca1.actual_dates
FROM (
SELECT DISTINCT
[mdcode]
, [act_desc]
FROM Table1
) d
CROSS APPLY (
SELECT
STUFF((
SELECT
',' + a.actual_date
FROM table1 a
WHERE a.mdcode = d.mdcode
AND a.act_desc = d.act_desc --<< change here
ORDER BY a.actual_date
FOR xml PATH ('')
)
, 1, 1, '')
) AS ca1 (actual_dates)
;
By the way STUFF() is used to remove the first comma from the concatenation, that is its only role. CROSS APPLY is arguably better than using a correlated subquery in the select clause, because it is performed before that clause as part of the from clause. As I said earlier its optional but I prefer it.
see: http://sqlfiddle.com/#!3/a24ba/4
& also see these samples
I would skip the PIVOT altogether and just use FOR XML_PATH, like this:
DECLARE #DynamicPivotQuery AS NVARCHAR(MAX)
--Get distinct values of the PIVOT Column
SELECT #DynamicPivotQuery= ISNULL(#DynamicPivotQuery + ',','select mdcode,')
+ 'stuff((select '',''+actual_date from tpc where mdcode=t.mdcode and act_desc = ''' + ACT_DESC + ''' for xml path(''''),type).value(''.'',''varchar(max)''),1,1,'''') '
+ QUOTENAME(ACT_DESC)
FROM (SELECT DISTINCT ACT_DESC FROM tpc) AS des
select #DynamicPivotQuery = #DynamicPivotQuery + 'from tpc t group by mdcode'
EXEC sp_executesql #DynamicPivotQuery
The dynamic query generates a query like this:
select mdcode,
stuff(
(
select ','+actual_date
from tpc where mdcode=t.mdcode and act_desc = 'sample1'
for xml path(''),type
).value('.','varchar(max)')
,1,1,'') sample1,
stuff(
(
select ','+actual_date
from tpc where mdcode=t.mdcode and act_desc = 'sample2'
for xml path(''),type
).value('.','varchar(max)')
,1,1,'') sample2
from tpc t
group by mdcode;
The SQL Fiddle demonstrates the static query and the dynamic one

SQL 2008 - Combine multiple row values into one row with 'n' columns [duplicate]

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

SQL Double Dynamic Pivot

I am working on a double dynamic pivot based on 2 columns (HardwarePhase & HardwarePhase_Result).
Using the first result set in the image below, is the raw data that I have. Each set of 5 items (highlighted in images) are grouped based on HardwareTestCaseID.
The second result set in the image, is the current results that I'm getting from how I've constructed this query. Ideally, the result of the second column would be the same results, but instead it would be grouping the results.
The grouping is supposed to be based on the HardwareTestCaseID, but however, this is not happening.
The results I actually want are shown here. (There should be multiple rows, but this is just how it should be grouped per 5 entries).
This is the query I am currently using:
NOTE: The #col variables are built up based on the list of HardwarePhases (P0, M1, M2, M3).
select #query = 'SELECT ' + #colsNames + ',' + #colsResultNames + ', HardwareTestCaseID FROM
(
SELECT HardwarePhase_Result, HardwarePhase, ResultValue, HardwareTestCaseID, HardwareStatus
FROM #temp4
) as x
pivot
(
MAX(ResultValue)
FOR HardwarePhase_Result IN (' + #colsResult + ')
) as p
pivot
(
MAX(HardwareStatus)
FOR HardwarePhase IN (' + #cols + ')
) as p2 ';
using this table:
create table #temp4
(
HardwarePhase nvarchar(max),
HardwarePhase_Result nvarchar(max),
ResultValue bigint,
HardwareTestCaseID bigint,
HardwareStatus nvarchar(max),
Block nvarchar(max)
);
I personally would do it slightly different since you want to PIVOT on two columns. I would look at unpivoting the data in the multiple columns first, then apply the PIVOT function. I also would suggest that you start with writing a hard-coded version of the query first then convert it to dynamic SQL - this allows you to get the correct logic.
To unpivot the data, I would use CROSS APPLY so you can convert the pairs of columns into rows at the same time, the syntax would be similar to the following:
select col, value, HardwareTestCaseID
from temp4
cross apply
(
select HardwarePhase, HardwareStatus union all
select HardwarePhase_Result, cast(ResultValue as varchar(10))
) c (col, value)
See SQL Fiddle with Demo. Your data is then in the format:
| COL | VALUE | HARDWARETESTCASEID |
|-----------|-------------|--------------------|
| P0 | Not Started | 365 |
| P0_Result | 1 | 365 |
| M1 | Pass | 365 |
| M1_Result | 1 | 365 |
| M4 | Pass | 365 |
| M4_Result | 1 | 365 |
| M2 | Blocked | 365 |
| M2_Result | 1 | 365 |
Then you just apply the pivot function to the data:
select M1, M2, M3, M4, P0,
M1_Result, M2_Result, M3_Result,
M4_Result, P0_Result,
HardwareTestCaseID
from
(
select col, value, HardwareTestCaseID
from temp4
cross apply
(
select HardwarePhase, HardwareStatus union all
select HardwarePhase_Result, cast(ResultValue as varchar(10))
) c (col, value)
) d
pivot
(
max(value)
for col IN (M1, M2, M3, M4, P0,
M1_Result, M2_Result, M3_Result,
M4_Result, P0_Result)
) piv;
See SQL Fiddle with Demo.
Once you have the logic down, then convert it to dynamic SQL:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(col)
from temp4
cross apply
(
select HardwarePhase, 1 union all
select HardwarePhase_Result, 2
) c (col, so)
group by col, so
order by so, col
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT ' + #cols + ' , HardwareTestCaseID
from
(
select col, value, HardwareTestCaseID
from temp4
cross apply
(
select HardwarePhase, HardwareStatus union all
select HardwarePhase_Result, cast(ResultValue as varchar(10))
) c (col, value)
) x
pivot
(
max(value)
for col in (' + #cols + ')
) p '
execute sp_executesql #query;
See SQL Fiddle with Demo. This process gets a result:
| M1 | M2 | M3 | M4 | P0 | M1_RESULT | M2_RESULT | M3_RESULT | M4_RESULT | P0_RESULT | HARDWARETESTCASEID |
|---------|---------|---------|---------|-------------|-----------|-----------|-----------|-----------|-----------|--------------------|
| Pass | Blocked | Pass | Pass | Not Started | 1 | 1 | 1 | 1 | 1 | 365 |
| Blocked | Blocked | Blocked | Blocked | Pass | 1 | (null) | 1 | 1 | 1 | 366 |
--This is Just AWESOME. Simplified it for just one table as it's a much more common case (and could not find anything even close to this elegant after trying for hours)
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(your_key_column)
from YOUR_ORIGINAL_KEY_AND_VALUE_TABLE
group by your_key_column
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT your_row_heading_columns,' + #cols + '
INTO YOUR_NEW_PIVOTED_TABLE
from
(
select your_row_heading_columns,your_key_column,your_value_column
from YOUR_ORIGINAL_KEY_AND_VALUE_TABLE
) x
pivot
(
max(your_value_column)
for your_key_column in (' + #cols + ')
) p '
execute sp_executesql #query;

Dynamic Pivot Columns in SQL Server

I have a table named Property with following columns in SQL Server:
Id Name
There are some property in this table that certain object in other table should give value to it.
Id Object_Id Property_Id Value
I want to make a pivot table like below that has one column for each property I've declared in 1'st table:
Object_Id Property1 Property2 Property3 ...
I want to know how can I get columns of pivot dynamically from table. Because the rows in 1'st table will change.
Something like this:
DECLARE #cols AS NVARCHAR(MAX);
DECLARE #query AS NVARCHAR(MAX);
select #cols = STUFF((SELECT distinct ',' +
QUOTENAME(Name)
FROM property
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '');
SELECT #query =
'SELECT *
FROM
(
SELECT
o.object_id,
p.Name,
o.value
FROM propertyObjects AS o
INNER JOIN property AS p ON o.Property_Id = p.Id
) AS t
PIVOT
(
MAX(value)
FOR Name IN( ' + #cols + ' )' +
' ) AS p ; ';
execute(#query);
SQL Fiddle Demo.
This will give you something like this:
| OBJECT_ID | PROPERTY1 | PROPERTY2 | PROPERTY3 | PROPERTY4 |
-------------------------------------------------------------
| 1 | ee | fd | fdf | ewre |
| 2 | dsd | sss | dfew | dff |