Want to create logic in SQL Server to create a derived column based on comparing two comma-separated columns.
Sample table data -
Create table ##table1 (ID INT Identity Primary Key, FulfillmentChannelStatus varchar(255),RoleAlternateSourcingChannel varchar (255))
insert into ##table1 values ('Filled,Open,In-process','Internal,Recruiter,Contractor')
,('Open,In-process,New','Contractor,Internal,Recruiter')
,('New,Filled','Contractor,Recruiter ')
,('Filled','Recruiter')
,('Open,New,Filled','Internal,Recruiter,Contractor')
,('Filled,Filled,Filled','Internal,Contractor,Recruiter')
,('Open ,Filled, In-proces','Contractor,Internal,Recruiter')
,('Filled','Others')
,('Cancelled,Filled','Contractor,Recruiter')
,('Cancelled, Filled, Cancel - In Process','Contractor,Recruiter,Internal')
Logic for new column--
--select * from ##tble
DECLARE #separator CHAR(1) = ','
SELECT
[Role Id],[RoleAlternateSourcingChannel],[FulfillmentChannelStatus] , [Filled fulfil] = x.value('(/root/r[sql:column("t.pos")]/text())[1]', 'VARCHAR(10)')
into ##temp FROM ##tble
CROSS APPLY (SELECT x = TRY_CAST('<root><r><![CDATA[' +
REPLACE([FulfillmentChannelStatus], #separator, ']]></r><r><![CDATA[') +
']]></r></root>' AS XML)
.query('
for $x in /root/r[text()="Filled"][1]
return count(root/r[. << $x]) + 1
').value('text()[1]','INT')) AS t(pos)
CROSS APPLY (SELECT TRY_CAST('<root><r><![CDATA[' +
REPLACE([RoleAlternateSourcingChannel], #separator, ']]></r><r><![CDATA[') +
']]></r></root>' AS XML)) AS t2(x)
Scenario: I have two comma-separated columns
1 . I need to calculate values for only "Filled" values (in column [Role Alternate Sourcing Channel])
2. In 1st first row- I have a Filled value for Internal Scheduling in column ([RoleAlternateSourcingChannel]) so in the output column – it will be Internal.
3. In 2nd row – I don’t have any Filled so the output will be Null.
4. in 3rd row - I have a Filled value for Recruiter so the output will be Recruiter.
And so on…
5.In Row 6 for all value is filled so the output will be a recruiter. because preference of Recruiter>Internal>Contractor
Other than Recruiter/Internal/ Contractor all filled values will be Null.
The position of Filled value is not fixed. It can be anywhere such as - either at 1st place/position or 2nd place or in 3rd place.
Expected Output -
|+----+------------------------+----------------------------+---------------+
| ID |FulfillmentChannelStatus|RoleAlternateSourcingChannel| Filled fulfil |
+----+------------------------+----------------------------+---------------+
| 1 | Filled,Open,In-process | Internal,Recruiter,Contractor | Internal |
| 2 | Open,In-process,New | Contractor,Internal,Recruiter | NULL |
| 3 | New,Filled | Contractor,Recruiter | Recruiter |
| 4 | Filled | Recruiter | Recruiter |
| 5 | Open,New,Filled | Internal,Recruiter,Contractor | Contractor |
| 6 | Filled,Filled,Filled | Internal,Contractor,Recruiter | Recruiter |
| 7 | Open ,Filled, In-process| Contractor,Internal,Recruiter | Internal |
| 8 | Filled | Others | Null
| 9 | Cancelled, Filled, Cancel - In Procecess|Contractor,Internal,Recruiter | Internal
| 10| Cancelled, Filled| Internal,Recruiter| Recruiter
+----+------------------------+-------+--------+----------------------------+
**Question:** I tried Query2, For all other cases it is working fine now but for Row 9 and 10 O/P is Null but it should be Internal and Recruiter respectively.
A minimal reproducible example ##1-4 is not provided.
Shooting from the hip.
Please try the following solution based on XQuery.
XML and XQuery data model is based on ordered sequences, exactly what we need.
You moved the goalposts in the middle of the game.
I made just the "Recruiter","Internal","Contractor" as a legitimate
values for the RoleAlternateSourcingChannel column. Everything
else is filtered out.
I don't see any easy way to handle the preference of
Recruiter>Internal>Contractor for the row #6.
SQL #1
-- DDL and sample data population, start
DECLARE #tbl TABLE (ID INT IDENTITY PRIMARY KEY, FulfillmentChannelStatus VARCHAR(255), RoleAlternateSourcingChannel VARCHAR(255));
INSERT INTO #tbl (FulfillmentChannelStatus, RoleAlternateSourcingChannel) VALUES
('Filled,Open,In-process', 'Internal,Recruiter,Contractor'),
('Open,In-process,New', 'Contractor,Internal,Recruiter'),
('New,Filled', 'Contractor,Recruiter'),
('Filled', 'Recruiter'),
('Open,New,Filled', 'Internal,Recruiter,Contractor'),
('Filled,Filled,Filled', 'Internal,Contractor,Recruiter'),
('Open,Filled,In-process', 'Contractor,Internal,Recruiter'),
('Filled', 'Others');
-- DDL and sample data population, end
DECLARE #separator CHAR(1) = ',';
SELECT tbl.*
, Result = x.value('(/root/r[sql:column("t.pos")]/text())[1]', 'VARCHAR(10)')
FROM #tbl AS tbl
CROSS APPLY (SELECT x = TRY_CAST('<root><r><![CDATA[' +
REPLACE(FulfillmentChannelStatus, #separator, ']]></r><r><![CDATA[') +
']]></r></root>' AS XML)
.query('
if (count(/root/r[text()="Filled"]) eq 1) then
for $x in /root/r[text()="Filled"]
return count(root/r[. << $x]) + 1
else ()
').value('text()[1]','INT')) AS t(pos)
CROSS APPLY (SELECT TRY_CAST('<root><r><![CDATA[' +
REPLACE(RoleAlternateSourcingChannel, #separator, ']]></r><r><![CDATA[') +
']]></r></root>' AS XML).query('<root>
{
for $x in /root/r[text()=("Recruiter","Internal","Contractor")]
return $x
}
</root>
')) AS t2(x);
Output
+----+--------------------------+-------------------------------+------------+
| ID | FulfillmentChannelStatus | RoleAlternateSourcingChannel | Result |
+----+--------------------------+-------------------------------+------------+
| 1 | Filled,Open,In-process | Internal,Recruiter,Contractor | Internal |
| 2 | Open,In-process,New | Contractor,Internal,Recruiter | NULL |
| 3 | New,Filled | Contractor,Recruiter | Recruiter |
| 4 | Filled | Recruiter | Recruiter |
| 5 | Open,New,Filled | Internal,Recruiter,Contractor | Contractor |
| 6 | Filled,Filled,Filled | Internal,Contractor,Recruiter | NULL |
| 7 | Open,Filled,In-process | Contractor,Internal,Recruiter | Internal |
| 8 | Filled | Others | NULL |
+----+--------------------------+-------------------------------+------------+
SQL #2
DB fiddle
-- DDL and sample data population, start
DECLARE #tbl TABLE (ID INT IDENTITY PRIMARY KEY, FulfillmentChannelStatus VARCHAR(255), RoleAlternateSourcingChannel VARCHAR(255));
INSERT INTO #tbl (FulfillmentChannelStatus, RoleAlternateSourcingChannel) VALUES
('Filled,Open,In-process', 'Internal,Recruiter,Contractor'),
('Open,In-process,New', 'Contractor,Internal,Recruiter'),
('New,Filled', 'Contractor,Recruiter'),
('Filled', 'Recruiter'),
('Open,New,Filled', 'Internal,Recruiter,Contractor'),
('Filled,Filled,Filled', 'Internal,Contractor,Recruiter'),
('Open,Filled,In-process', 'Contractor,Internal,Recruiter'),
('Filled', 'Others'),
('Cancelled,Filled','Contractor,Recruiter'),
('Cancelled, Filled, Cancel - In Process','Contractor,Recruiter,Internal');
-- DDL and sample data population, end
DECLARE #separator CHAR(1) = ',';
;WITH rs AS
(
SELECT ID, x
FROM #tbl
CROSS APPLY (SELECT TRY_CAST('<root>' +
'<source><r><![CDATA[' + REPLACE(REPLACE(FulfillmentChannelStatus,SPACE(1),''), #separator, ']]></r><r><![CDATA[') +
']]></r></source>' +
'<target><r><![CDATA[' + REPLACE(REPLACE(RoleAlternateSourcingChannel,SPACE(1),''), #separator, ']]></r><r><![CDATA[') +
']]></r></target>' +
'</root>' AS XML).query('<root>
{
for $x in /root/source/r
let $pos := count(root/source/r[. << $x]) + 1
return <r>
<s>{data($x)}</s><t>{data(/root/target/r[$pos])}</t>
</r>
}
</root>')) AS t(x)
), cte AS
(
SELECT ID
, c.value('(s/text())[1]', 'VARCHAR(30)') AS source
, c.value('(t/text())[1]', 'VARCHAR(30)') AS [target]
FROM rs
CROSS APPLY x.nodes('/root/r') AS t(c)
), cte2 AS
(
SELECT *
, ROW_NUMBER() OVER (PARTITION BY ID ORDER BY
CASE [target]
WHEN 'Recruiter' THEN 1
WHEN 'Internal' THEN 2
WHEN 'Contractor' THEN 3
END) AS seq
FROM cte
WHERE source = 'Filled'
AND [target] IN ('Recruiter','Internal','Contractor')
)
SELECT t.*
, c.[target] --, c.seq
FROM #tbl AS t
LEFT OUTER JOIN cte2 AS c ON c.ID = t.ID
WHERE c.seq = 1 OR c.seq is NULL
ORDER BY t.ID;
Output
+----+----------------------------------------+-------------------------------+------------+
| ID | FulfillmentChannelStatus | RoleAlternateSourcingChannel | target |
+----+----------------------------------------+-------------------------------+------------+
| 1 | Filled,Open,In-process | Internal,Recruiter,Contractor | Internal |
| 2 | Open,In-process,New | Contractor,Internal,Recruiter | NULL |
| 3 | New,Filled | Contractor,Recruiter | Recruiter |
| 4 | Filled | Recruiter | Recruiter |
| 5 | Open,New,Filled | Internal,Recruiter,Contractor | Contractor |
| 6 | Filled,Filled,Filled | Internal,Contractor,Recruiter | Recruiter |
| 7 | Open,Filled,In-process | Contractor,Internal,Recruiter | Internal |
| 8 | Filled | Others | NULL |
| 9 | Cancelled,Filled | Contractor,Recruiter | Recruiter |
| 10 | Cancelled, Filled, Cancel - In Process | Contractor,Recruiter,Internal | Recruiter |
+----+----------------------------------------+-------------------------------+------------+
I want to split a column in 1 table into multiple columns in another table
Let's say I want to join the FoodAttributeTable to the FoodTable:
FoodTable
+------------+-------+
| Food | Price |
+------------+-------+
| Strawberry | 10 |
| Broccoli | 25 |
+------------+-------+
FoodAttributeTable
+------------+---------------+----------------+
| Food | AttributeName | AttributeValue |
+------------+---------------+----------------+
| Strawberry | Vitamin | C |
| Strawberry | Weight | 15g |
| Strawberry | Color | Red |
| Broccoli | Vitamin | B |
| Broccoli | Weight | 70g |
| Broccoli | Color | Green |
+------------+---------------+----------------+
Table to be:
FoodTable
+------------+-------+---------+--------+-------+
| Food | Price | Vitamin | Weight | Color |
+------------+-------+---------+--------+-------+
| Strawberry | 10 | C | 15g | Red |
| Broccoli | 25 | B | 70g | Green |
+------------+-------+---------+--------+-------+
you can use a simple PIVOT query like below.
Check out official MSDN documentation on PIVOT.
select
Food,Price,Vitamin,Weight,Color
from
(
select f.Food,f.Price,
AttributeName ,AttributeValue
from
FoodTable f join
FoodAttributeTable fat on
f.Food=fat.Food
)s
pivot
(max(AttributeValue) for AttributeName in (Vitamin,Weight,Color))
p
Also here's a live demo
This is a Dynamic Pivot Query. Therefore, I would only recommend this if your FoodAttributeTable attributes are dynamics.
Create Tables and Insert Data
CREATE TABLE FoodAttributeTable(
Food nvarchar(50) NULL,
AttributeName nvarchar(50) NULL,
AttributeValue nvarchar(50) NULL
)
INSERT FoodAttributeTable (Food, AttributeName, AttributeValue) VALUES (N'Strawberry', N'Vitamin', N'C')
INSERT FoodAttributeTable (Food, AttributeName, AttributeValue) VALUES (N'Strawberry', N'Weight', N'15g')
INSERT FoodAttributeTable (Food, AttributeName, AttributeValue) VALUES (N'Strawberry', N'Color', N'Red')
INSERT FoodAttributeTable (Food, AttributeName, AttributeValue) VALUES (N'Broccoli', N'Vitamin', N'B')
INSERT FoodAttributeTable (Food, AttributeName, AttributeValue) VALUES (N'Broccoli', N'Weight', N'70g')
INSERT FoodAttributeTable (Food, AttributeName, AttributeValue) VALUES (N'Broccoli', N'Color', N'Green')
CREATE TABLE FoodTable(
Food nvarchar(50) NULL,
Price decimal(18, 0) NULL
)
INSERT FoodTable (Food, Price) VALUES (N'Strawberry', N'10')
INSERT FoodTable (Food, Price) VALUES (N'Broccoli', N'25')
Query
DECLARE #colsValues AS NVARCHAR(max) = Stuff((SELECT DISTINCT ',' + Quotename(fat.attributename)
FROM foodattributetable fat
FOR xml path(''), type).value('.', 'NVARCHAR(MAX)'), 1, 1, '');
DECLARE #query AS NVARCHAR(max) = 'SELECT *
FROM (SELECT *
FROM foodattributetable
PIVOT(Max(attributevalue)
FOR attributename IN ('+ #colsValues +')) piv) fat
INNER JOIN foodtable tb
ON tb.food = fat.food';
EXECUTE(#query)
Output
+------------+-------+---------+--------+------------+-------+
| Food | Color | Vitamin | Weight | Food | Price |
+------------+-------+---------+--------+------------+-------+
| Broccoli | Green | B | 70g | Broccoli | 25 |
| Strawberry | Red | C | 15g | Strawberry | 10 |
+------------+-------+---------+--------+------------+-------+
Demo: http://rextester.com/ATZF46215
I'm trying to extract a value from my XML and seem to be struggling. Hope someone can help
here is my XML
'<Transfer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Products>
<Product TUT="true" ID="38319223176264031724">
<Identifier>38319223176264031724</Identifier>
<ProductItemCode>83192</ProductItemCode>
<Qty>1</Qty>
<NetWeight>23.100</NetWeight>
<GrossWeight>23.684</GrossWeight>
<SerialNumber>317</SerialNumber>
<ECertItemNumber>2</ECertItemNumber>
<Markets Type="ECERT">
<Market>EU</Market>
<Market>US</Market>
</Markets>
<Attribute Name="PackDate">2016-09-20T00:00:00</Attribute>
<Attribute Name="PlantID">124</Attribute>
<Attribute Name="SlgrDate">2016-09-19T00:00:00</Attribute>
</Product>
<Product TUT="true" ID="28319219766306010024">
<Identifier>28319219766306010024</Identifier>
<ProductItemCode>83192</ProductItemCode>
<Qty>1</Qty>
<NetWeight>19.700</NetWeight>
<GrossWeight>20.284</GrossWeight>
<SerialNumber>100</SerialNumber>
<ECertItemNumber>2</ECertItemNumber>
<Markets Type="ECERT">
<Market>EU</Market>
<Market>US</Market>
</Markets>
<Attribute Name="PackDate">2016-11-01T00:00:00</Attribute>
<Attribute Name="PlantID">124</Attribute>
<Attribute Name="SlgrDate">2016-10-31T00:00:00</Attribute>
</Product>
</Products>
</Transfer>'
What I want to extract are the Identifier, ProductItemCode, NetWeight, GrossWeight, AND the Attribute Values of PackDate and SlgrDate.
I can easily get all of the fields EXCEPT for Attribute Values of PackDate and SlgrDate
Here is my code for the fields
if OBJECT_ID('tempdb..#XmlImportTest') is not null
drop table #XmlImportTest
CREATE TABLE #XmlImportTest(
xmlFileName VARCHAR(300) NOT NULL,
xml_data XML NOT NULL
)
GO
DECLARE #xmlFileName VARCHAR(3000)
SELECT #xmlFileName = 'K:\Upload\CSNXML\WaybillXml.xml'
--– dynamic sql is just so we can use #xmlFileName variable in OPENROWSET
EXEC('INSERT INTO #XmlImportTest(xmlFileName, xml_data)
SELECT ''' + #xmlFileName + ''', xmlData
FROM(
SELECT *
FROM OPENROWSET (BULK ''' + #xmlFileName + ''', SINGLE_BLOB) AS XMLDATA
) AS FileImport (XMLDATA)
')
GO
DECLARE #XML AS XML, #hDoc AS INT, #SQL NVARCHAR (MAX)
select #xml = (SELECT xml_data from #XmlImportTest)
EXEC sp_xml_preparedocument #hDoc OUTPUT, #XML
SELECT Identifier as barcode,ProductItemCode as standpack,SerialNumber, NetWeight netwt_ind, GrossWeight grosswt_ind
FROM OPENXML (#hDoc, '/Transfer/Products/Product',2)
WITH (Identifier varchar(80),
ProductItemCode varchar(10),
SerialNumber varchar(48),
NetWeight decimal(13,2),
GrossWeight decimal(13,2)
)
exec sp_xml_removedocument #hDoc
the xml file contains the same as the sample xml provided
Now I have no idea how to get the value out of the Attributes for each product.
I am running this in SQL SERVER 2008
Use the optional ColPattern to specify the XPath to the node you want.
FROM OPENXML (#hDoc, '/Transfer/Products/Product',2)
WITH (
Identifier varchar(80),
ProductItemCode varchar(10),
SerialNumber varchar(48),
NetWeight decimal(13,2),
GrossWeight decimal(13,2),
PackDate datetime 'Attribute[#Name = "PackDate"]',
PlantID int 'Attribute[#Name = "PlantID"]',
SlgrDate datetime 'Attribute[#Name = "SlgrDate"]'
)
FROM OPENXML is outdated and should not be used anymore (rare exceptions exist)
Try to use the up-to-date XML-type-methods:
SELECT p.value(N'#TUT',N'bit') AS TUT
,p.value(N'#ID',N'nvarchar(max)') AS ID
,p.value(N'(Identifier/text())[1]',N'nvarchar(max)') AS Identifier
,p.value(N'(ProductItemCode/text())[1]',N'int') AS ProductItemCode
,p.value(N'(Qty/text())[1]',N'int') AS Qty
,p.value(N'(NetWeight/text())[1]',N'decimal(14,4)') AS NetWeight
,p.value(N'(SerialNumber/text())[1]',N'int') AS SerialNumber
,p.value(N'(ECertItemNumber/text())[1]',N'int') AS ECertItemNumber
,p.value(N'(Markets/#Type)[1]',N'nvarchar(max)') AS Markets_Type
,m.value(N'text()[1]',N'nvarchar(max)') AS Markets_Market
,p.value(N'(Attribute[#Name="PackDate"]/text())[1]',N'datetime') AS PackDate
,p.value(N'(Attribute[#Name="PlantID"]/text())[1]',N'int') AS PlantID
,p.value(N'(Attribute[#Name="SlgrDate"]/text())[1]',N'datetime') AS SlgrDate
FROM #xml.nodes(N'Transfer/Products/Product') AS A(p)
CROSS APPLY a.p.nodes(N'Markets/Market') AS B(m);
The result
+-----+----------------------+----------------------+-----------------+-----+-----------+--------------+-----------------+--------------+----------------+-------------------------+---------+-------------------------+
| TUT | ID | Identifier | ProductItemCode | Qty | NetWeight | SerialNumber | ECertItemNumber | Markets_Type | Markets_Market | PackDate | PlantID | SlgrDate |
+-----+----------------------+----------------------+-----------------+-----+-----------+--------------+-----------------+--------------+----------------+-------------------------+---------+-------------------------+
| 1 | 38319223176264031724 | 38319223176264031724 | 83192 | 1 | 23.1000 | 317 | 2 | ECERT | EU | 2016-09-20 00:00:00.000 | 124 | 2016-09-19 00:00:00.000 |
+-----+----------------------+----------------------+-----------------+-----+-----------+--------------+-----------------+--------------+----------------+-------------------------+---------+-------------------------+
| 1 | 38319223176264031724 | 38319223176264031724 | 83192 | 1 | 23.1000 | 317 | 2 | ECERT | US | 2016-09-20 00:00:00.000 | 124 | 2016-09-19 00:00:00.000 |
+-----+----------------------+----------------------+-----------------+-----+-----------+--------------+-----------------+--------------+----------------+-------------------------+---------+-------------------------+
| 1 | 28319219766306010024 | 28319219766306010024 | 83192 | 1 | 19.7000 | 100 | 2 | ECERT | EU | 2016-11-01 00:00:00.000 | 124 | 2016-10-31 00:00:00.000 |
+-----+----------------------+----------------------+-----------------+-----+-----------+--------------+-----------------+--------------+----------------+-------------------------+---------+-------------------------+
| 1 | 28319219766306010024 | 28319219766306010024 | 83192 | 1 | 19.7000 | 100 | 2 | ECERT | US | 2016-11-01 00:00:00.000 | 124 | 2016-10-31 00:00:00.000 |
+-----+----------------------+----------------------+-----------------+-----+-----------+--------------+-----------------+--------------+----------------+-------------------------+---------+-------------------------+
Hint: If you do not need the <Markets> as 1:n-relation (here doubling your resultset!), just remove the CROSS APPLY and the line starting with m.value.
I want to transpose my table.
I have simple 'Person' table as shown below.
+---+----------+------------+------------+----------------------+
|ID | Person | BirthDate | Phone | Email |
+---+----------+------------+------------+----------------------+
| 1 | Tom | 1985-11-08 | 1111111111 | tom#somedomain.com |
+---+----------+------------+------------+----------------------+
| 2 | Dick | 1982-02-24 | 2222222222 | dick#otherdomain.com |
+---+----------+------------+------------+----------------------+
| 3 | Harry | 1986-04-17 | 3333333333 | harry#thatdomain.com |
+---+----------+------------+------------+----------------------+
And I want this table to be transposed like below.
+-----------+--------------------+----------------------+----------------------+
| Key | Value1 | Value2 | Value3 |
+-----------+--------------------+----------------------+----------------------+
| ID | 1 | 2 | 3 |
+-----------+--------------------+----------------------+----------------------+
| Person | Tom | Dick | Harry |
+-----------+--------------------+----------------------+----------------------+
| BirthDate | 1985-11-08 | 1982-02-24 | 1986-04-17 |
+-----------+--------------------+----------------------+----------------------+
| Phone | 1111111111 | 2222222222 | 3333333333 |
+-----------+--------------------+----------------------+----------------------+
| Email | tom#somedomain.com | dick#otherdomain.com | harry#thatdomain.com |
+-----------+--------------------+----------------------+----------------------+
I am using MS SQL server 2008 R2.
Try this.. First u need to unpivot the columns using Cross apply to get the data in single row. Then pivot that row to get the result.
CREATE TABLE #tt
(ID INT,Person VARCHAR(50),BirthDate DATE,
Phone BIGINT,Email VARCHAR(50)
)
INSERT INTO #tt
VALUES (1,'Tom','1985-11-08',1111111111,'tom#somedomain.com' ),
( 2,'Dick','1982-02-24',2222222222,'dick#otherdomain.com'),
( 3,'Harry ','1986-04-17',3333333333,'harry#thatdomain.com' )
SELECT [key],
Max([value1]) [value1],
Max([value2]) [value2],
Max([value3]) [value3]
FROM (SELECT 'value' + CONVERT(VARCHAR(30), id) valued,
*
FROM #tt
CROSS apply (VALUES ('ID',
CONVERT(VARCHAR(50), ID)),
('Person',Person),
('BirthDate',CONVERT(VARCHAR(50), BirthDate)),
('Phone',CONVERT(VARCHAR(50), Phone)),
('Email',Email)) cp ([key], data))a
PIVOT (Max(data)
FOR valued IN([value1],[value2],[value3])) piv
GROUP BY [key]
DYNAMIC VERSION
Declare #cols varchar(max)='',#aggcols varchar(max)='',#sql nvarchar(max)
SELECT #cols+= ',value' + CONVERT(VARCHAR(30), id)
FROM #tt
SELECT #aggcols+= ',max([value' + CONVERT(VARCHAR(30), id) +']) value' + CONVERT(VARCHAR(30), id)
FROM #tt
select #cols= right(#cols,LEN(#cols)-1)
select #aggcols =right(#aggcols,LEN(#aggcols)-1)
set #sql = 'SELECT [key],
'+#aggcols+'
FROM (SELECT ''value'' + CONVERT(VARCHAR(30), id) valued,
*
FROM #tt
CROSS apply (VALUES (''ID'',CONVERT(VARCHAR(50), ID)),
(''Person'',Person),
(''BirthDate'',CONVERT(VARCHAR(50), BirthDate)),
(''Phone'',CONVERT(VARCHAR(50), Phone)),
(''Email'',Email)) cp ([key], data))a
PIVOT (Max(data)
FOR valued IN('+#cols+')) piv
GROUP BY [key] '
execute sp_executesql #sql
OUTPUT
+----------+--------------------+---------------------+----------------------+
|key | value1 | value2 | value3 |
+----------+--------------------+---------------------+----------------------+
|BirthDate | 1985-11-08 | 1982-02-24 | 1986-04-17 |
+----------+--------------------+---------------------+----------------------+
|Email | tom#somedomain.com |dick#otherdomain.com | harry#thatdomain.com |
+----------+--------------------+---------------------+----------------------+
|ID | 1 | 2 | 3 |
+----------+--------------------+---------------------+----------------------+
|Person | Tom | Dick | Harry |
+----------+--------------------+---------------------+----------------------+
|Phone | 1111111111 | 2222222222 | 3333333333 |
+----------+--------------------+---------------------+----------------------+