I have a table SchoolStudent with three columns and the following values
Now, for a student we can have dynamic columns which are stored in a table with values as:
I want a query where i can club static fields as well as dynamic fields in a single SQL Query. I tried using Pivot but was not successful. Please suggest on how to accomplish this. I am basically doing it so that on frontend i have one resultset which can be searched thru.
So query should be:
Select
StudentID, FirstName, Address, MotherName,
MotherEmail, MotherOccupation, Mother Salary,
FacebookProfileName, VehicleRegNo
from SchoolStudent
Inner Join
(Logic to convert rows to columns)
Since you have dynamic columns, then you will need to use dynamic SQL to generate the result that you want. This query will generate the list of columns in the second table and PIVOT them to then be joined to the SchoolStudent to get the other columns:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(DynamicColumnName)
from StudentDetails
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = '
select s.studentid,
s.firstname,
s.address,
d.*
from SchoolStudent s
inner join
(
SELECT studentId,' + #cols + '
from
(
select studentId, dynamiccolumnname,
dynamiccolumnvalue
from StudentDetails
) x
pivot
(
max(dynamiccolumnvalue)
for dynamiccolumnname in (' + #cols + ')
) p
) d
on s.StudentId = d.StudentId'
execute(#query);
See SQL Fiddle with Demo. This query gives the result:
| STUDENTID | FIRSTNAME | ADDRESS | FACEBOOK PROFILE NAME | MOTHER EMAIL | MOTHER NAME | MOTHER OCCUPATION | MOTHER SALARY | VEHICLE REG NO |
-----------------------------------------------------------------------------------------------------------------------------------------------
| 4 | admin | efewfwfew | ross | ram#ram.com | ram#ram.com | ram#ram.com | ram#ram.com | UP 14 as |
You could use sub query format like this:
SELECT t1.StudentID,
t1.FirstName,
t1.Address,
(SELECT t2.DynamicColumnValue FROM table2 t2 WHERE t2.StudentID = t1.StudentID AND t2.DynamicColumnName = 'Mother Name') AS 'MotherName',
(SELECT t2.DynamicColumnValue FROM table2 t2 WHERE t2.StudentID = t1.StudentID AND t2.DynamicColumnName = 'Mother Email') AS 'MotherEmail'
FROM Table1 t1
or you could use inner join format like this:
SELECT t1.StudentID,
t1.FirstName,
t1.Address,
t2.DynamicColumnValue AS 'MotherName'
t3.DynamicColumnValue AS 'MotherEmail'
FROM Table1 t1
INNER JOIN Table2 t2 ON t1.StudentID = t2.StudentID AND t2.DynamicColumnName = 'Mother Name'
INNER JOIN Table2 t3 ON t1.StudentID = t3.StudentID AND t2.DynamicColumnName = 'Mother Email'
With some dynamic sql, you can do this:
DECLARE #cols NVARCHAR(2000)
SELECT #cols = STUFF((SELECT DISTINCT '],[' + DynamicColumnName FROM DynamicValues ORDER BY '],[' + DynamicColumnName FOR XML PATH('')), 1, 2, '') + ']'
DECLARE #query NVARCHAR(4000);
SET #query = N'select s.StudentID, s.FirstName, s.Address,'+#cols+' from SchoolStudent s
join
(
select * from DynamicValues
pivot (Max(DynamicColumnValue) for DynamicColumnName in ('+#cols+')) as pvt
) as b on b.StudentID=s.StudentID'
EXECUTE(#query)
Related
I've seen many examples of concatenating strings in SQL Server, but if they worry about sorting, it's always by the column being concatenated.
I need to order the values based on data in a different fields.
Sample table:
ClassID | StudentName | SortOrder
-----------------------------
A |James |1
A |Janice |3
A |Leonard |2
B |Luke |2
B |Leia |1
B |Artoo |3
And the results I'd like to get are:
ClassID |StudentName
--------------------------------
A |James, Leonard, Janice
B |Leia, Luke, Artoo
How can this be done in SQL Server 2016?
(I'm looking forward to STRING_AGG in 2017, but we're not there yet...)
Thanks!
Here you go:
SELECT
s1.ClassID
, STUFF((SELECT
',' + s2.StudentName
FROM dbo.Student AS s2
WHERE s1.classID = s2.ClassID
ORDER BY s2.SortOrder
FOR XML PATH('')), 1, 1, '') AS StudentNames
FROM dbo.Student AS s1
GROUP BY s1.ClassID
SQL Fiddle
MS SQL Server 2017 Schema Setup:
CREATE TABLE MyTable(ClassID varchar(255),StudentName varchar(255),SortOrder int)
INSERT INTO MyTable(ClassID,StudentName,SortOrder)VALUES('A','James',1),('A','Janice',3),('A','Leonard',2),
('B','Luke',2),('B','Lela',1),('B','Artoo',3)
Query 1:
SELECT
t.ClassID
, STUFF((SELECT
',' + t1.StudentName
FROM MyTable t1
WHERE t.classID = t1.ClassID
ORDER BY t1.SortOrder
FOR XML PATH('')), 1, 1, '') AS StudentNamesConcat
FROM MyTable AS t
GROUP BY t.ClassID
Results:
| ClassID | StudentNamesConcat |
|---------|----------------------|
| A | James,Leonard,Janice |
| B | Lela,Luke,Artoo |
Here the query
IF OBJECT_ID('tempdb..#Temp') IS NOT NULL DROP TABLE #Temp;
CREATE TABLE #Temp(ClassId varchar(10),studName varchar(100),SortOrder int)
INSERT INTO #Temp(ClassId , studName, SortOrder)
SELECT 'A','James',1 UNION ALL
SELECT 'A','Janice',3UNION ALL
SELECT 'A','Leonard',2 UNION ALL
SELECT 'B','Luke',2 UNION ALL
SELECT 'B','Leia',1 UNION ALL
SELECT 'B','Artoo',3
-- select * from #Temp
select
distinct
stuff((
select ',' + u.studName
from #Temp u
where u.studName = studName and U.ClassId = L.ClassId
order by u.SortOrder
for xml path('')
),1,1,'') as userlist,ClassId
from #Temp L
group by ClassId
You can try using PIVOT also. This will support even older version of SQL server.
Only limitation : you should know the maximum SortOrder value. Below code will work for SortOrder <=20 of any ClassID
SELECT ClassID, ISNULL([1],'') +ISNULL(', '+[2],'')+ISNULL(', '+[3],'')+ISNULL(', '+
[4],'')+ISNULL(', '+[5],'')+ISNULL(', '+[6],'')+ISNULL(', '+[7],'')+ISNULL(', '+
[8],'')+ISNULL(', '+[9],'')+ISNULL(', '+[10],'')+ISNULL(', '+[11],'')+ISNULL(', '+
[12],'')+ISNULL(', '+[13],'')+ISNULL(', '+[14],'')+ISNULL(', '+[15],'')+ISNULL(', '+
[16],'')+ISNULL(', '+[17],'')+ISNULL(', '+[18],'')+ISNULL(', '+[19],'')+ISNULL(', '+
[20],'') AS StudentName
FROM
(SELECT SortOrder,ClassID,StudentName
FROM [Table1] A
) AS SourceTable
PIVOT
(
MAX(StudentName)
FOR SortOrder IN ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12],[13],[14],[15],[16],[17],[18],[19],[20])
) AS PivotTable
I am creating a web app in which I have a requirement where I want to display a column value as a header
Example
SELECT Name, Leave
FROM tblUser
INNER JOIN tblLeaveMaster ON tblUser.EmployeeID = tblLeaveMaster.EmployeeID
From that query, I get these results:
Name Leave
---------------
Test1 5
Test2 10
test3 2
Now I want to get these values as
Test1 Test2 Test3
-----------------
5 10 2
How can I achieve this?
You can try using pivot
select pv.* from
(SELECT Name,Leave
FROM tblUser INNER JOIN tblLeaveMaster ON tblUser.EmployeeID=tblLeaveMaster.EmployeeID
)X
pivot
(max(leave) for name in ([Test1],[Test2],[Test3])) as pv
For Dynamic PIVOT
declare #sql varchar(max)='',#col_list varchar(8000)=''
set #col_list = (select distinct quotename([Name])+',' from (SELECT Name,Leave
FROM tblUser INNER JOIN tblLeaveMaster ON tblUser.EmployeeID=tblLeaveMaster.EmployeeID
)X
for xml path(''))
set #col_list = left (#col_list,len(#col_list)-1)
set #sql = 'select '+#col_list+' from
(SELECT Name,Leave
FROM tblUser INNER JOIN tblLeaveMaster ON tblUser.EmployeeID=tblLeaveMaster.EmployeeID
)X
pivot (max([Leave]) for [Name] in ('+#col_list+'))pv'
exec(#sql)
try by using case when
select max( case when name='Test1' then Leave end) as test1,
max( case when name='Test2' then Leave end) as test2,
max( case when name='Test3' then Leave end) as test3 from
tblUser INNER JOIN tblLeaveMaster
ON tblUser.EmployeeID=tblLeaveMaster.EmployeeID
You can try to use condition aggregate function. CASE WHEN with MAX or MIN
SELECT
MAX(CASE WHEN Name = 'Test1' THEN Leave END) Test1,
MAX(CASE WHEN Name = 'Test2' THEN Leave END) Test2,
MAX(CASE WHEN Name = 'Test3' THEN Leave END) Test3
FROM tblUser
INNER JOIN tblLeaveMaster ON tblUser.EmployeeID=tblLeaveMaster.EmployeeID
EDIT
If your column want to create dynamic you can try to use Dynamic PIVOT
create your SQL statement and make condition aggregate function by connect SQL string. then use execute it Dynamically.
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
SET #cols = STUFF((SELECT distinct ', MAX(CASE WHEN Name = ''' + Name+''' THEN Leave END) ' + QUOTENAME(Name)
FROM tblUser
INNER JOIN tblLeaveMaster ON tblUser.EmployeeID=tblLeaveMaster.EmployeeID
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #query= 'SELECT '+ #cols+'
FROM tblUser
INNER JOIN tblLeaveMaster ON tblUser.EmployeeID=tblLeaveMaster.EmployeeID'
execute(#query)
sqlfiddle
You can find your result from the query as shown below. Here I have taken your query output in a temporary table.
Create table #finalData(ColName Varchar(30), Leave INT)
INSERT INTO #finalData Values('Test1', 5),('Test2', 10),('Test3', 2)
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(c.ColName)
FROM #finalData c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT ' + #cols + ' from
(
select ColName
, Leave
from #finalData
) x
pivot
(
max(Leave)
for ColName in (' + #cols + ')
) p '
execute(#query)
DROP TABLE #finalData
Hope this will help you.
The output is as shown below
Test1 Test2 Test3
5 10 2
select pv.* from
(SELECT Name,Leave
FROM tblUser INNER JOIN tblLeaveMaster ON tblUser.EmployeeID=tblLeaveMaster.EmployeeID
)X
pivot
(max(leave) for name in ([Test1],[Test2],[Test3])) as pv
Note:- this work for me but how to use where condition base on dropdown list selected value.
eg: if wants to show only 2020 and not 2019.
I tried to convert the (null) values with 0 (zeros) output in PIVOT function but have no success.
Below is the table and the syntax I've tried:
SELECT
CLASS,
[AZ],
[CA],
[TX]
FROM #TEMP
PIVOT (SUM(DATA)
FOR STATE IN ([AZ], [CA], [TX])) AS PVT
ORDER BY CLASS
CLASS AZ CA TX
RICE 10 4 (null)
COIN 30 3 2
VEGIE (null) (null) 9
I tried to use the ISNULL but did not work.
PIVOT SUM(ISNULL(DATA,0)) AS QTY
What syntax do I need to use?
SELECT CLASS,
isnull([AZ],0),
isnull([CA],0),
isnull([TX],0)
FROM #TEMP
PIVOT (SUM(DATA)
FOR STATE IN ([AZ], [CA], [TX])) AS PVT
ORDER BY CLASS
If you have a situation where you are using dynamic columns in your pivot statement you could use the following:
DECLARE #cols NVARCHAR(MAX)
DECLARE #colsWithNoNulls NVARCHAR(MAX)
DECLARE #query NVARCHAR(MAX)
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(Name)
FROM Hospital
WHERE Active = 1 AND StateId IS NOT NULL
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #colsWithNoNulls = STUFF(
(
SELECT distinct ',ISNULL(' + QUOTENAME(Name) + ', ''No'') ' + QUOTENAME(Name)
FROM Hospital
WHERE Active = 1 AND StateId IS NOT NULL
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
EXEC ('
SELECT Clinician, ' + #colsWithNoNulls + '
FROM
(
SELECT DISTINCT p.FullName AS Clinician, h.Name, CASE WHEN phl.personhospitalloginid IS NOT NULL THEN ''Yes'' ELSE ''No'' END AS HasLogin
FROM Person p
INNER JOIN personlicense pl ON pl.personid = p.personid
INNER JOIN LicenseType lt on lt.licensetypeid = pl.licensetypeid
INNER JOIN licensetypegroup ltg ON ltg.licensetypegroupid = lt.licensetypegroupid
INNER JOIN Hospital h ON h.StateId = pl.StateId
LEFT JOIN PersonHospitalLogin phl ON phl.personid = p.personid AND phl.HospitalId = h.hospitalid
WHERE ltg.Name = ''RN'' AND
pl.licenseactivestatusid = 2 AND
h.Active = 1 AND
h.StateId IS NOT NULL
) AS Results
PIVOT
(
MAX(HasLogin)
FOR Name IN (' + #cols + ')
) p
')
You cannot place the IsNull() until after the data is selected so you will place the IsNull() around the final value in the SELECT:
SELECT CLASS,
IsNull([AZ], 0) as [AZ],
IsNull([CA], 0) as [CA],
IsNull([TX], 0) as [TX]
FROM #TEMP
PIVOT
(
SUM(DATA)
FOR STATE IN ([AZ], [CA], [TX])
) AS PVT
ORDER BY CLASS
Sometimes it's better to think like a parser, like T-SQL parser. While executing the statement, parser does not have any value in Pivot section and you can't have any check expression in that section. By the way, you can simply use this:
SELECT CLASS
, IsNull([AZ], 0)
, IsNull([CA], 0)
, IsNull([TX], 0)
FROM #TEMP
PIVOT (
SUM(DATA)
FOR STATE IN (
[AZ]
, [CA]
, [TX]
)
) AS PVT
ORDER BY CLASS
You have to account for all values in the pivot set. you can accomplish this using a cartesian product.
select pivoted.*
from (
select cartesian.key1, cartesian.key2, isnull(relationship.[value],'nullvalue') as [value]
from (
select k1.key1, k2.key2
from ( select distinct key1 from relationship) k1
,( select distinct key2 from relationship) k2
) cartesian
left outer join relationship on relationship.key1 = cartesian.key1 and relationship.key2 = carterisan.key2
) data
pivot (
max(data.value) for ([key2_v1], [key2_v2], [key2_v3], ...)
) pivoted
To modify the results under pivot, you can put the columns in the selected fields and then modify them accordingly. May be you can use DECODE for the columns you have built using pivot function.
Kranti A
I have encountered a similar problem. The root cause is that (use your scenario for my case), in the #temp table, there is no record for:
a. CLASS=RICE and STATE=TX
b. CLASS=VEGIE and (STATE=AZ or STATE=CA)
So, when MSSQL does pivot for no record, MSSQL always shows NULL for MAX, SUM, ... (aggregate functions).
None of above solutions (IsNull([AZ], 0)) works for me, but I do get ideas from these solutions.
Sorry, it really depends on the #TEMP table. I can only provide some suggestions.
Make sure #TEMP table have records for below condition, even Data is null.
a. CLASS=RICE and STATE=TX
b. CLASS=VEGIE and (STATE=AZ or STATE=CA)
You may need to use cartesian product: select A.*, B.* from A, B
In the select query for #temp, if you need to join any table with WHERE, then would better put where inside another sub select query. (Goal is 1.)
Use isnull(DATA, 0) in #TEMP table.
Before pivot, make sure you have achieved Goal 1.
I can't give an answer to the original question, since there is no enough info for #temp table. I have pasted my code as example here.
SELECT * FROM (
SELECT eeee.id as enterprise_id
, eeee.name AS enterprise_name
, eeee.indicator_name
, CONVERT(varchar(12) , isnull(eid.[date],'2019-12-01') , 23) AS data_date
, isnull(eid.value,0) AS indicator_value
FROM (select ei.id as indicator_id, ei.name as indicator_name, e.* FROM tbl_enterprise_indicator ei, tbl_enterprise e) eeee
LEFT JOIN (select * from tbl_enterprise_indicator_data WHERE [date]='2020-01-01') eid
ON eeee.id = eid.enterprise_id and eeee.indicator_id = enterprise_indicator_id
) AS P
PIVOT
(
SUM(P.indicator_value) FOR P.indicator_name IN(TX,CA)
) AS T
Table 1:
Id | Name
1 | Example1
2 | Example2
Table 2:
Id | Table1_IDs
1 | 1,2
2 | 2
I want to select table1 from table2 using table1_IDs like:
select *
from table1
where id in (select t.table1_IDs from table2 t)
You can build query string end then use sp_executesql stored procedure to run it. Something like this (not tested)
declare #ids varchar(2000)
select #ids = coalesce(#ids + ',', '') + convert(varchar(100), table1_IDs)
from table2
declare #query nvarchar(2000) = 'select * from table1 where id in (' + #ids + ')'
execute sp_executesql #query
But this is not good idea to use coma separated values becaouse it decrease perfomance. Try to refactor yours tables.
Any other options with same structure?
select * from table1 ta join
(
SELECT id,
LTRIM(RTRIM(m.n.value('.[1]','varchar(8000)'))) AS Certs
from (
SELECT id,CAST('<XMLRoot><RowData>' + REPLACE(value,',','</RowData><RowData>') + '</RowData></XMLRoot>' AS XML) AS x
FROM table2
) t
CROSS APPLY x.nodes('/XMLRoot/RowData')m(n)) b on b.id=ta.id
break table2 comma delemeted into rows and use with Table1 in Join.
I have a table for Profiles stores profile properties values in row style, ex:
[ProfileID] [PropertyDefinitionID] [PropertyValue]
1 6 Jone
1 7 Smith
1 8 Mr
1 3 50000
and another table for property definitions :
[PropertyDefinitionID] [PropertyName]
6 FirstName
7 LastName
8 Prefix
3 Salary
How to use PIVOT or any other way to show it in this way:
[ProfileID] [FirstName] [LastName] [Salary]
1 Jone Smith 5000
It's easy to do this without PIVOT keyword, just by grouping
select
P.ProfileID,
min(case when PD.PropertyName = 'FirstName' then P.PropertyValue else null end) as FirstName,
min(case when PD.PropertyName = 'LastName' then P.PropertyValue else null end) as LastName,
min(case when PD.PropertyName = 'Salary' then P.PropertyValue else null end) as Salary
from Profiles as P
left outer join PropertyDefinitions as PD on PD.PropertyDefinitionID = P.PropertyDefinitionID
group by P.ProfileID
you can also do this with PIVOT keyword
select
*
from
(
select P.ProfileID, P.PropertyValue, PD.PropertyName
from Profiles as P
left outer join PropertyDefinitions as PD on PD.PropertyDefinitionID = P.PropertyDefinitionID
) as P
pivot
(
min(P.PropertyValue)
for P.PropertyName in ([FirstName], [LastName], [Salary])
) as PIV
UPDATE: For dynamic number of properties - take a look at Increment value in SQL SELECT statement
It looks like you might have an unknown number of PropertyName's that you need to turn into columns. If that is the case, then you can use dynamic sql to generate the result:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(PropertyName)
from propertydefinitions
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT profileid, ' + #cols + ' from
(
select p.profileid,
p.propertyvalue,
d.propertyname
from profiles p
left join propertydefinitions d
on p.PropertyDefinitionID = d.PropertyDefinitionID
) x
pivot
(
max(propertyvalue)
for propertyname in (' + #cols + ')
) p '
execute(#query)
See SQL Fiddle with Demo.