Dynamic sql pivot values coming in seperate columns - sql

HI I am trying to write dynamic pivot as I have over 100 columns
ID Question Answer
1 Name peter
1 DOB 11/12/2003
1 address …..
1 Issue1 d
1 Issue2 a
2 Name sam
2 DOB 10/01/1998
2 address …..
2 Issue1 p
2 Issue2 f
I want the output like this:
ID Name DOB address Issue1 Issue2
1 peter 11/12/2003 …. d a
2 sam 10/01/1998 …. p f
Here is the code that I have used:-
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(Question)
from #temp
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = N'SELECT ID + #cols + N'
from #temp
pivot
(
max([answer])
for Question in (' + #cols + N')
) p '
exec sp_executesql #query;
But I am getting each answer in a separate row. I want all the answers of an ID to come in one row. Can somebody help me. Appreciate your time on this. Thank you.
I am getting the output like this, which is not I want. I want the output as above.
ID Name DOB address Issue1 Issue2
1 Peter
1 11/12/2003
1 …
1 d
1 a

It looks like you are looking for a way to dynamically create a pivot based on your table without aggregation. You can try the following:
SELECT #cols = STUFF((SELECT ',' + QUOTENAME(Question)
FROM #TEMP
GROUP BY QUESTION
ORDER BY QUESTION
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT ID,' + #cols + ' FROM
(
SELECT ID, Question, Answer
FROM #TEMP
) x
pivot
(
MAX(Answer)
for Question in (' + #cols + ')
) p '
execute(#query);
Expected Output:
+----+---------+------------+--------+--------+-------+
| ID | address | DOB | Issue1 | Issue2 | Name |
+----+---------+------------+--------+--------+-------+
| 1 | ….. | 11/12/2003 | d | a | peter |
| 2 | ….. | 10/1/1998 | p | f | sam |
+----+---------+------------+--------+--------+-------+

Related

Count the number of matches for a prefix in a SQL query

I have a table in an SQL server that looks like the one below and I want to count the number of unique occurrences where specific prefixes are used in the data column, like "21:00:00".
Dataset:
+-------------------------+
| data |
+-------------------------+
| 21:00:00:24:ff:5e:3a:bd |
| 50:01:43:80:18:6b:2a:4c |
| 21:00:00:1b:32:0f:a7:54 |
| 10:00:00:90:fa:a8:da:2a |
+-------------------------+
Desired query output:
+----------+----------+----------+
| 21:00:00 | 50:01:43 | 10:00:00 |
+----------+----------+----------+
| 2 | 1 | 1 |
+----------+----------+----------+
I have been able to get the query to count a single prefix at a time by using this:
SELECT COUNT(DISTINCT wwpn) AS "21:00:00" FROM table WHERE wwpn LIKE '21:00:00%'
However, I want to count multiple prefixes as shown in the desired query output.
I've been waiting for someone to do a dynamic pivot (like Matt said in the comments) but no one has done it yet : (...I tried it myself and this is what I managed...
Query:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + LEFT(QUOTENAME(data), 9) + ']'
FROM DataTable
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = N'SELECT ' + #cols + N' from
(
select LEFT(data, 8) as data, COUNT(*) as count
from DataTable
GROUP BY LEFT(data, 8)
) x
pivot
(
max(count)
for data in (' + #cols + N')
) p '
exec sp_executesql #query;
Results:
10:00:00 | 21:00:00 | 50:01:43
---------|----------|---------
1 | 2 | 1
---------|----------|---------
Use this query:
SELECT LEFT([Data], 8) as prefix, count(*) as cnt
FROM tableName
GROUP BY LEFT([Data], 8);
If you know the prefixes in advance then you can do something simple like this;
Create test data;
CREATE TABLE #TestData (FieldName nvarchar(50))
INSERT INTO #TestData
VALUES
('21:00:00:24:ff:5e:3a:bd')
,('50:01:43:80:18:6b:2a:4c')
,('21:00:00:1b:32:0f:a7:54')
,('10:00:00:90:fa:a8:da:2a')
Query
SELECT
SUM(CASE WHEN FieldName LIKE '21:00:00%' THEN 1 ELSE 0 END) [21:00:00]
,SUM(CASE WHEN FieldName LIKE '50:01:43%' THEN 1 ELSE 0 END) [50:01:43]
,SUM(CASE WHEN FieldName LIKE '10:00:00%' THEN 1 ELSE 0 END) [10:00:00]
FROM #TestData
Result
21:00:00 50:01:43 10:00:00
2 1 1

Is there an easy way with (T-)SQL to transform rows into columns like this?

So let's say I have 3 tables like
Questions
========================
id | qtext
========================
1 | "What is 3 x 23?"
------------------------
2 | "Your age?"
Registrants
========================
id | name
========================
1 | "Jason"
------------------------
2 | "Subhasish"
Answers
======================================
registrant_id | question_id | val
======================================
1 | 1 | 69
--------------------------------------
2 | 2 | 45
--------------------------------------
1 | 2 | 26
Is there a way to create from this a table like
AnswersByPartner
==================================================
Name | "What is 3 x 23?" | "Your age?"
==================================================
"Jason" | 69 | 26
--------------------------------------------------
"Subhasish" | NULL | 45
Yes! Here you go (Please check the column names for typo's):
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(c.qtext)
FROM Questions c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT Name, ' + #cols + ' from
(
select r.name, a.val,q.qtext from
registrants r
inner join answers a
on r.id = a.registrant_id
inner join questions q
on q.id = a.question_id
) x
pivot
(
max(val)
for qtext in (' + #cols + ')
) p '
execute(#query)
I always liked doing it like this:
select
R.name as Name,
A1.val as [What is 3 x 23?],
A2.val as [Your age?]
from Registrants R
left join Answers A1 on R.id = A1.registrant_id and A1.question_id = 1
left join Answers A2 on R.id = A2.registrant_id and A2.question_id = 2

Dynamically append columns based on another table in SQL

Suppose I want the following behavior in SQL
QTY Table
SERIAL_NO QTY CODE
1111111 1 AA
1111112 1 AA
1111111 2 BB
1111111 4 BB
1111113 7 CC
Code Table
CODE CODE_NAME
AA NameA
BB NameB
CC NameC
Query Result
SERIAL_NO NameA NameB NameC
1111111 1 6 0
1111112 1 0 0
1111113 0 0 7
NameA,B,C column in query result are basically the sums grouped by their respective code.
How do I achieve this behavior? The hardest part I'm trying to grasp is to dynamically add the columns to the query result based on the code table. For example, if the user adds another code called DD, the query result should automatically append NameD to the right side, and get the corresponding sum for that new code.
If you really need it in SQL you can leverage PIVOT and dynamic SQL in the following way
DECLARE #cols NVARCHAR(MAX), #colp NVARCHAR(MAX), #sql NVARCHAR(MAX)
SET #cols = STUFF(
(
SELECT DISTINCT ',COALESCE(' + QUOTENAME(code_name) + ',0) AS ' + QUOTENAME(code_name)
FROM code
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
SET #colp = STUFF(
(
SELECT DISTINCT ',' + QUOTENAME(code_name)
FROM code
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
SET #sql = 'SELECT serial_no, ' + #cols +
' FROM
(
SELECT serial_no, code_name, qty
FROM qty q JOIN code c
ON q.code = c.code
) x
PIVOT
(
SUM(qty) FOR code_name IN (' + #colp + ')
) p
ORDER BY serial_no'
EXECUTE(#sql)
Output:
| SERIAL_NO | NAMEA | NAMEB | NAMEC |
|-----------|-------|-------|-------|
| 1111111 | 1 | 6 | 0 |
| 1111112 | 1 | 0 | 0 |
| 1111113 | 0 | 0 | 7 |
Here is SQLFiddle demo

Dynamic Pivot Columns in SQL Server

I have a table named Property with following columns in SQL Server:
Id Name
There are some property in this table that certain object in other table should give value to it.
Id Object_Id Property_Id Value
I want to make a pivot table like below that has one column for each property I've declared in 1'st table:
Object_Id Property1 Property2 Property3 ...
I want to know how can I get columns of pivot dynamically from table. Because the rows in 1'st table will change.
Something like this:
DECLARE #cols AS NVARCHAR(MAX);
DECLARE #query AS NVARCHAR(MAX);
select #cols = STUFF((SELECT distinct ',' +
QUOTENAME(Name)
FROM property
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '');
SELECT #query =
'SELECT *
FROM
(
SELECT
o.object_id,
p.Name,
o.value
FROM propertyObjects AS o
INNER JOIN property AS p ON o.Property_Id = p.Id
) AS t
PIVOT
(
MAX(value)
FOR Name IN( ' + #cols + ' )' +
' ) AS p ; ';
execute(#query);
SQL Fiddle Demo.
This will give you something like this:
| OBJECT_ID | PROPERTY1 | PROPERTY2 | PROPERTY3 | PROPERTY4 |
-------------------------------------------------------------
| 1 | ee | fd | fdf | ewre |
| 2 | dsd | sss | dfew | dff |

Convert rows into columns by grouping one row value

My table is like
CompanyName | DirectorName
ADS | Rai
ADS | Rao
ADS | Raj
ADS | Rio
SAS | Josh
SAS | John
But I want result like:
CompanyName | Director1 | Director2 | Director3 | Director4
ADS |Rai |Rao |Raj |Rio
SAS |Josh |John | |
For each company director name count varies. So kindly suggest how to form the data as per requirement dynamically.
Try this:
DECLARE #cols AS NVARCHAR(MAX);
DECLARE #query AS NVARCHAR(MAX);
SELECT #cols = STUFF((SELECT distinct ',' +
QUOTENAME('Director' + CAST(ROW_NUMBER()
OVER(PARTITION BY c.companyname
ORDER BY c.companyname)
AS VARCHAR(10)))
FROM CompaniesDirectors c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)') ,1,1,'');
SET #query = 'SELECT companyname, ' + #cols + ' from
(
SELECT
c.CompanyName,
c.DirectorName,
''Director'' + CAST(ROW_NUMBER()
OVER(PARTITION BY c.companyname
ORDER BY c.companyname)
AS VARCHAR(10)) director_num
FROM CompaniesDirectors c
) x
PIVOT
(
MAX(directorname)
FOR director_num IN (' + #cols + ')
) p ';
EXECUTE(#query);
This should give you something like:
companyname Director1 Director2 Director3 Director4
AB Foo Bar rr tt
ADS Rai Rao Raj Rio
SQL Fiddle Demo1
1: Thanks to #bluefeet