Dynamic Multi-Column SQL - sql

I have two tables with structures like this:
VelocityBase
Aisle | ItemId | ConfigId | InventSizeId | InventColorId | InventLocationId | DataAreaId | VelocityCategory
VelocitySalesCount
ItemId | ConfigId | InventSizeId | InventColorId | InventLocationId | DataAreaId | Sales
Every row in the Base table represents a SKU and the sum of the related SalesCount records' "Sales" fields determines the "Picks". This query works:
SELECT Aisle, COUNT(*) as '# SKUs',
SUM(Sales) as '# Picks',
SUM(CASE WHEN VelocityCategory = 'Hot' THEN 1 ELSE 0 END) as 'Hot SKUs',
SUM(CASE WHEN VelocityCategory = 'Hot' THEN SALES ELSE 0 END) as 'Hot Picks',
SUM(CASE WHEN VelocityCategory = 'Warm' THEN 1 ELSE 0 END) as 'Warm SKUs',
SUM(CASE WHEN VelocityCategory = 'Warm' THEN SALES ELSE 0 END) as 'Warm Picks',
SUM(CASE WHEN VelocityCategory = 'Cold' THEN 1 ELSE 0 END) as 'Cold SKUs',
SUM(CASE WHEN VelocityCategory = 'Cold' THEN SALES ELSE 0 END) as 'Cold Picks'
FROM [dbo].[VelocityBase] Base
LEFT OUTER JOIN [dbo].[VelocitySalesCount] SalesCount
ON Base.ItemId = SalesCount.ItemId
AND Base.ConfigId = SalesCount.ConfigId
AND Base.InventSizeId = SalesCount.InventSizeId
AND Base.InventColorId = SalesCount.InventColorId
AND Base.InventLocationId = SalesCount.InventLocationId
AND SalesCount.DataAreaId = Base.DataAreaId
GROUP BY Aisle
ORDER BY Aisle
However, the columns are hard coded. What I would like is that the "Hot", "Warm", "Cold", etc be generated based on what values are present in the database for this column. That way if a user added a row that had "Lukewarm" as the VelocityCategory, two new columns would appear with that data.
I'm not sure if something like SQL to generate SQL or maybe a PIVOT function would do the trick.
Thanks in advance!
EDIT:
I'm narrowing in. I've got the Sum of the Sales figures using this:
DECLARE #SQLStatement NVARCHAR(4000)
,#PivotValues NVARCHAR(4000);
SET #PivotValues = '';
SELECT #PivotValues = #PivotValues + ',' + QUOTENAME(VelocityCategory)
FROM
(
SELECT DISTINCT VelocityCategory
FROM dbo.VelocityBase
) src;
SET #PivotValues = SUBSTRING(#PivotValues,2,4000);
SELECT #SQLStatement =
'SELECT pvt.*
FROM
(
SELECT Aisle, VelocityCategory, Sales
FROM VelocityBase Base
LEFT OUTER JOIN [dbo].[VelocitySalesCount] SalesCount
ON Base.ItemId = SalesCount.ItemId
AND Base.ConfigId = SalesCount.ConfigId
AND Base.InventSizeId = SalesCount.InventSizeId
AND Base.InventColorId = SalesCount.InventColorId
AND Base.InventLocationId = SalesCount.InventLocationId
AND SalesCount.DataAreaId = Base.DataAreaId
) VelocityBase
PIVOT ( Sum(Sales) FOR VelocityCategory IN ('+#PivotValues+') ) pvt';
EXECUTE sp_executesql #SQLStatement;
Thanks for the link to the previous question which got me this far.

I usually do not use PIVOT, just "usual" dynamic SQL like this:
DECLARE #sSQL NVARCHAR(MAX)= '' ,
#sSQLSum NVARCHAR(MAX)= '' ,
#sSQlBegin NVARCHAR(MAX)= '
SELECT Aisle, COUNT(*) As ''# SKUs'',
SUM(Sales) As ''# Picks'',
' ,
#sSQLEnd NVARCHAR(MAX)= 'FROM [Dbo].[VelocityBase] Base
LEFT OUTER JOIN [Dbo].[VelocitySalesCount] SalesCount
ON Base.ItemId = SalesCount.ItemId
AND Base.ConfigId = SalesCount.ConfigId
AND Base.InventSizeId = SalesCount.InventSizeId
AND Base.InventColorId = SalesCount.InventColorId
AND Base.InventLocationId = SalesCount.InventLocationId
AND SalesCount.DataAreaId = Base.DataAreaId
GROUP BY Aisle
ORDER BY Aisle' ;
WITH c AS ( SELECT DISTINCT
VelocityCategory N
FROM Dbo.VelocityBase
)
SELECT #sSQLSum = #sSQLSum + 'SUM(CASE WHEN c.N=''' + c.N
+ ''' THEN 1 ELSE 0 END ) AS ''' + c.N + ' SKUs'',' + CHAR(13)
+ 'SUM(CASE WHEN c.N=''' + c.N
+ ''' THEN SALES ELSE 0 END ) AS ''' + c.N + ' Sales'',' + CHAR(13)
FROM c
IF(LEN(#sSQLSum))>0
SET #sSQLSum = LEFT(#sSQLSum, ( LEN(#sSQLsum) - 2 ))
SET #sSQL = #sSQlBegin + #sSQLSum + CHAR(13) + #sSQLEnd
EXEC (#sSQL)

Unless you generate the query dynamically, I don't think there's a way to generate what you want.
Your problem could be solved easily if your tables were normalized. For instance, the VelocityBase table should have a VelocityCategoryID column instead of a VelocityCategory column. This new column should be a foreign key to a new table called VelocityCategory (or something like that) then your query for this calculation becomes almost trivial.

Related

Add percentage on every columns on pivot table

******Here is below query for your knowledge how to add percentge columns on every pivot columns let you give the answer 1
I've created a SQL pivot and want to add a further calculated percentage column based on the aggregated figures within and I cannot figure out how to make this happen.****
DELETE TOP(100) PERCENT FROM PlusRS.rpt.RepairLevel
-- STEP NO. 2
INSERT INTO PlusRS.rpt.RepairLevel (ProgramID, PartNo, SerialNo, InvoiceFamily, RepairLevel)
SELECT WOH.ProgramID
, WOH.PartNo
, WOH.SerialNo
, PNA.Value AS InvoiceFamily
, CASE WOH.RepairTypeID WHEN 42 THEN '1'
WHEN 71 THEN '3'
ELSE MAX(PLVL.Value)
END AS RepairLevel
FROM pls.WOHeader WOH
INNER JOIN pls.WOStationHistory WSH ON WOH.ID = WSH.WOHeaderID
INNER JOIN pls.CodeRepairType CRT ON CRT.ID = WOH.RepairTypeID
INNER JOIN pls.WOLine WOL ON WOL.WOHeaderID = WOH.ID AND WOL.StatusID = 14
LEFT JOIN pls.PartNoAttribute PNA ON PNA.AttributeID = 354 AND PNA.ProgramID = WOH.ProgramID AND PNA.PartNo = WOH.PartNo
LEFT JOIN pls.PartNoAttribute PLVL ON PLVL.AttributeID = 149 AND PNA.ProgramID = WOH.ProgramID AND PLVL.PartNo = WOL.ComponentPartNo
WHERE WSH.IsPass = 1 AND WSH.WorkStationID = 2 AND WOH.StatusID != 3
AND woh.ProgramID = '10001' and CONVERT(Date, WSH.lastactivitydate) >= '2022.04.11 ' AND CONVERT(Date, WSH.lastactivitydate) <= '2022.06.15'
GROUP BY WOH.ProgramID
, WOH.PartNo
, WOH.SerialNo
, PNA.Value
, WOH.RepairTypeId
UPDATE PlusRS.rpt.RepairLevel SET BCRatio = CASE WHEN RepairLevel = '0' THEN 9
WHEN RepairLevel = '1' THEN 0
WHEN RepairLevel = '2' THEN 53
WHEN RepairLevel = '2.5' THEN 0
WHEN RepairLevel = '3' THEN 38
WHEN RepairLevel = 'O' THEN 0
END
Declare #sqlquery nvarchar(max),
#column nvarchar(max);
SELECT #column = COALESCE(#column + ',', '') + QUOTENAME(InvoiceFamily)
FROM
(
SELECT Distinct InvoiceFamily
FROM PlusRS.rpt.RepairLevel
) AS PIVOT_COLUMNS
DECLARE #ColumnForSum AS NVARCHAR(MAX)
SELECT #ColumnForSum =REPLACE(#column,',','+')
SELECT #ColumnForSum =REPLACE(#ColumnForSum,'[','ISNULL([')
SELECT #ColumnForSum =REPLACE(#ColumnForSum,']','],0)')
SET #sqlquery =
N'select * into #TempBag from
(
SELECT SerialNo as Qty
, InvoiceFamily
, RepairLevel
, BCRatio
FROM
PlusRS.rpt.RepairLevel
)as t
pivot(
count(qty)
for InvoiceFamily In('+#column+')
)As Pvt_table
ORDER BY RepairLevel
select t.*, SUM('+#ColumnForSum+') Total from #TempBag t
GROUP BY RepairLevel,BCRatio,'+#column+'
Drop Table #TempBag'
EXEC sp_executesql #sqlquery**

Group rows by ID in pivot query

I'm trying to create a pivot query to display survey response data from database.
Here is my query so far:
declare
#columns nvarchar(max) ='',
#sql nvarchar(max) = '',
#responseIDs nvarchar(max) = '';
select
#columns+=QUOTENAME(QuestionLabel) + ','
from
SurveyQuestions
where
SurveyID=1
order by
QuestionSort;
set #columns = LEFT(#columns, LEN(#columns)-1);
set #responseIDs = (select ResponseIDs from SurveyCart where SurveyCartID=7);
set #sql='
select * from (
select
QuestionLabel, ResponseAnswer, ResponseID, AmountPaid =
(
IIF(QuestionTypeID=2 and ResponseAnswer=''Yes'', QuestionValue, 0) +
IIF(QuestionTypeID=6 and CAST(ResponseAnswer as int)>0, QuestionValue*Cast(ResponseAnswer as int), 0)
)
from
SurveyQuestions q
inner join SurveyResponseAnswers ra
on q.QuestionID=ra.QuestionID
where
ResponseID in (' + #responseIDs + ')
) t
pivot(
MAX(ResponseAnswer) for QuestionLabel in (' + #columns + ')
) as pivot_table;';
EXECUTE(#sql)
Here are the results I get
ResponseID AmountPaid Attending? Select Cost with Quantities
19 0.00 NULL Option 3 NULL
20 0.00 NULL Option 1
19 25.00 Yes NULL NULL
20 25.00 Yes NULL NULL
19 30.00 NULL NULL 3
The Results I want:
ResponseID AmountPaid Attending? Select Cost with Quantities
19 55.00 Yes Option 3 3
20 25.00 Yes Option 1
I want there to be only one row for each response ID and where there are blank spots in Attending and Cost With quantities I need the results from the bottom rows and the amounts to be added together based on ResponseID.
select
ResponseID,
sum(case when QuestionLabel = 'AmountPaid' then
iif(QuestionTypeID = 2 and ResponseAnswer = 'Yes', QuestionValue, 0) +
iif(QuestionTypeID = 6 and cast(ResponseAnswer as int) > 0,
QuestionValue * Cast(ResponseAnswer as int), 0)
end) as "AmountPaid",
min(case when QuestionLabel = 'Attending?'
then ResponseAnswer end) as "Attending?",
min(case when QuestionLabel = 'Select'
then ResponseAnswer end) as "Select",
min(case when QuestionLabel = 'Cost with quantities'
then ResponseAnswer end) as "Cost with Quantities"
from
SurveyQuestions q
inner join SurveyResponseAnswers ra
on q.QuestionID = ra.QuestionID
where ResponseID in (
select ResponseIDs from SurveyCart where SurveyCartID = 7
);
I don't understand exactly what a "Question Value" or a "Survey Cart" is but I think this should be close. Unless you expect the question labels to change in the future I don't see why you can't just hard-code them here as you've already done with a number of ID values.

Joining two tables and getting rows as columns

I have two tables. One is a transaction table of user id with following details.
user_id Product_id timestamp transaction_id
123 A_1 ID1
123 A_2 ID1
124 A_1 ID2
125 A_2
Now there is a product_id mapping with the division to which the product belongs
Mapping:
Product_id Division
A_1 Grocery
A_2 Electronics and so on
I need a final table where I have one record for each user id and the corresponding items bought in each division as separate columns. Like
User_ID Grocery Electronics
123 1 1
124 1 0
I did something like this:
select user_id, case (when Division ="Grocery" then count(product_id) else 0) end as Grocery
when Division="Electronics" then count(product_id) else 0) end as Electronics
from
( select user_id, a.product_id, b.division from transact as a
left join
mapping b
on a.product_id=b.product_id
)
group by user_id
Does this sound good?
When you use conditional aggregation, the case is the argument to the aggregation function:
select user_id,
sum(case when m.Division = 'Grocery' then 1 else 0 end) as Grocery,
sum(case when m.Division = 'Electronics' then 1 else 0 end) as Electronics
from transact t left join
mapping m
on t.product_id = m.product_id
group by user_id
SQL Fiddle
In addition:
Your table aliases should be abbreviations for the table. Makes the code easier to understand.
You don't need a subquery.
Use single quotes for string constants. This is the SQL standard.
This is a dynamic version of Gordon Linoff's answer, which uses conditional aggregation. This works for SQL-Server.
DECLARE #sql1 VARCHAR(4000) = ''
DECLARE #sql2 VARCHAR(4000) = ''
DECLARE #sql3 VARCHAR(4000) = ''
SELECT #sql1 =
'SELECT
t.user_id
'
SELECT #sql2 = #sql2 +
' , SUM(CASE WHEN m.division = ''' + division + ''' THEN 1 ELSE 0 END) AS [' + division + ']' + CHAR(10)
FROM(
SELECT DISTINCT division FROM Mapping
)t
ORDER BY division
SELECT #sql3 =
'FROM Transact t
LEFT JOIN Mapping m
ON m.product_id = t.product_id
GROUP BY t.user_id'
PRINT (#sql1 + #sql2 + #sql3)
EXEC (#sql1 + #sql2 + #sql3)
SQL Fiddle
as per my understanding it can be acheived using PIVOT also
declare #transact table (user_id int,product_id varchar(5),transaction_id varchar(5))
insert into #transact (user_id,product_id,transaction_id)values (123,'A_1','ID1'),(123,'A_2','ID1'),(124,'A_1','ID2'),(125,'A_2',NULL)
declare #mapping table (product_id varchar(5),division varchar(20))
insert into #mapping (product_id,division)values ('A_1','Grocery'),('A_2','Electronics')
select * from (
select t.user_id,tt.division as Division,tt.product_id As product_id from #transact t
left join #mapping tt
on t.product_id = tt.product_id)T
PIVOT(COUNT(product_id)FOR division IN ([Grocery],[Electronics]))P

Pivot a fixed multiple column table in sql server

I have a table which I need to pivot for reporting services:
DateCreated Rands Units Average Price Success % Unique Users
-------------------------------------------------------------------------
2013-08-26 0 0 0 0 0
2013-08-27 0 0 0 0 0
2013-08-28 10 2 5 100 1
2013-08-29 12 1 12 100 1
2013-08-30 71 9 8 100 1
2013-08-31 0 0 0 0 0
2013-09-01 0 0 0 0 0
In other words I need to have Rands, Units, Average Price etc at rows and the dates as columns.
I have read various examples but I just can't seem to get it right.
Any help would be much appreciated!
This one will do what you want, but you have to specify all the dates
select
c.Name,
max(case when t.DateCreated = '2013-08-26' then c.Value end) as [2013-08-26],
max(case when t.DateCreated = '2013-08-27' then c.Value end) as [2013-08-27],
max(case when t.DateCreated = '2013-08-28' then c.Value end) as [2013-08-28],
max(case when t.DateCreated = '2013-08-29' then c.Value end) as [2013-08-29],
max(case when t.DateCreated = '2013-08-30' then c.Value end) as [2013-08-30],
max(case when t.DateCreated = '2013-08-31' then c.Value end) as [2013-08-31],
max(case when t.DateCreated = '2013-09-01' then c.Value end) as [2013-09-01]
from test as t
outer apply (
select 'Rands', Rands union all
select 'Units', Units union all
select 'Average Price', [Average Price] union all
select 'Success %', [Success %] union all
select 'Unique Users', [Unique Users]
) as C(Name, Value)
group by c.Name
You can create a dynamic SQL for this, something like this:
declare #stmt nvarchar(max)
select #stmt = isnull(#stmt + ',', '') +
'max(case when t.DateCreated = ''' + convert(nvarchar(8), t.DateCreated, 112) + ''' then c.Value end) as [' + convert(nvarchar(8), t.DateCreated, 112) + ']'
from test as t
select #stmt = '
select
c.Name, ' + #stmt + ' from test as t
outer apply (
select ''Rands'', Rands union all
select ''Units'', Units union all
select ''Average Price'', [Average Price] union all
select ''Success %'', [Success %] union all
select ''Unique Users'', [Unique Users]
) as C(Name, Value)
group by c.Name'
exec sp_executesql #stmt = #stmt
I solved this in the end using dynamic sql, very similar to the marked answer. I wasn't able to find a way of doing this without dynamic sql. The dates had to be in order and the last 7 days, they also had to use the day of the week names (which I didn't specify in the question).
The biggest change I needed to make was changing the table variable into a temporary table.
This is because dynamic sql statements execute in a different context and don't know about any variables you have created.
In the end I was completely off track trying to use PIVOT and APPLY should be used in situations where there are more than one "type" of value.
I have included my solution below since it could help someone who has a similar problem:
CREATE TABLE #SummaryTable
(
[DateCreated] DATE UNIQUE,
[Rands] DECIMAL,
[Units] INT,
[Average Price] DECIMAL,
[Success %] INT,
[Unique Users] INT
);
--Code to fill table
declare #stmt nvarchar(max)
select #stmt = isnull(#stmt + ',', '') +
'max(case when t.DateCreated = ''' + convert(nvarchar(16), t.DateCreated, 126)
+ ''' then c.Value end) as [' + left(datename(dw, t.DateCreated),3) + ']'
from #SummaryTable as t
select #stmt = '
select
c.Name, ' + #stmt + ' from #SummaryTable as t
outer apply (
select ''Rands'', Rands union all
select ''Units'', Units union all
select ''Average Price'', [Average Price] union all
select ''Success'', [Success %] union all
select ''Unique Users'', [Unique Users]
) as C(Name, Value)
group by c.Name'
exec(#stmt)

How can I query row data as columns?

I'm sure I'm missing something here.
I have a dataset like this:
FK RowNumber Value Type Status
1 1 aaaaa A New
1 2 bbbbb B Good
1 3 ccccc A Bad
1 4 ddddd C Good
1 5 eeeee B Good
2 1 fffff C Bad
2 2 ggggg A New
2 3 hhhhh C Bad
3 1 iiiii A Good
3 2 jjjjj A Good
I'd like to query the top 3 results and Pivot them as columns, so the end result set looks like this:
FK Value1 Type1 Status1 Value2 Type2 Status2 Value3 Type3 Status3
1 aaaaa A New bbbbb B Good ccccc A Bad
2 fffff C Bad ggggg A New hhhhh C Bad
3 iiiii A Good jjjjj A Good
How can I accomplish this in SQL Server 2005?
I have been attempting this using PIVOT, but I am still very unfamiliar with that keyword and cannot get it to work the way I want.
SELECT * --Id, [1], [2], [3]
FROM
(
SELECT Id, Value, Type, Status
, ROW_NUMBER() OVER (PARTITION BY Id ORDER Status, Type) as [RowNumber]
FROM MyTable
) as T
PIVOT
(
-- I know this section doesn't work. I'm still trying to figure out PIVOT
MAX(T.Value) FOR RowNumber IN ([1], [2], [3]),
MAX(T.Type) FOR RowNumber IN ([1], [2], [3]),
MAX(T.Status) FOR RowNumber IN ([1], [2], [3])
) AS PivotTable;
My actual data set is a bit more complex than this, and I need the top 10 records, not the top 3, so I don't want to simply do CASE WHEN RowNumber = X THEN... for each one.
Update
I tested all the answers below, and found most of them seem about the same with no apparent performance difference in smaller data sets (around 3k records), however there was a slight difference when running the queries against larger data sets.
Here are the results of my tests using 80,000 records and querying for 5 columns in the top 10 rows, so my end result set was 50 columns + the Id column. I'd suggest you test them on your own to decide which one works best for you and your environment.
bluefoot's answer of unpivoting and re-pivoting the data averaged the fastest at about 12 seconds. I also liked this answer because I found it easiest to read and maintain.
Aaron's answer and koderoid's answer both suggest using a MAX(CASE WHEN RowNumber = X THEN ...), and was close behind averaging at around 13 seconds.
Rodney's answer of using multiple PIVOT statements averaged around 16 seconds, although it might be faster with fewer PIVOT statements (my tests had 5).
And the first half of Aaron's answer that suggested using a CTE and OUTER APPLY was the slowest. I don't know how long it would take to run because I cancelled it after 2 minutes, and that was with around 3k records, 3 rows, and 3 columns instead of 80k records, 10 rows, and 5 columns.
You can do an UNPIVOT and then a PIVOT of the data. this can be done either statically or dynamically:
Static Version:
select *
from
(
select fk, col + cast(rownumber as varchar(1)) new_col,
val
from
(
select fk, rownumber, value, cast(type as varchar(10)) type,
status
from yourtable
) x
unpivot
(
val
for col in (value, type, status)
) u
) x1
pivot
(
max(val)
for new_col in
([value1], [type1], [status1],
[value2], [type2], [status2],
[value3], [type3])
) p
see SQL Fiddle with demo
Dynamic Version, this will get the list of columns to unpivot and then to pivot at run-time:
DECLARE #colsUnpivot AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX),
#colsPivot as NVARCHAR(MAX)
select #colsUnpivot = stuff((select ','+quotename(C.name)
from sys.columns as C
where C.object_id = object_id('yourtable') and
C.name not in ('fk', 'rownumber')
for xml path('')), 1, 1, '')
select #colsPivot = STUFF((SELECT ','
+ quotename(c.name
+ cast(t.rownumber as varchar(10)))
from yourtable t
cross apply
sys.columns as C
where C.object_id = object_id('yourtable') and
C.name not in ('fk', 'rownumber')
group by c.name, t.rownumber
order by t.rownumber
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query
= 'select *
from
(
select fk, col + cast(rownumber as varchar(10)) new_col,
val
from
(
select fk, rownumber, value, cast(type as varchar(10)) type,
status
from yourtable
) x
unpivot
(
val
for col in ('+ #colsunpivot +')
) u
) x1
pivot
(
max(val)
for new_col in
('+ #colspivot +')
) p'
exec(#query)
see SQL Fiddle with Demo
Both will generate the same results, however the dynamic is great if you do not know the number of columns ahead of time.
The Dynamic version is working under the assumption that the rownumber is already a part of the dataset.
You can try to do the pivot in three separate pivot statements. Please give this a try:
SELECT Id
,MAX(S1) [Status 1]
,MAX(T1) [Type1]
,MAX(V1) [Value1]
--, Add other columns
FROM
(
SELECT Id, Value , Type, Status
, 'S' + CAST(ROW_NUMBER() OVER (PARTITION BY Id ORDER BY Status, Type) AS VARCHAR(10)) [Status_RowNumber]
, 'T' + CAST(ROW_NUMBER() OVER (PARTITION BY Id ORDER BY Status, Type) AS VARCHAR(10)) [Type_RowNumber]
, 'V' + CAST(ROW_NUMBER() OVER (PARTITION BY Id ORDER BY Status, Type) AS VARCHAR(10)) [Value_RowNumber]
FROM MyTable
) as T
PIVOT
(
MAX(Status) FOR Status_RowNumber IN ([S1], [S2], [S3],[S4],[S5],[S6],[S7],[S8],[S9],[S10])
)AS StatusPivot
PIVOT(
MAX(Type) FOR Type_RowNumber IN ([T1], [T2], [T3],[T4],[T5],[T6],[T7],[T8],[T9],[T10])
)AS Type_Pivot
PIVOT(
MAX(Value) FOR Value_RowNumber IN ([V1], [V2], [V3],[V4],[V5],[V6],[V7],[V8],[V9],[V10])
)AS Value_Pivot
GROUP BY Id
I don't know the full scope of the criteria for selecting the top ten records, but this produces and output that may get you closer to your answer.
SQL Fiddle Example
Rodney's muli-pivot is clever, that's for sure. Here are two other alternatives that are of course less appealing when you get into the 10X vs. 3X area.
;WITH a AS
(
SELECT Id, Value, Type, Status,
n = ROW_NUMBER() OVER (PARTITION BY Id ORDER BY [Status], [Type])
FROM dbo.MyTable
)
SELECT a.Id,
Value1 = a.Value, Type1 = a.[Type], Status1 = a.[Status],
Value2 = b.Value, Type2 = b.[Type], Status2 = b.[Status],
Value3 = c.Value, Type3 = c.[Type], Status3 = c.[Status]
FROM a
OUTER APPLY (SELECT * FROM a AS T2 WHERE n = a.n + 1 AND id = a.id) AS b
OUTER APPLY (SELECT * FROM a AS T2 WHERE n = b.n + 1 AND id = b.id) AS c
WHERE a.n = 1
ORDER BY a.Id;
-- or --
;WITH a AS
(
SELECT Id, Value, [Type], [Status],
n = ROW_NUMBER() OVER (PARTITION BY Id ORDER BY [Status], [Type])
FROM dbo.MyTable
)
SELECT Id,
Value1 = MAX(CASE WHEN n = 1 THEN Value END),
Type1 = MAX(CASE WHEN n = 1 THEN [Type] END),
Status1 = MAX(CASE WHEN n = 1 THEN [Status] END),
Value2 = MAX(CASE WHEN n = 2 THEN Value END),
Type2 = MAX(CASE WHEN n = 2 THEN [Type] END),
Status2 = MAX(CASE WHEN n = 2 THEN [Status] END),
Value3 = MAX(CASE WHEN n = 3 THEN Value END),
Type3 = MAX(CASE WHEN n = 3 THEN [Type] END),
Status3 = MAX(CASE WHEN n = 3 THEN [Status] END)
FROM a
GROUP BY Id
ORDER BY a.Id;
This might work for you, though it's not elegant.
select aa.FK_Id
, isnull(max(aa.Value1), '') as Value1
, isnull(max(aa.Type1), '') as Type1
, isnull(max(aa.Status1), '') as Status1
, isnull(max(aa.Value2), '') as Value2
, isnull(max(aa.Type2), '') as Type2
, isnull(max(aa.Status2), '') as Status2
, isnull(max(aa.Value3), '') as Value3
, isnull(max(aa.Type3), '') as Type3
, isnull(max(aa.Status3), '') as Status3
from
(
select FK_Id
, case when RowNumber = 1 then Value else null end as Value1
, case when RowNumber = 1 then [Type] else null end as Type1
, case when RowNumber = 1 then [Status] else null end as Status1
, case when RowNumber = 2 then Value else null end as Value2
, case when RowNumber = 2 then [Type] else null end as Type2
, case when RowNumber = 2 then [Status] else null end as Status2
, case when RowNumber = 3 then Value else null end as Value3
, case when RowNumber = 3 then [Type] else null end as Type3
, case when RowNumber = 3 then [Status] else null end as Status3
from Table1
) aa
group by aa.FK_Id
try something like this:
declare #rowCount int
set #rowCount = 10
declare #isNullClause varchar(4024)
set #isnullClause = ''
declare #caseClause varchar(4024)
set #caseClause = ''
declare #i int
set #i = 1
while(#i <= #rowCount) begin
set #isnullClause = #isNullClause +
' , max(aa.Value' + CAST(#i as varchar(3)) + ') as Value' + CAST(#i as varchar(3)) +
' , max(aa.Type' + CAST(#i as varchar(3)) + ') as Type' + CAST(#i as varchar(3)) +
' , max(aa.Status' + CAST(#i as varchar(3)) + ') as Status' + CAST(#i as varchar(3)) + ' ';
set #caseClause = #caseClause +
' , case when RowNumber = ' + CAST(#i as varchar(3)) + ' then Value else null end as Value' + CAST(#i as varchar(3)) +
' , case when RowNumber = ' + CAST(#i as varchar(3)) + ' then Type else null end as Type' + CAST(#i as varchar(3)) +
' , case when RowNumber = ' + CAST(#i as varchar(3)) + ' then Status else null end as Status' + CAST(#i as varchar(3)) + ' '
set #i = #i + 1;
end
declare #sql nvarchar(4000)
set #sql = 'select aa.FK_Id ' + #isnullClause + ' from ( select FK_Id '
+ #caseClause + ' from Table1) aa group by aa.FK_Id '
exec SP_EXECUTESQL #sql