Reputation: 910
I've been trying for a while to use SQL Server pivot but I just don't seem to be getting it right. I've read a bunch of SO answers, but don't understand how pivot works.
I'm writing a stored procedure. I have Table 1 (received as a TVP), and need to make it look like Table 2 (see this image for tables).
Important: the values in Table1.valueTypeID cannot be hard coded into the logic because they can always change. Therefore, the logic must be super dynamic.
Please see the code below. The pivot is at the end of the stored procedure.
-- Create date: 12/10/2013
-- Description: select all the contacts associated with received accountPassport
-- =============================================
ALTER PROCEDURE [dbo].[selectContactsPropsByAccountPassport]
-- Add the parameters for the stored procedure here
@accountPassport int,
@valueTypeFiltersTVP valueTypeFiltersTVP READONLY
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
DECLARE @accountID int;
DECLARE @contactsAppAccountPassport int;
DECLARE @searchResults TABLE
(
resultContactID int
);
DECLARE @resultContactID int;
DECLARE @contactsPropsForReturn TABLE
(
contactID int,
valueTypeID int,
value varchar(max)
);
create table #contactsPropsForReturnFiltered(contactID int,valueTypeID int, value varchar(max))
/*
DECLARE #contactsPropsForReturnFiltered TABLE
(
contactID int,
valueTypeID int,
value varchar(max)
);
*/
--2. get @contactsAppAccountPassport associated with recieved @accountPassport
-- go into dbo.accounts and get the @accountID associated with this @accountPassport
SELECT
@accountID = ID
FROM
dbo.accounts
WHERE
passport = @accountPassport
-- go into dbo.accountsProps and get the value (@contactsAppAccountPassport) where valueType=42 and accountID = @accountID
SELECT
@contactsAppAccountPassport = value
FROM
dbo.accountsProps
WHERE
(valueTypeID=42) AND (accountID = @accountID)
--3. get all the contact ID's from dbo.contacts associated with @contactsAppAccountPassport
INSERT INTO
@searchResults
SELECT
ID
FROM
dbo.contacts
WHERE
contactsAppAccountPassport = @contactsAppAccountPassport
--4. Get the props of all contact ID's from 3.
--start for each loop....our looping object is @resultContactID row. if there are more rows, we keep looping.
DECLARE searchCursor CURSOR FOR
SELECT
resultContactID
FROM
@searchResults
OPEN searchCursor
FETCH NEXT FROM searchCursor INTO @resultContactID
WHILE (@@FETCH_STATUS=0)
BEGIN
INSERT INTO
@contactsPropsForReturn
SELECT
contactID,
valueTypeID,
value
FROM
dbo.contactsProps
WHERE
contactID = @resultContactID
FETCH NEXT FROM searchCursor INTO @resultContactID
END --end of WHILE loop
--end of cursor (both CLOSE and DEALLOCATE necessary)
CLOSE searchCursor
DEALLOCATE searchCursor
-- select and return only the props that match with the requested props
-- (we don't want to return all the props, only the ones requested)
INSERT INTO
#contactsPropsForReturnFiltered
SELECT
p.contactID,
p.valueTypeID,
p.value
FROM
@contactsPropsForReturn as p
INNER JOIN
@valueTypeFiltersTVP as f
ON
p.valueTypeID = f.valueTypeID
DECLARE @cols AS NVARCHAR(MAX),
@query AS NVARCHAR(MAX);
SET @cols = STUFF((SELECT distinct ',' + QUOTENAME(ValueTypeId)
FROM #contactsPropsForReturnFiltered
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'');
set @query = 'SELECT contactid, ' + @cols + ' from
(
select contactid
, Value
,ValueTypeId
from #contactsPropsForReturnFiltered
) x
pivot
(
min(Value)
for ValueTypeId in (' + @cols + ')
) p ';
execute(@query);
END
Upvotes: 2
Views: 1681
Reputation: 50241
Why do you need to present the data in this way?
In many cases, clients are better at pivoting than the database engine. For example, SQL Server Reporting Services easily does this with the matrix control. Similarly, if you are coding a web page in, say, Asp.Net, you can run through the recordset quickly to pass your data into a new data representation (meanwhile collecting unique values) and then in a single pass through the new data object spit out the HTML to render the result.
If at all possible, have your client do the pivoting instead of the server.
UPDATE:
If you really want to use table variables in dynamic SQL, you can just fine in SQL Server 2008 and up. Here's an example script:
USE tempdb
GO
CREATE TYPE IDList AS TABLE (
ID int
);
GO
DECLARE @SQL nvarchar(max);
SET @SQL = 'SELECT * FROM @TransactionIDs WHERE ID >= 4;'
DECLARE @TransactionIDs IDLIst;
INSERT @TransactionIDs VALUES (1), (2), (4), (8), (16);
EXEC sp_executesql @SQL, N'@TransactionIDs IDList READONLY', @TransactionIDs;
GO
DROP TYPE IDList;
Upvotes: 0
Reputation: 1152
You need to use dynamic pivot in your case. Try the following
create table table1
(
contactid int,
ValueTypeId int,
Value varchar(100)
);
insert into table1 values (56064, 40, 'Issac');
insert into table1 values (56064, 34, '(123)456-7890');
insert into table1 values (56065, 40, 'Lola');
insert into table1 values (56065, 34, '(123)456-7832');
insert into table1 values (56068, 40, 'Mike');
insert into table1 values (56068, 41, 'Gonzalez');
insert into table1 values (56068, 34, '(123)456-7891');
DECLARE @cols AS NVARCHAR(MAX),
@query AS NVARCHAR(MAX);
SET @cols = STUFF((SELECT distinct ',' + QUOTENAME(ValueTypeId)
FROM table1
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'');
set @query = 'SELECT contactid, ' + @cols + ' from
(
select contactid
, Value
,ValueTypeId
from table1
) x
pivot
(
min(Value)
for ValueTypeId in (' + @cols + ')
) p ';
execute(@query);
drop table table1
Upvotes: 2