I have a table with 3 columns
pID | key_name | value
-----------------------
10 | 'series' |'Songs'
10 | 'wood' |'Beech'
10 | 'language' |'German'
11 | 'series' |'Songs'
11 | 'wood' |'Oak'
11 | 'language' |'French'
12 | 'series' |'Exams'
12 | 'language' |'English'
I need to update a table where the key_names are now column names, thus
pID | series | wood | language
-----------------------------
10 ! 'Songs'|'Beech'|'German'
11 | 'Songs'|'Oak' |'French'
12 | 'Exams'| |'English'
Now I could write some SQL like
UPDATE dest-tbl INNER JOIN start-tbl
ON dest-tbl.pID = start-tbl.pID
SET dest-tbl.series = start-tbl.value
WHERE dest-tbl.key_name = 'series'
but since there are 65 different key_name values that would mean having to have 65 variations on that SQL.
It strikes me that the best way to do that might be to create an array of the key_name values and loop through that, except that I haven't go a clue how to do that.
Can anyone help me with this?
Using MariaDB v10.3
MTIA
EDIT:
I think I'm close to an answer with the SQL below.
I do need to insert this into another table and to filter the results based on the value of a field in another table. The code from SELECT down to GROUP creates the output I need but I've now got a problem with the JOIN part
INSERT INTO results ('series', 'wood', 'language')
SELECT table1.pID,
MAX(CASE WHEN table1.meta_key = 'series' THEN table1.meta_value END) 'series',
MAX(CASE WHEN table1.meta_key = 'wood' THEN table1.meta_value END) 'wood',
MAX(CASE WHEN table1.meta_key = 'language' THEN table1.meta_value END) 'language'
FROM table1
GROUP BY table1.pID
INNER JOIN ON table2.id = table1.pID
WHERE table2.id.type = 'product';
SELECT
t1.pID,
t1.value,
t2.value,
t3.value
FROM Table1 t1
LEFT JOIN Table1 t2 on t2.pID=t1.pID and t2.key_name='wood'
LEFT JOIN Table1 t3 on t3.pID=t1.pID and t3.key_name='language'
WHERE t1.key_name='series';
output:
+ -------- + ---------- + ---------- + ---------- +
| 10 | Songs | Beech | German |
| 11 | Songs | Oak | French |
| 12 | Exams | | English |
+ -------- + ---------- + ---------- + ---------- +
DBFIDDLE
As i said in the comments to get a flexible solution you need dynamic sql
CREATE TABLE tab1 (
`pID` INTEGER,
`key_name` VARCHAR(10),
`value` VARCHAR(9)
);
INSERT INTO tab1
(`pID`, `key_name`, `value`)
VALUES
('10', 'series', 'Songs'),
('10', 'wood', 'Beech'),
('10', 'language', 'German'),
('11', 'series', 'Songs'),
('11', 'wood', 'Oak'),
('11', 'language', 'French'),
('12', 'series', 'Exams'),
('12', 'language', 'English');
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'max(CASE WHEN `key_name` = ''',
`key_name`,
''' THEN `value` ELSe "" END) AS `',
`key_name`, '`'
)
) INTO #sql
FROM `tab1` ;
SET #sql = CONCAT('SELECT `pID`, ',#sql,' from tab1
GROUP BY `pID`');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
✓
✓
pID | language | series | wood
--: | :------- | :----- | :----
10 | German | Songs | Beech
11 | French | Songs | Oak
12 | English | Exams |
✓
db<>fiddle here
My solution, tested and working though a bit slow, is as follows:-
INSERT INTO results ('series', 'wood', 'language')
SELECT table1.pID,
MAX(CASE WHEN table1.meta_key = 'series' THEN table1.meta_value END) 'series',
MAX(CASE WHEN table1.meta_key = 'wood' THEN table1.meta_value END) 'wood',
MAX(CASE WHEN table1.meta_key = 'language' THEN table1.meta_value END) 'language'
FROM table1, table2
WHERE table2.ID=table1_pID AND table2.id.type = 'product';
GROUP BY table1.pID
Related
I am creating a code to join two different tables under a certain condition. The tables look like this
(TABLE 2)
date | deal_code | originator | servicer | random |
-----------------------------------------------------
2011 | 001 | commerzbank | SPV1 | 1 |
2012 | 001 | commerzbank | SPV1 | 12 |
2013 | 001 | commerzbank | SPV1 | 7 |
2013 | 005 | unicredit | SPV2 | 7 |
and another table
(TABLE 1)
date | deal_code | amount |
---------------------------
2011 | 001 | 100 |
2012 | 001 | 100 |
2013 | 001 | 100 |
2013 | 005 | 200 |
I would like to have this as the final result
date | deal_code | amount | originator | servicer | random |
--------------------------------------------------------------
2013 | 001 | 100 | commerzbank | SPV1 | 7 |
2013 | 005 | 200 | unicredit | SPV2 | 7 |
I created the following code
select q1.deal_code, q1.date
from table1 q1
where q1.date = (SELECT MAX(t4.date)
FROM table1 t4
WHERE t4.deal_code = q1.deal_code)
that gives me:
(TABLE 3)
date | deal_code | amount |
---------------------------
2013 | 001 | 100 |
2013 | 005 | 200 |
That is the latest observation for table 1, now I would like to have the originator and servicer information given the deal_code and date. Any suggestion? I hope to have been clear enough. Thanks.
This should do what you are looking for. Please be careful when naming columns. Date is a reserved word and is too ambiguous to be a good name for a column.
declare #Something table
(
SomeDate int
, deal_code char(3)
, originator varchar(20)
, servicer char(4)
, random int
)
insert #Something values
(2011, '001', 'commerzbank', 'SPV1', 1)
, (2012, '001', 'commerzbank', 'SPV1', 12)
, (2013, '001', 'commerzbank', 'SPV1', 7)
, (2013, '005', 'unicredit ', 'SPV2', 7)
declare #SomethingElse table
(
SomeDate int
, deal_code char(3)
, amount int
)
insert #SomethingElse values
(2011, '001', '100')
, (2012, '001', '100')
, (2013, '001', '100')
, (2013, '005', '200')
select x.SomeDate
, x.deal_code
, x.originator
, x.servicer
, x.random
, x.amount
from
(
select s.SomeDate
, s.deal_code
, s.originator
, s.servicer
, s.random
, se.amount
, RowNum = ROW_NUMBER()over(partition by s.deal_code order by s.SomeDate desc)
from #Something s
join #SomethingElse se on se.SomeDate = s.SomeDate and se.deal_code = s.deal_code
) x
where x.RowNum = 1
Looks like this would work:
DECLARE #MaxYear INT;
SELECT #MaxYear = MAX(date)
FROM table1 AS t1
INNER JOIN table2 AS t2
ON t1.deal_code = t2.deal_code;
SELECT t1.date,
t1.deal_code,
t1.amount,
t2.originator,
t2.servicer,
t2.random
FROM table1 AS t1
INNER JOIN table2 AS t2
ON t1.date = #MaxYear
AND t1.deal_code = t2.deal_code;
I agree with Sean Lange about the date column name. His method gets around the dependency on the correlated sub-query, but at the heart of things, you really just need to add an INNER JOIN to your existing query in order to get the amount column into your result set.
select
q2.date,
q2.deal_code,
q1.amount,
q2.originator,
q2.servicer,
q2.random
from
table1 q1
join
table2 q2
on q1.date = q2.date
and q1.deal_code = q2.deal_code
where q1.date = (SELECT MAX(t4.date)
FROM table1 t4
WHERE t4.deal_code = q1.deal_code)
I am working on a poorly designed database. I need to query the table to find the solution for the following issue.
Let's say I have a table like this.
+------------+------------+--------------+-----------+
| id | SubCode | Type | NumId |
+----------------------------------------------------+
| 1 | SB1212 | TCH | 100000000 |
| 1 | SB1212 | APP | 100000000 |
| 1 | SB1212 | TCH | 100000001 |
| 1 | SB1212 | APP | 100000002 |
+----------------------------------------------------+
I need to find the NumId of the people who only belong to Type='TCH' ( They shouldn't belong to Type='APP').
Note that the NumId is can be duplicated.
I wrote the following query and it's taking too much time to load. This looks like a simple issue but I am working on this for too long to not see a solution now. Can someone point me where I am doing wrong?
SELECT NumId
FROM TeacherSubject
WHERE SubCode = 'SB1212'
AND Type = 'TCH' OR Type = 'APP'
AND id NOT IN (SELECT NumId FROM TeacherSubject
WHERE SubCode = 'SB1212'
AND Type = 'APP')
ORDER BY NumId DESC
The output I am expecting is 100000001. Because 100000000 belongs to TCH and APP types.
You could use a CTE
with cte as (
select *
from TeacherSubject
where [Type] = 'APP'
and Subcode = 'SB1212'
)
select NumID
from TeacherSubject
where [Type] = 'TCH'
and Subcode = 'SB1212'
and NumID not in (select NumID from cte)
order by NumID desc
Try this
SELECT
NumId
FROM YourTable YT
WHERE [Type] = 'TCH'
AND SubCode = 'SB1212'
AND NOT EXISTS
(
SELECT
1
FROM YourTable
WHERE NumId = YT.NumId
AND [Type] <> YT.[Type]
AND [SubCode] = YT.SubCode
)
I have following table with some data.
CREATE TABLE #NetProfit (ID int, [Name] varchar(50),[Class] varchar(50), Balance money)
go
--Populate Sample records
INSERT INTO #NetProfit VALUES(4,'Income','No Class',303386.8462)
INSERT INTO #NetProfit VALUES(6,'Expenses','No Class',22443.5317)
INSERT INTO #NetProfit VALUES(4,'Income','2 TestUser3',0.00)
INSERT INTO #NetProfit VALUES(5,'Cost','2 TestUser3',0.3875)
INSERT INTO #NetProfit VALUES(6,'Expenses','2 TestUser3',6439.2129)
INSERT INTO #NetProfit VALUES(5,'Cost','3 TestUser3',0.1395)
INSERT INTO #NetProfit VALUES(6,'Expenses','3 TestUser3',6451.6129)
INSERT INTO #NetProfit VALUES(5,'Cost','38 Code#1012',3.0225)
INSERT INTO #NetProfit VALUES(6,'Expenses','38 Code#1012',30.225)
go
select * from #NetProfit
drop table #NetProfit
+----+----------+--------------+-------------+
| ID | Name | Class | Balance |
+----+----------+--------------+-------------+
| 4 | Income | No Class | 303386.8462 |
| 6 | Expenses | No Class | 22443.5317 |
| 4 | Income | 2 TestUser3 | 0 |
| 5 | Cost | 2 TestUser3 | 0.3875 |
| 6 | Expenses | 2 TestUser3 | 6439.2129 |
| 5 | Cost | 3 TestUser3 | 0.1395 |
| 6 | Expenses | 3 TestUser3 | 6451.6129 |
| 5 | Cost | 38 Code#1012 | 3.0225 |
| 6 | Expenses | 38 Code#1012 | 30.225 |
+----+----------+--------------+-------------+
I want to subtract [Balance] column row wise group by [Class] column.
For ex. NetProfit = (Income - Cost - Expenses) for each [Class] column with group by [Class].
Here is the output I am expecting.
+-------------+-------------+-------------+--------------+
| No Class | 2 TestUser3 | 3 TestUser3 | 38 Code#1012 |
+-------------+-------------+-------------+--------------+
| 280943.3145 | 6439.6004 | 6451.7524 | 33.2475 |
+-------------+-------------+-------------+--------------+
Any help would be appreciated. Thanks!
This could be the first step to achieve desired result:
SELECT NP.Class, ABS(ISNULL(SUM(CASE NP.Name WHEN 'Income' THEN NP.Balance END), 0) - SUM(CASE NP.Name WHEN 'Income' THEN NULL ELSE NP.Balance END)) AS Balance
FROM #NetProfit AS NP
GROUP BY NP.Class;
Then you would have to dynamically pivot these results. Statically, your query should look like that:
SELECT *
FROM (
SELECT NP.Class, ABS(ISNULL(SUM(CASE NP.Name WHEN 'Income' THEN NP.Balance END), 0) - SUM(CASE NP.Name WHEN 'Income' THEN NULL ELSE NP.Balance END)) AS Balance
FROM #NetProfit AS NP
GROUP BY NP.Class) AS SourceTable
PIVOT (MAX(Balance) FOR Class IN ([2 TestUser3], [3 TestUser3], [38 Code#1012], [No Class])) AS PivotTable;
So by plugging in dynamic SQL, This is the result:
DECLARE #SQL NVARCHAR(MAX);
DECLARE #Col NVARCHAR(MAX);
SET #Col = STUFF(( SELECT DISTINCT ',' + QUOTENAME(NP.Class)
FROM #NetProfit AS NP
FOR XML PATH('')), 1, 1, '');
SET #SQL = N'
SELECT *
FROM (
SELECT NP.Class, ABS(ISNULL(SUM(CASE NP.Name WHEN ''Income'' THEN NP.Balance END), 0) - SUM(CASE NP.Name WHEN ''Income'' THEN NULL ELSE NP.Balance END)) AS Balance
FROM #NetProfit AS NP
GROUP BY NP.Class) AS SourceTable
PIVOT (MAX(Balance) FOR Class IN (' + #Col + N')) AS PivotTable';
EXECUTE sys.sp_executesql #SQL;
That's the output:
+-------------+-------------+--------------+-------------+
| 2 TestUser3 | 3 TestUser3 | 38 Code#1012 | No Class |
+-------------+-------------+--------------+-------------+
| 6439,6004 | 6451,7524 | 33,2475 | 280943,3145 |
+-------------+-------------+--------------+-------------+
This query return calculation in rows.
With pivot table you can turn rows to columns
select isnull(c1, c2), isnull(i, 0)-isnull(e,0) from (
select * from
(select class c1, sum(balance) i
from #NetProfit
where Name = 'income'
group by class) i
full join
(
select class c2, sum(balance) e
from #NetProfit
where Name != 'income'
group by class) e on i.c1 = e.c2) t
Try this (assuming ID 4, for income, is a positive whereas the rest should be treated as negatives;
select
[Class No]=SUM(case when [Class]='No Class' then (case when ID=4 then Balance else Balance*-1 end) else 0 end),
[TestUser2]=SUM(case when [Class]='2 TestUser3' then (case when ID=4 then Balance else Balance*-1 end) else 0 end),
[TestUser3]=SUM(case when [Class]='3 TestUser3' then (case when ID=4 then Balance else Balance*-1 end) else 0 end),
[Code#1012]=SUM(case when [Class]='Code#1012' then (case when ID=4 then Balance else Balance*-1 end) else 0 end)
from
#NetProfit
I'm trying to take three tables that I have and show the data in a way the user asked me to do it. The tables look like this. (I should add that I am using MS SQL Server)
First Table: The ID is varchar, since it's an ID they use to identify assets and they use numbers as well as letters.
aID| status | group |
-----------------------
1 | acti | group1 |
2 | inac | group2 |
A3 | acti | group1 |
Second Table: This table is fixed. It has around 20 values and the IDs are all numbers
atID| traitname |
------------------
1 | trait1 |
2 | trait2 |
3 | trait3 |
Third Table: This table is used to identify the traits the assets in the first table have. The fields that have the same name as fields in the above tables are obviously linked.
tID| aID | atID | trait |
----------------------------------
1 | 1 | 1 | NAME |
2 | 1 | 2 | INFO |
3 | 2 | 3 | GOES |
4 | 2 | 1 | HERE |
5 | A3 | 2 | HAHA |
Now, the user wants the program to output the data in the following format:
aID| status | group | trait1 | trait2 | trait 3
-------------------------------------------------
1 | acti | group1 | NAME | INFO | NULL
2 | inac | group2 | HERE | NULL | GOES
A3 | acti | group1 | NULL | HAHA | NULL
I understand that to achieve this, I have to use the Pivot command in SQL. However, I've read and tried to understand it but I just can't seem to get it. Especially the part where it asks for a MAX value. I don't get why I need that MAX.
Also, the examples I've seen are for one table. I'm not sure if I can do it with three tables. I do have a query that joins all three of them with the information I need. However, I don't know how to proceed from there. Please, any help with this will be appreciated. Thank you.
There are several ways that you can get the result, including using the PIVOT function.
You can use an aggregate function with a CASE expression:
select t1.aid, t1.status, t1.[group],
max(case when t2.traitname = 'trait1' then t3.trait end) trait1,
max(case when t2.traitname = 'trait2' then t3.trait end) trait2,
max(case when t2.traitname = 'trait3' then t3.trait end) trait3
from table1 t1
inner join table3 t3
on t1.aid = t3.aid
inner join table2 t2
on t3.atid = t2.atid
group by t1.aid, t1.status, t1.[group];
See SQL Fiddle with Demo
The PIVOT function requires an aggregate function this is why you would need to use either the MIN or MAX function (since you have a string value).
If you have a limited number of traitnames then you could hard-code the query:
select aid, status, [group],
trait1, trait2, trait3
from
(
select t1.aid,
t1.status,
t1.[group],
t2.traitname,
t3.trait
from table1 t1
inner join table3 t3
on t1.aid = t3.aid
inner join table2 t2
on t3.atid = t2.atid
) d
pivot
(
max(trait)
for traitname in (trait1, trait2, trait3)
) piv;
See SQL Fiddle with Demo.
If you have an unknown number of values, then you will want to look at using dynamic SQL to get the final result:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(traitname)
from Table2
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT aid, status, [group],' + #cols + '
from
(
select t1.aid,
t1.status,
t1.[group],
t2.traitname,
t3.trait
from table1 t1
inner join table3 t3
on t1.aid = t3.aid
inner join table2 t2
on t3.atid = t2.atid
) x
pivot
(
max(trait)
for traitname in (' + #cols + ')
) p '
execute sp_executesql #query;
See SQL Fiddle with Demo
I am using Sql Server 2012 for my DBMS.
In my database I have a Product table that is related to a series of catalog tables. These catalog tables represent the various Product Categories. The idea being that while all products have certain attributes in common (our internal identifier for it, a supplier, a cost, etc) the specifics of the products will vary (furniture is not described the same way as a digital product would be described).
Additionally, each product has a parent entity. A parent may have multiple children (or products in this case).
My task is to select the products and related information for a given list of parent Id's and populate that information into an XML document.
The tables I'm currently working with are:
PRODUCT
PRODUCT_DIGITAL
PRODUCT_FURNITURE
Product has a PK/FK relationship with PRODUCT_DIGITAL on Product_Id. Same type of relationship exist for PRODUCT to PRODUCT_FURNITURE.
If product looks like this:
PRODUCT_ID -- PRODUCT_CATEGORY -- PARENT_ID -- PARENT_TYPE -- DELIVERY_IN_DAYS
100 DIG 1 1 7
101 DIG 1 1 8
102 DIG 1 1 1
103 DIG 2 1 2
104 DIG 2 1 1
And PRODUCT_DIGITAL looks like this:
PRODUCT_ID -- PRODUCT_TYPE -- PRODUCT_NAME -- PRODUCT_MNEMONIC
100 A IMG1 IMAWTRFL
101 B SND1 SNDENGRV
102 B SND2 SNDHRSLF
103 A IMG2 IMGNBRTO
104 B SND3 SNDGTWNE
In the end I want result set that looks like this:
PRODUCT_CATEGORY -- PRODUCT_ID -- PRODUCT_TYPE -- PARENT_ID -- DELIVERY_IN_DAYS -- PROD_EXTENSION_NAME -- PROD_EXTENSION_TYPE -- PROD_EXTENSION_VALUE
DIG 100 A 1 7 PRODUCT_NAME STRING IMG1
DIG 100 A 1 7 PRODUCT_MNEMONIC STRING IMAWTRFL
DIG 101 B 1 8 PRODUCT_NAME STRING SND1
DIG 101 B 1 8 PRODUCT_MNEMONIC STRING SNDENGRV
DIG 102 B 1 1 PRODUCT_NAME STRING SND2
DIG 102 B 1 1 PRODUCT_MNEMONIC STRING SNDHRSLF
DIG 103 A 2 2 PRODUCT_NAME STRING IMG2
DIG 103 A 2 2 PRODUCT_MNEMONIC STRING IMGNBRTO
DIG 104 B 2 1 PRODUCT_NAME STRING SND3
DIG 104 B 2 1 PRODUCT_MNEMONIC STRING SNDGTWNE
My original searching brought me to UNPIVOT - but so far I have not been able to get my head around it. What I ended up doing was creating a temp table and updating it in passes, then joining back to the products table for the final select:
create table #tbl(product_id int, prod_extension_name varchar(100), prod_extension_type varchar(100), prod_extension_value varchar(1000))
insert into #tbl
select p.product_id, c.column_name,
case c.data_type
when 'varchar' then 'string'
else data_type end as data_type
, null
from dbo.product p, information_schema.columns c
where c.table_name = 'PRODUCT_DIGITAL'
and c.column_name in ('PRODUCT_NAME','PRODUCT_MNEMONIC')
update #tbl
set prod_extension_value = p.product_name
from dbo.product p
where #tbl.product_id = p.product_id
and #tbl.colname = 'PRODUCT_NAME'
update #tbl
set prod_extension_value = p.product_mnemonic
from dbo.product p
where #tbl.product_id = p.product_id
and #tbl.colname = 'PRODUCT_MNEMONIC'
select p.product_category, p.product_id, pd.product_category, #tbl.prod_extension_name, #tbl.prod_extension_type, #tbl.prod_extension_value
from dbo.product p inner join dbo.product_digital pd on p.product_id = pd.product_id
inner join #tbl on p.product_id = #tbl.product_id
order by product_id
Can someone point me to a better way to do this? It seems like I should be able to do this faster, without having to make multiple updates, etc.
I think this will do what you want. This implements the UNPIVOT function and then get the column information from the information_schema.columns view to get the result:
select product_id,
product_category,
parent_id,
delivery_in_days,
PROD_EXTENSION_NAME,
case when c.data_type = 'varchar'
then 'STRING' else null end as PROD_EXTENSION_TYPE,
PROD_EXTENSION_VALUE
from
(
select
p.product_id,
p.product_category,
p.parent_id,
p.delivery_in_days,
PRODUCT_NAME,
PRODUCT_MNEMONIC
from product p
left join product_digital pd
on p.product_id = pd.product_id
) src
unpivot
(
PROD_EXTENSION_VALUE
for PROD_EXTENSION_NAME in (PRODUCT_NAME, PRODUCT_MNEMONIC)
) up
inner join
(
select c.column_name, c.data_type
from information_schema.columns c
where c.table_name = 'PRODUCT_DIGITAL'
and c.column_name in ('PRODUCT_NAME','PRODUCT_MNEMONIC')
) c
on up.PROD_EXTENSION_NAME = c.column_name
See SQL Fiddle with Demo
The result is:
| PRODUCT_ID | PRODUCT_CATEGORY | PARENT_ID | DELIVERY_IN_DAYS | PROD_EXTENSION_NAME | PROD_EXTENSION_TYPE | PROD_EXTENSION_VALUE |
-----------------------------------------------------------------------------------------------------------------------------------
| 100 | DIG | 1 | 7 | PRODUCT_NAME | STRING | IMG1 |
| 100 | DIG | 1 | 7 | PRODUCT_MNEMONIC | STRING | IMAWTRFL |
| 101 | DIG | 1 | 8 | PRODUCT_NAME | STRING | SND1 |
| 101 | DIG | 1 | 8 | PRODUCT_MNEMONIC | STRING | SNDENGRV |
| 102 | DIG | 1 | 1 | PRODUCT_NAME | STRING | SND2 |
| 102 | DIG | 1 | 1 | PRODUCT_MNEMONIC | STRING | SNDHRSLF |
| 103 | DIG | 2 | 2 | PRODUCT_NAME | STRING | IMG2 |
| 103 | DIG | 2 | 2 | PRODUCT_MNEMONIC | STRING | IMGNBRTO |
| 104 | DIG | 2 | 1 | PRODUCT_NAME | STRING | SND3 |
| 104 | DIG | 2 | 1 | PRODUCT_MNEMONIC | STRING | SNDGTWNE |
Edit #1: If you want to perform this type of transformation dynamically then you will need to use dynamic SQL. to do this you will use the following.
First, you will need to get the list of columns to UNPIVOT:
select #colsUnpivot = stuff((select ','+quotename(C.name)
from sys.columns as C
where C.object_id = object_id('PRODUCT_DIGITAL') and
C.name not in ('PRODUCT_ID', 'PRODUCT_TYPE') -- include the items you DO NOT want to unpivot
for xml path('')), 1, 1, '')
The final script will be:
DECLARE #colsUnpivot AS NVARCHAR(MAX),
#cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #colsUnpivot = stuff((select ','+quotename(C.name)
from sys.columns as C
where C.object_id = object_id('PRODUCT_DIGITAL') and
C.name not in ('PRODUCT_ID', 'PRODUCT_TYPE')
for xml path('')), 1, 1, '')
select #cols = stuff((select ', '''+C.name + ''''
from sys.columns as C
where C.object_id = object_id('PRODUCT_DIGITAL') and
C.name not in ('PRODUCT_ID', 'PRODUCT_TYPE')
for xml path('')), 1, 1, '')
set #query
= ' select
product_id,
product_category,
parent_id,
delivery_in_days,
PROD_EXTENSION_NAME,
case when c.data_type = ''varchar''
then ''STRING'' else null end as PROD_EXTENSION_TYPE,
PROD_EXTENSION_VALUE
from
(
select
p.product_id,
p.product_category,
p.parent_id,
p.delivery_in_days,
PRODUCT_NAME,
PRODUCT_MNEMONIC
from product p
left join product_digital pd
on p.product_id = pd.product_id
) src
unpivot
(
PROD_EXTENSION_VALUE
for PROD_EXTENSION_NAME in ('+ #colsunpivot +')
) up
inner join
(
select c.column_name, c.data_type
from information_schema.columns c
where c.table_name = ''PRODUCT_DIGITAL''
and c.column_name in ('+#cols+')
) c
on up.PROD_EXTENSION_NAME = c.column_name'
exec(#query)
See SQL Fiddle with Demo. This will produce the same result as the original version except it is dynamic.
I might be jumping the gun, but you say that your final output should be XML.
While I have not included all your requirements, would something like this suit your requirements? (I think I may have over simplified your requirements)
;WITH PRODUCT (PRODUCT_ID, PRODUCT_CATEGORY, PARENT_ID, PARENT_TYPE, DELIVERY_IN_DAYS) AS
(
SELECT 100, 'DIG', 1, 1, 7 UNION ALL
SELECT 101, 'DIG', 1, 1, 8 UNION ALL
SELECT 102, 'DIG', 1, 1, 1 UNION ALL
SELECT 103, 'DIG', 2, 1, 2 UNION ALL
SELECT 104, 'DIG', 2, 1, 1
)
,PRODUCT_DIGITAL (PRODUCT_ID, PRODUCT_TYPE, PRODUCT_NAME, PRODUCT_MNEMONIC) AS
(
SELECT 100, 'A', 'IMG1', 'IMAWTRFL' UNION ALL
SELECT 101, 'B', 'SND1', 'SNDENGRV' UNION ALL
SELECT 102, 'B', 'SND2', 'SNDHRSLF' UNION ALL
SELECT 103, 'A', 'IMG2', 'IMGNBRTO' UNION ALL
SELECT 104, 'B', 'SND3', 'SNDGTWNE'
)
SELECT P.PRODUCT_CATEGORY
,P.PRODUCT_ID
,P.PARENT_TYPE
,P.PARENT_ID
,P.DELIVERY_IN_DAYS
,PD.PRODUCT_NAME
,PD.PRODUCT_MNEMONIC
FROM PRODUCT P
JOIN PRODUCT_DIGITAL PD ON P.PRODUCT_ID = PD.PRODUCT_ID
FOR XML PATH