SSRS - Struggling with Grouping - sql-server

I've got a table that looks like this...
I need to create a report like this
but cant figure out how to create the columns in SSRS... any help would be greatly appreciated.

With a slight change to your data you can easily do this in SSRS.
Assuming your data is currently in a table that looks like example then you can use something like this in your dataset query..
SELECT Account, SalesPerson, Cost, Product, 'Expected' as Measure, ExpectedCloseDate as ActivityDate
FROM myTable WHERE ExpectedCloseDate IS NOT NULL
UNION ALL
SELECT Account, SalesPerson, Cost, Product, 'Actual' as Measure, ActualCloseDate as ActivityDate
FROM myTable WHERE ActualCloseDate IS NOT NULL
Then in your report, add a Matrix
Add a RowGroup that Groups by Account, SalesPerson and Product
Add a Column Group that groups by Measure
Add a Parent Column Group to the Measure column group that groups by
Year and Month
Once this is done, drop the Cost field onto the matrix as the data field and that should be that other than a bit of formatting on the date.
If you are still struggling, let me know and I will post a full answer, I'm not at my PC at the moment (and it's midnight!)

Not an expert on SSRS reports, but the actual data pivoting can be done in SQL. The solution below uses Common Table Expression's (CTE's) and the pivot keyword. The solution is not the most dynamic one, but it covers your example data.
-- create sample data
declare #data table
(
Account nvarchar(15),
SalesPerson nvarchar(15),
Cost int,
Product nvarchar(15),
ExpectedCloseDate date,
ActualCloseDate date
);
insert into #data (Account, SalesPerson, Cost, Product, ExpectedCloseDate, ActualCloseDate) values
('ABC House', 'John Doe', 120, 'Blocks', '2020-02-03', '2020-02-03'),
('Pizza House', 'Jose Guava', 10, 'Boxes', '2020-04-04', '2020-04-24'),
('The Hanger', 'John Doe', 10, 'Wings', '2020-02-03', '2020-06-24'),
('The Store', 'John Doe', 10, 'Catnip', '2020-02-03', null ),
('The Store', 'Jose Guava', 15, 'Boxes', '2020-03-04', '2020-05-24'),
('WangTai', 'Pete Ringer', 20, 'Lettuce', '2020-04-01', '2020-04-04'),
('WangTai', 'Pete Ringer', 22, 'Paper Plates', '2020-05-01', null ),
('Woolies', 'Pete Ringer', 20, 'Catnip', '2020-03-04', '2020-03-04');
Solution overview:
Convert the dates to months.
Pivot the expected costs.
Pivot the actual costs.
Join everything together.
Solution:
-- solution
with dataMonths as
(
select d.Account,
d.SalesPerson,
d.Product,
d.Cost,
month(d.ExpectedCloseDate) as 'ExpectedMonth',
month(d.ActualCloseDate) as 'ActualMonth'
from #data d
),
dataExpected as
(
select p.Account,
p.SalesPerson,
p.Product,
[2] as 'ExpFeb20',
[3] as 'ExpMar20',
[4] as 'ExpApr20',
[5] as 'ExpMay20',
[6] as 'ExpJun20',
[7] as 'ExpJul20'
from dataMonths dm
pivot ( sum(dm.Cost) for ExpectedMonth in ([2], [3], [4], [5], [6], [7]) ) p
),
dataActual as
(
select p.Account,
p.SalesPerson,
p.Product,
[2] as 'ActFeb20',
[3] as 'ActMar20',
[4] as 'ActApr20',
[5] as 'ActMay20',
[6] as 'ActJun20',
[7] as 'ActJul20'
from dataMonths dm
pivot ( sum(dm.Cost) for ActualMonth in ([2], [3], [4], [5], [6], [7]) ) p
)
select dm.Account,
dm.SalesPerson,
dm.Product,
de.ExpFeb20,
da.ActFeb20,
de.ExpMar20,
da.ActMar20,
de.ExpApr20,
da.ActApr20,
de.ExpMay20,
da.ActMay20,
de.ExpJun20,
da.ActJun20,
de.ExpJul20,
da.ActJul20
from dataMonths dm
join dataExpected de
on de.Account = dm.Account
and de.SalesPerson = dm.SalesPerson
and de.Product = dm.Product
join dataActual da
on da.Account = dm.Account
and da.SalesPerson = dm.SalesPerson
and da.Product = dm.Product;
This gives me something quite close to the final report:
Account SalesPerson Product ExpFeb20 ActFeb20 ExpMar20 ActMar20 ExpApr20 ActApr20 ExpMay20 ActMay20 ExpJun20 ActJun20 ExpJul20 ActJul20
--------------- --------------- --------------- ----------- ----------- ----------- ----------- ----------- ----------- ----------- ----------- ----------- ----------- ----------- -----------
ABC House John Doe Blocks 120 120 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL
Pizza House Jose Guava Boxes NULL NULL NULL NULL 10 10 NULL NULL NULL NULL NULL NULL
The Hanger John Doe Wings 10 NULL NULL NULL NULL NULL NULL NULL NULL 10 NULL NULL
The Store John Doe Catnip 10 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL
The Store Jose Guava Boxes NULL NULL 15 NULL NULL NULL NULL 15 NULL NULL NULL NULL
WangTai Pete Ringer Lettuce NULL NULL NULL NULL 20 20 NULL NULL NULL NULL NULL NULL
WangTai Pete Ringer Paper Plates NULL NULL NULL NULL NULL NULL 22 NULL NULL NULL NULL NULL
Woolies Pete Ringer Catnip NULL NULL 20 20 NULL NULL NULL NULL NULL NULL NULL NULL

Related

How to select changed columns

The Problem
I'm trying to detect and react to changes in a table where each update is being recorded as a new row with some values being the same as the original, some changed (the ones I want to detect) and some NULL values (not considered changed).
For example, given the following table MyData, and assuming the OrderNumber is the common value,
ID OrderNumber CustomerName PartNumber Qty Price OrderDate
1 123 Acme Corp. WG301 4 15.02 2020-01-02
2 456 Base Inc. AL337 7 20.15 2020-02-03
3 123 NULL WG301b 5 19.57 2020-01-02
If I execute the query for OrderNumber = 123 I would like the following data returned:
Column OldValue NewValue
ID 1 3
PartNumber WG301 WG301b
Qty 4 5
Price 15.02 19.57
Or possibly a single row result with only the changes filled, like this (however, I would strongly prefer the former format):
ID OrderNumber CustomerName PartNumber Qty Price OrderDate
3 NULL NULL WG301b 5 19.57 NULL
My Solution
I have not had a chance to test this, but I was considering writing the query with the following approach (pseudo-code):
select
NewOrNull(last.ID, prev.ID) as ID,
NewOrNull(last.OrderNumber, prev.OrderNumber) as OrderNumber
NewOrNull(last.CustomerName, prev.CustomerName) as CustomerName,
...
from last row with OrderNumber = 123
join previous row where OrderNumber = 123
Where the function NewOrNull(lastVal, prevVal) returns NULL if the values are equal or lastVal value is NULL, otherwise the lastVal.
Why I'm Looking for an Answer
I'm afraid that the ugly join, the number of calls to the function, and the procedural approach may make this approach not scalable. Before I start down the rabbit hole, I was wondering...
The Question
...are there any other approaches I should try, or any best practices to solving this specific type of problem?
I came up with a solution for the second (less preferred) format:
The Data
Using the following data:
INSERT INTO MyData
([ID], [OrderNumber], [CustomerName], [PartNumber], [Qty], [Price], [OrderDate])
VALUES
(1, 123, 'Acme Corp.', 'WG301', '4', '15.02', '2020-01-02'),
(2, 456, 'Base Inc.', 'AL337', '7', '20.15', '2020-02-03'),
(3, 123, NULL, 'WG301b', '5', '19.57', '2020-01-02'),
(4, 123, 'ACME Corp.', 'WG301b', NULL, NULL, '2020-01-02'),
(6, 456, 'Base Inc.', NULL, '7', '20.15', '2020-02-05');
The Function
This function returns the updated value if it has changed, otherwise NULL:
CREATE FUNCTION dbo.NewOrNull
(
#newValue sql_variant,
#oldValue sql_variant
)
RETURNS sql_variant
AS
BEGIN
DECLARE #ret sql_variant
SELECT #ret = CASE
WHEN #newValue IS NULL THEN NULL
WHEN #oldValue IS NULL THEN #newValue
WHEN #newValue = #oldValue THEN NULL
ELSE #newValue
END
RETURN #ret
END;
The Query
This query returns the history of changes for the given order number:
select dbo.NewOrNull(new.ID, old.ID) as ID,
dbo.NewOrNull(new.OrderNumber, old.OrderNumber) as OrderNumber,
dbo.NewOrNull(new.CustomerName, old.CustomerName) as CustomerName,
dbo.NewOrNull(new.PartNumber, old.PartNumber) as PartNumber,
dbo.NewOrNull(new.Qty, old.Qty) as Qty,
dbo.NewOrNull(new.Price, old.Price) as Price,
dbo.NewOrNull(new.OrderDate, old.OrderDate) as OrderDate
from MyData new
left join MyData old
on old.ID = (
select top 1 ID
from MyData pre
where pre.OrderNumber = new.OrderNumber
and pre.ID < new.ID
order by pre.ID desc
)
where new.OrderNumber = 123
The Result
ID OrderNumber CustomerName PartNumber Qty Price OrderDate
1 123 Acme Corp. WG301 4 15.02 2020-01-02
3 (null) (null) WG301b 5 19.57 (null)
4 (null) ACME Corp. (null) (null) (null) (null)
The Fiddle
Here's the SQL Fiddle that shows the whole thing in action.
http://sqlfiddle.com/#!18/b720f/5/0

Split date result sets into columns in sql server

can you please assist
I have a query that shows number of teachers, the site the visited and when date they visited. This query looks at all teachers visits for the past week.
I want to split the dateattended field into columns to show daily visit for the past week. Below is how it looks.
EmployeeNumber Name HomeSite Site Attended Day Attended
TP-000322789 Samuel Mohlamnyane Teacher Port Elizabeth 2014-10-18 07:23
TP-000148774 Jean Smoothie Teacher Hennopsview 2014-10-13 08:55
TP-000148774 Jean Smoothie Teacher Hennopsview 2014-10-16 08:43
TP-000148122 Anthony Mike Teacher Tzaneen 2014-10-19 09:19
TP-000148122 Anthony Mike Teacher Tzaneen 2014-10-15 08:26
TP-000328452 Geneve Gorridon Teacher Tzaneen 2014-10-14 07:44
TP-000346529 Edmos Dube Teacher Melrose 2014-10-18 07:47
TP-000321374 Anita Rene Classen Teacher Johannesburg 2014-10-17 07:57
TP-000324511 Anthonysia White Teacher Durbanville 2014-10-15 07:53
TP-000324511 Anthonysia White Teacher Durbanville 2014-10-18 12:26
TP-000327471 Moses Mathebula Teacher Polokwane 2014-10-13 05:50
TP-000148194 Nonhlanhla Ndlovu Teacher Vereeniging 2014-10-15 07:06
TP-000323383 Lerato Manyanka Teacher Bedfordview 2014-10-13 07:26
TP-000323383 Lerato Manyanka Teacher Bedfordview 2014-10-16 06:51
TP-000323384 Lerato Manyanka Teacher Bedfordview 2014-10-17 08:57
Now I want to split Day attended to show date in different columns from yesterday going down to the last seven days.
Below is the code I used to get the above result set. And how the result should look like.
EmployeeNumber Name HomeSite Site Attended Day 1 Day2 Day 3 Day 4 Day 5 Day 6 Day 7
TP-000148194 Nonhlanhla Ndlovu Teacher Vereeniging 2014-10-15 07:06
TP-000323383 Lerato Manyanka Teacher Bedfordview 2014-10-17 08:57 2014-10-16 06:51 2014-10-13 07:26
SELECT mdet.MemRefNo AS 'EmployeeNumber'
, cont.FirstName + ' ' + cont.LastName AS Name
, s.Name AS 'HomeSite'
, Attend.VisitedSite AS 'Site Attended'
, Attend.Weekdays AS 'Day Attended'
FROM MemberDetail mdet
INNER JOIN MembershipHistory mhis ON mdet.CurrentMembershipID = mhis.ID32
INNER JOIN contacts cont ON cont.GUID = mdet.ContactGUID
INNER JOIN Sites s ON s.id = cont.HomeSiteID
INNER JOIN Packages pg ON pg.ID = mhis.PackageID
CROSS APPLY
(
SELECT min(a1.attenddate) AS Weekdays , a1.contactguid, a1.SiteID , s.Name as VisitedSite FROM dbo.attendance a1
INNER JOIN Sites s ON s.id = a1.Siteid
WHERE DATEDIFF(DAY,a1.attenddate,GETDATE()) <= 7
and ContactGuid = mdet.ContactGuid
AND a1.isswipesuccessful = 1
GROUP BY a1.ContactGuid, DATEPART(DW, a1.attenddate),a1.SiteID , s.Name
) Attend
WHERE pg.Description LIKE '%Teacher%'
I think this is the query you want, it uses a pivot with the results from your existing query as source through a common table expression. Ideally the code could be merged into one query, but as you didn't provide any test data from the source tables, but only the output I didn't try to rewrite it. Note that if a person visited the same site more than one time in a single day, only the latest time will be shown.
-- using original query as source
;WITH visits AS (
SELECT mdet.MemRefNo AS 'EmployeeNumber'
, cont.FirstName + ' ' + cont.LastName AS Name
, s.Name AS 'HomeSite'
, Attend.VisitedSite AS 'Site Attended'
, Attend.Weekdays AS 'Day Attended'
FROM MemberDetail mdet
INNER JOIN MembershipHistory mhis ON mdet.CurrentMembershipID = mhis.ID32
INNER JOIN contacts cont ON cont.GUID = mdet.ContactGUID
INNER JOIN Sites s ON s.id = cont.HomeSiteID
INNER JOIN Packages pg ON pg.ID = mhis.PackageID
CROSS APPLY
(
SELECT min(a1.attenddate) AS Weekdays , a1.contactguid, a1.SiteID , s.Name as VisitedSite FROM dbo.attendance a1
INNER JOIN Sites s ON s.id = a1.Siteid
WHERE DATEDIFF(DAY,a1.attenddate,GETDATE()) <= 7
and ContactGuid = mdet.ContactGuid
AND a1.isswipesuccessful = 1
GROUP BY a1.ContactGuid, DATEPART(DW, a1.attenddate),a1.SiteID , s.Name
) Attend
WHERE pg.Description LIKE '%Teacher%'
)
-- query to produce results
SELECT
EmployeeNumber,
Name,
HomeSite,
[Site Attended],
[1] AS 'Day 1',
[2] AS 'Day 2',
[3] AS 'Day 3',
[4] AS 'Day 4',
[5] AS 'Day 5',
[6] AS 'Day 6',
[7] AS 'Day 7'
FROM (
SELECT *, DATEDIFF(day, [Day Attended], GETDATE()) diff
FROM visits
WHERE [Day Attended] > (GETDATE()-7) -- adjust this to limit date range
) a
PIVOT (
MAX([Day Attended]) FOR [diff] in ([1],[2],[3],[4],[5],[6],[7])
) AS Pivoted;
It would probably be trivial to modify the existing query to get the desired results.
Sample SQL Fiddle (using the sample output data as source).
Sample results:
EmployeeNumber Name HomeSite Site Attended Day 1 Day 2 Day 3 Day 4 Day 5 Day 6 Day 7
-------------------- ---------------------------------------- -------------------- -------------------- ----------------------- ----------------------- ----------------------- ----------------------- ----------------------- ----------------------- -----------------------
TP-000148122 Anthony Mike Teacher Tzaneen NULL 2014-10-19 09:19:00.000 NULL NULL NULL 2014-10-15 08:26:00.000 NULL
TP-000148194 Nonhlanhla Ndlovu Teacher Vereeniging NULL NULL NULL NULL NULL 2014-10-15 07:06:00.000 NULL
TP-000148774 Jean Smoothie Teacher Hennopsview NULL NULL NULL NULL 2014-10-16 08:43:00.000 NULL NULL
TP-000321374 Anita Rene Classen Teacher Johannesburg NULL NULL NULL 2014-10-17 07:57:00.000 NULL NULL NULL
TP-000322789 Samuel Mohlamnyane Teacher Port Elizabeth NULL NULL 2014-10-18 07:23:00.000 NULL NULL NULL NULL
TP-000323383 Lerato Manyanka Teacher Bedfordview NULL NULL NULL 2014-10-17 08:57:00.000 2014-10-16 06:51:00.000 NULL NULL
TP-000324511 Anthonysia White Teacher Durbanville NULL NULL 2014-10-18 12:26:00.000 NULL NULL 2014-10-15 07:53:00.000 NULL
TP-000346529 Edmos Dube Teacher Melrose NULL NULL 2014-10-18 07:47:00.000 NULL NULL NULL NULL

MS SQL Change expiry date when inserting value with same name

I have a question relating to trigger. I have a table (FoodPrice) with different columns like a start date and an expiry date.
Let's consider that I would like to add a price that could expire like a sale, I would like (ON INSERT) to set the ExpiryDate of the previous value to the current date:
Initial table
Food Value StartDate ExpiryDate
------ ---------- ---------- ----------
Carrot 25.5 24/12/2013 NULL
Apple 44.9 5/1/2014 NULL
Squash 25.6 12/3/2013 NULL
New table with inserted rows:
Food Value StartDate ExpiryDate
------ ---------- ---------- ----------
Carrot 25.5 24/12/2013 28/4/2014
Apple 44.9 5/1/2014 NULL
Squash 25.6 12/3/2013 28/4/2014
Carrot 24 28/4/2014 NULL
Squash 22 28/4/2014 NULL
Dupplicate values for Food column is not a big deal but is it possible to create a trigger to solve this problem ? Thank you !
Here's the code:
-- the table and sample data
create table FoodPrice (
Food varchar(10),
Value decimal(5,2),
StartDate date,
ExpiryDate date
);
go
insert FoodPrice values
('Carrot', 20, '20131124' , '20131224'),
('Apple' , 40, '20140101' , '20140105'),
('Squash', 25, '20130301' , '20130312'),
('Carrot', 25.5, '20131224' , NULL),
('Apple' , 44.9, '20140105' , NULL),
('Squash', 25.6, '20130312' , NULL)
go
-- the trigger
create trigger trFoodPrice_insert
on FoodPrice
after insert
as
;with x as (
select fp.food, fp.startdate as fp_startdate, fp.expirydate as fp_expirydate,
ins.startdate as ins_startdate, ins.expirydate as ins_expirydate,
row_number() over(partition by fp.food order by fp.startdate) as rn
from ins
inner join foodprice fp on ins.food=fp.food
and fp.startdate < ins.startdate
and fp.expirydate is null
),
y as (
select *
from x
where rn = 1
)
--select * from y
update y
set fp_expirydate = ins_startdate
go
-- let's test it
insert foodprice values
('Carrot', 24, '20140428', null),
('Squash', 22, '20140428', null)
go
select * from
foodprice
order by food, startdate
As always, I'm a big fan of first testing the select before the actual update, hence the CTE.

Insert into Table B from Table A using Distinct

I have 2 tables
Table A
NameID FirstName MiddleName LastName Addr1 Addr2 Phn1 Phn2 City State
NULL Micheal Calvin Dodson 12 23 1234 123 XYZ ABC
NULL John NULL Keith NULL NULL 2344 NULL SQE FDG
NULL John NULL Keith NULL NULL 2344 NULL SQE FDG
NULL William Stephen NULL 45 NULL NULL NULL HJD ABC
NULL Victor NULL Anthony NULL NULL NULL NULL NULL NULL
Table B
NameID FirstName MiddleName LastName Addr1 Addr2 Phn1 Phn2 City State Zip Email Gender...
I need to get the distinct records of (FirstName,MiddleName,LastName) of Table A and insert the same details along with the other fields matching with Table A into Table B.
My Table B has NameID as an identity coloum. So after inserting a unique record into Table B, I need to get that NameID and insert it back into Table A shown below :
TABLE A
Table A
NameID FirstName MiddleName LastName Addr1 Addr2 Phn1 Phn2 City State
1 Micheal Calvin Dodson 12 23 1234 123 XYZ ABC
2 John NULL Keith NULL NULL 2344 NULL SQE FDG
2 John NULL Keith NULL NULL 2344 NULL SQE FDG
3 William Stephen NULL 45 NULL NULL NULL HJD ABC
4 Victor NULL Anthony NULL NULL NULL NULL NULL NULL
TABLE B
NameID FirstName MiddleName LastName Addr1 Addr2 Phn1 Phn2 City State Zip Email Gender...
1 Micheal Calvin Dodson 12 23 1234 123 XYZ ABC NULL NULL NULL
2 John NULL Keith NULL NULL 2344 NULL SQE FDG NULL NULL NULL
3 William Stephen NULL 45 NULL NULL NULL HJD ABC NULL NULL NULL
4 Victor NULL Anthony NULL NULL NULL NULL NULL NULL NULL NULL NULL
Can you please help me with this. Im not able to get this query right. Code in SQL Server 2008
Thanks in advance,
Sunitha
I think the easiest way to do this is with two queries. The first problem is handling duplicates in TableA. The following query selects an arbitrary row for each name combination:
insert into TableB()
select ()
from (select a.*,
row_number() over (partition by FirstName, MiddleName, LastName order by FirstName) as seqnum
from TableA a
) a
where seqnum = 1
Then, update the original table:
update TableA
set NameId = (select max(NameId) from TableB
where TableB.FirstName = TableA.FirstName and
TableB.MiddleName = TableA.MiddleName and
TableB.LastName = TableA.LastName
)
where NameId is null
If your fields contain NULL values (rather than blanks), you can use coalesce() for the join conditions:
update TableA
set NameId = (select max(NameId) from TableB
where coalesce(TableB.FirstName, '<null>') = coalesce(TableA.FirstName, '<null>') and
coalesce(TableB.MiddleName, '<null>') = coalesce(TableA.MiddleName, '<null>') and
coalesce(TableB.LastName , '<null>')= coalesce(TableA.LastName, '<null>')
)
where NameId is null
DECLARE #results TABLE
(
NameID INT,
FirstName VARCHAR(32), -- guessing on data types for these columns
MiddleName VARCHAR(32),
LastName VARCHAR(32)
);
;WITH x AS
(
SELECT FirstName, MiddleName, LastName,
rn = ROW_NUMBER() OVER (PARTITION BY FirstName, MiddleName, LastName
ORDER BY (SELECT NULL)
) --, ... other columns ...
FROM dbo.TableA
)
INSERT dbo.TableB
(
FirstName, MiddleName, LastName --, ... other columns ...
)
OUTPUT
inserted.NameID, inserted.FirstName,
inserted.MiddleName, inserted.LastName
INTO #results
SELECT FirstName, MiddleName, LastName --, ... other columns ...
FROM x WHERE rn = 1;
UPDATE a SET NameID = r.NameID
FROM dbo.TableA AS a
INNER JOIN #results AS r
ON COALESCE(a.FirstName,'') = COALESCE(r.FirstName,'')
AND COALESCE(a.MiddleName,'') = COALESCE(r.MiddleName,'')
AND COALESCE(a.LastName,'') = COALESCE(r.LastName,'');

Pivot on SQL Server 2010

I want to return the top three pets for each employee as columns instead of rows i.e.
Owner_ID Pet
--------------------
1 Cat
1 Dog
1 Hamster
2 Cow
2 Sheep
3 Dog
Convert it to
Owner_ID Pet1 Pet2 Pet3
-------------------------------------
1 Cat Dog Hamster
2 Cow Sheep null
3 Dog null null
The name of pets come from a lookup table and there can be any number of pets but I only want to return the top 3.
Here is my query:
SELECT Owner,Pet1, Pet2,Pet3
FROM
(select distinct OwnerID as Owner,glcom.Value as Pets
from Owner ,OwnerPets ,Pet
where Pet.Type='Furry'
and OwnerPets.OwnerID = OwnerID.OwnerID
and OwnerPets.PetID = Pet.PetID ) AS SourceTable
PIVOT
(
Max(Pets)
FOR Pets IN (Pet1, Pet2,Pet3)
) AS PivotTable;
Unfortunately it only returns null for each row... so the output I see is
Owner_ID Pet1 Pet2 Pet3
-------------------------------------
1 null null null
2 null null null
3 null null null
Hopefully it is a common problem and someone must have solved it already.
Thanks
If I have understood you correctly you can use ROW_NUMBER() over (partition by Owner_ID order by ...) on your query then pivot using those.
Example follows.
WITH Unpivoted AS
(
SELECT X.*, ROW_NUMBER() over (partition by Owner_ID order by Pet) AS RN
FROM (VALUES
(1, 'Cat') ,
(1, 'Dog') ,
(1, 'Hamster') ,
(1, 'Rhino'),
(1, 'Zebra'),
(2, 'Cow') ,
(2, 'Sheep') ,
(3, 'Dog' )
) AS X (Owner_ID, Pet)
)
SELECT Owner_Id, [1] AS Pet1, [2] AS Pet2,[3] AS Pet3 FROM Unpivoted
PIVOT
(
Max(Pet)
FOR RN IN ([1], [2],[3])
) AS PivotTable;
Returns
Owner_Id Pet1 Pet2 Pet3
----------- ------- ------- -------
1 Cat Dog Hamster
2 Cow Sheep NULL
3 Dog NULL NULL

Resources