Diego Diego - 6 months ago 44
SQL Question

SQL server: Transpose Rows to Columns (n:m relationship)

After trying it myself for some hours now I need to ask for help. I only did some basic SQL until now.

I want to solve the following:

(I have translated a couple of things for you to understand the context)

I have three tables:

Workers (Mitarbeiter in German - mitID)

| mitID | Name | FamName | DOB | abtIDref |
|-------|--------|---------|------------|----------|
| 1 | Frank | Sinatra | 12.12.1915 | 1 |
| 2 | Robert | Downey | 4.4.1965 | 2 |


INFO:
abtIDref
is an 1:n relation for the
Workplace
, but not involved here

Skills (Faehigkeiten in German - faeID)

| faeID | Descr | time | cost |
|-------|-------|------|------|
| 1 | HV | 2 | 0 |
| 2 | PEV | 1 | 0 |
| 3 | Drive | 8 | 250 |
| 4 | Nex | 20 | 1200 |


Link-List

| linkID | mitIDref | feaIDref | when |
|--------|----------|----------|------------|
| 1 | 2 | 1 | 27.07.2014 |
| 2 | 2 | 2 | 01.01.2016 |
| 3 | 2 | 3 | 20.01.2016 |
| 4 | 1 | 3 | 05.06.2015 |
| 5 | 1 | 4 | 02.11.2015 |


The desired result is:

| mitID | Name | FamName | DOB | abtIDref | HV | PEV | Drive | Nex |
|-------|--------|---------|------------|----------|-----------|------------|------------|------------|
| 1 | Frank | Sinatra | 12.12.1915 | 1 | | | 05.06.2015 | 02.11.2015 |
| 2 | Robert | Downey | 4.4.1965 | 2 | 27.7.2014 | 01.01.2016 | 20.01.2015 | |


Alternative it could be:

| mitID | Name | FamName | DOB | abtIDref | HV | PEV | Drive | Nex |
|-------|--------|---------|------------|----------|----|-----|-------|-----|
| 1 | Frank | Sinatra | 12.12.1915 | 1 | | | x | x |
| 2 | Robert | Downey | 4.4.1965 | 2 | x | x | x | |


The goal is that users/admins can add up new skills and someone can see on this resultlist, if a person has this skill.







What did i try:

I've come across multiple examples of dynamic SQL and the pivot function, but I don't know how to use it in my case, because I don't run a function like
AVG()
or
MIN()
.

I tried it like this:

DECLARE @columns AS VARCHAR(MAX);
DECLARE @sql AS VARCHAR(MAX);

select @columns = substring((Select DISTINCT ',' + QUOTENAME(faeID) FROM mdb_Fähigkeiten FOR XML PATH ('')),2, 1000);

SELECT @sql = 'SELECT * FROM mdb_Mitarbeiter
PIVOT
(
MAX(Value)
FOR mitID IN( ' + @columns + ' )
);';

execute(@sql);


And a second approach was:

declare @collist nvarchar(max)
SET @collist = stuff((select distinct ',' + QUOTENAME(Question)
FROM #t1 -- your table here
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),1,1,'')
select @collist
declare @q nvarchar(max)
set @q = '
select *
from (
select
Vorname, Bezeichnung, faeIDref
from (
select #t1.*, #t2.Answer, #t2.parent
from #t1
inner join #t2 on #t1.QID = #t2.QID
) as x
) as source
pivot (
max(Answer)
for Question in (' + @collist + ')
) as pvt
'

exec (@q)


But TBH I don't get the functions found.
I hope you can provide me with some guidance what I have to change (or even if I can) achieve this.

jpw jpw
Answer

I believe the query below is what you are looking for. Adjust the column and table names as needed to fit your database.

DECLARE @sql AS NVARCHAR(MAX)
DECLARE @cols AS NVARCHAR(MAX)

SELECT @cols= ISNULL(@cols + ',','') + QUOTENAME(Descr)
FROM Faehigkeiten ORDER BY faeID 

SET @sql = N'
    SELECT mitID, Name, FamName, DOB, abtIDref, ' + @cols + '
    FROM (
       SELECT mitID, Name, FamName, DOB, abtIDref, [when], descr 
       FROM Mitarbeiter m
       JOIN [Link-List] l ON m.mitID = l.mitIDref
       JOIN Faehigkeiten f ON f.faeID = l.feaIDref
    ) a
    PIVOT(MAX([when]) FOR descr IN (' + @cols + ')) p'

EXEC sp_executesql @sql