How do i join the rows into dynamic columns in this case? [duplicate] - sql

This question already has an answer here:
Pivot Dynamic Columns, no Aggregation
(1 answer)
Closed 9 years ago.
I'm sorry to post this, what i feel, almost duplicate. But i've tried the solutions i've found but haven't gotten it to work in my solution :(
This is how the SQL looks before i FUBARed it. This returns the data in a pretty good format. But i get duplicate rows with the same data except that the questions and answers are changed. I'd like that the Question would be the column name and the answer it's value.
SELECT c.*, sa.Question, sa.Answer
FROM Customers as c, Surveys s, SurveyAnswers sa
WHERE c.OrderID IN(SELECT id FROM #orders)
AND s.CustomerID = c.id
AND sa.SurveyID = s.ID
My SQL is weak and i got to get this done asap :( The alternative is to do the more heavy lifting in the .net app but i'd be nice to get the data directly
Best regards,
Mikael

You want to use Bluefeet's answer here to accomplish a dynamic column pivot. (I've omitted your #orders filter for brevity):
DECLARE
#cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(sa.Question)
FROM SurveyAnswers sa
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT CustomerID, Name, ' + #cols + ' from
(
select
c.ID as CustomerId,
c.Name,
sa.Question,
sa.Answer
FROM Customers as c
INNER JOIN Surveys s ON s.CustomerID = c.id
INNER JOIN SurveyAnswers sa ON sa.SurveyID = s.ID
) x
pivot
(
min(Answer)
for Question in (' + #cols + ')
) p '
execute(#query);
SqlFiddle here

Related

Restriction to use DECLARE in a SQL view

I have a sql query that I need to put into a view. I have it working as a stored procedure but my requirement is for it to be a View. This means I cant declare dynamic SQL as I have done in my stored procedure.
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(t.Data_Term)
from [UD_FldValues] t
inner join Surgery p
on t.Ud_Form_Id = p.Ud_Form_Id where t.Data_term!=''
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'');
set #query = 'SELECT Tran_Id,Section_Id,SubSection_Id,R_Id,Hospital_Id,Surgeon_Id,Procedure_ICD,Procedure_Details,DtSurgery_TimeIn,DtSurgery_TimeOut,Intra_oper_compl_Id, p.Discharge_Date,Discharge_Status_Id,Entered_By,Entered_Date,p.Status,p.Ud_Form_Id,p.Complication_ICD,p.Complication_Name, Procedure_Name, ' + #cols + ' from
(
select t.Tran_Id
, t.[R_Id]
, p.Ud_Form_Id
,p.Procedure_Name
,p.Section_Id
,p.SubSection_Id
,p.Hospital_Id
,p.Surgeon_Id
,p.Procedure_ICD
,p.Procedure_Details
,p.DtSurgery_TimeIn
,p.DtSurgery_TimeOut
,p.Intra_oper_compl_Id
,p.Discharge_Date
,p.Discharge_Status_Id
,p.Entered_By,
p.Entered_Date
,p.Status
,p.Complication_ICD
,p.[Complication_Name]
, case when (t.Data_Type=''Text'') THEN t.Text_Value Else Convert(varchar(MAx),Num_Value) END as txtvl
,t.Data_term
from Surgery p
left outer join [UD_FldValues] t
on t.Ud_Form_Id = p.Ud_Form_Id and t.Tran_Id=p.Surgery_Id
left outer join UD_Formmap fm ON fm.Ud_Form_Id = p.Ud_Form_Id
where fm.module_id = 1
) x
pivot
(
min(txtvl)
for Data_Term in (' + #cols + ')
) p '
exec(#query)
Please help me find an alternate way, but not store procedures. I need it in view
for a view it needs to be a single select statement,
your #cols statement returns a list of columns, this is used twice within your main select statement. so replacing #cols in your main query with the query that populates this should work.
set #query and then exec(#query) isnt necessary either so you just have to tidy up and make sure the right number of brackets are in place.
and it should look something like this.
SELECT Tran_Id
,Section_Id
,SubSection_Id
,R_Id,Hospital_Id
,Surgeon_Id,Procedure_ICD
,Procedure_Details
,DtSurgery_TimeIn
,DtSurgery_TimeOut
,Intra_oper_compl_Id
,p.Discharge_Date
,Discharge_Status_Id
,Entered_By,Entered_Date
,p.Status,p.Ud_Form_Id
,p.Complication_ICD
,p.Complication_Name
,Procedure_Name,
(select STUFF((SELECT distinct ',' + QUOTENAME(t.Data_Term)
from [UD_FldValues] t
inner join Surgery p
on t.Ud_Form_Id = p.Ud_Form_Id where t.Data_term!=''
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'');))
from
(
select t.Tran_Id
, t.[R_Id]
, p.Ud_Form_Id
,p.Procedure_Name
,p.Section_Id
,p.SubSection_Id
,p.Hospital_Id
,p.Surgeon_Id
,p.Procedure_ICD
,p.Procedure_Details
,p.DtSurgery_TimeIn
,p.DtSurgery_TimeOut
,p.Intra_oper_compl_Id
,p.Discharge_Date
,p.Discharge_Status_Id
,p.Entered_By,
p.Entered_Date
,p.Status
,p.Complication_ICD
,p.[Complication_Name]
, case when (t.Data_Type=''Text'') THEN t.Text_Value Else Convert(varchar(MAx),Num_Value) END as txtvl
,t.Data_term
from Surgery p
left outer join [UD_FldValues] t
on t.Ud_Form_Id = p.Ud_Form_Id and t.Tran_Id=p.Surgery_Id
left outer join UD_Formmap fm ON fm.Ud_Form_Id = p.Ud_Form_Id
where fm.module_id = 1
) x
pivot
(
min(txtvl)
for Data_Term in (
select STUFF((SELECT distinct ',' + QUOTENAME(t.Data_Term)
from [UD_FldValues] t
inner join Surgery p
on t.Ud_Form_Id = p.Ud_Form_Id where t.Data_term!=''
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'');)
) p

SQL Pivot Convert Null to 0 [duplicate]

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

Change Rows to Columns in SQL Server

I've been working on this query for an hour and half but I can't get it done,
First, this is my query:
SELECT
Questions, PossibleAnswer,
((COUNT(PossibleAnswer) + 0.0) / 10 ) * 100 AS Percentage
FROM
(SELECT
A.AnswerID, B.Questions, B.QuestionID, C.PossibleAnswer
FROM
TblSurveyCustomerAnswers A
INNER JOIN
TblSurveyQuestion B ON A.QuestionID = B.QuestionID
INNER JOIN
TblSurveyAnswer C ON A.AnswerID = C.AnswerID
WHERE
A.CustomerID = 1) AS SOURCE
GROUP BY
Questions, PossibleAnswer
The result is below:
Now, I want the rows for column name PossibleAnswer to be converted in columns, so I did a research and found the PIVOT command (I need dynamic since it's a possible answers field) and this is my code
DECLARE #DynamicPivotQuery AS NVARCHAR(MAX)
DECLARE #ColumnName AS NVARCHAR(MAX)
--Get distinct values of the PIVOT Column
SELECT #ColumnName= ISNULL(#ColumnName + ',','')
+ QUOTENAME(PossibleAnswer)
FROM
(
SELECT DISTINCT X.*
FROM
(
SELECT Questions,PossibleAnswer, ((COUNT(PossibleAnswer) + 0.0) / 10 ) * 100 AS Percentage
FROM
(
SELECT A.AnswerID,B.Questions, B.QuestionID, C.PossibleAnswer
FROM TblSurveyCustomerAnswers A
INNER JOIN TblSurveyQuestion B
ON A.QuestionID = B.QuestionID
INNER JOIN TblSurveyAnswer C
ON A.AnswerID = C.AnswerID
WHERE A.CustomerID = 1
) AS SOURCE
GROUP BY Questions, PossibleAnswer
) X
) AS B
--Prepare the PIVOT query using the dynamic
SET #DynamicPivotQuery =
'SELECT Questions, ' + #ColumnName + '
FROM TblSurveyCustomerAnswers A
INNER JOIN TblSurveyQuestion B
ON A.QuestionID = B.QuestionID
PIVOT(Max(Questions)
FOR PossibleAnswer IN (' + #ColumnName + ')) AS PVTTable'
--Execute the Dynamic Pivot Query
EXEC sp_executesql #DynamicPivotQuery
And I can't get the pivot work, need help. I'm stuck. See this error:
In general for questions like these you should provide sample data, table definitions and expected output so people can take your script, fiddle with it and produce something that works. See How to post a T-SQL question on a public forum for one way to do this.
Since it is hard to look at a dynamic script, not having the table structures, and point at what your problem is, let me give you the following advice:
Instead of taking your big query that produces the output and form queries around that big query, first insert the output of that query into a temporary table. You can do this by placing an INTO #temp_table clause after the SELECT clause. This creates a new temporary table #temp_table containing the output of the query.
SELECT --your select columns
INTO #p_in -- creates a temporary table #p_in that contains the output
FROM --the rest of your query
Determine the pivot columns based on the newly created temporary table. It'll be a lot more conscise and easier to spot errors
Write your Dynamic SQL using the temporary table (again it'll be a lot more conscise and easier to spot errors)
Don't forget to DROP the temporary table after executing the dynamic SQL.
I just try to solved problem without temporary table. You may edit query as your requirement.
--For PIVOT column
DECLARE #ColumnName AS NVARCHAR(MAX)
SELECT #ColumnName ''''+ PossibleAnswer + '''' + ' , ' + #ColumnName
FROM
(
SELECT
DISTINCT PossibleAnswer
FROM
(
SELECT
A.AnswerID, B.Questions, B.QuestionID, C.PossibleAnswer
FROM
TblSurveyCustomerAnswers A
INNER JOIN
TblSurveyQuestion B ON A.QuestionID = B.QuestionID
INNER JOIN
TblSurveyAnswer C ON A.AnswerID = C.AnswerID
WHERE
A.CustomerID = 1
) AS SOURCE
)B
--For removing last comma
IF #ColumnName != ''
BEGIN
SET #ColumnName = SUBSTRING(#ColumnName, 1, LEN(#ColumnName)-1)
END
-- Make result
SELECT *
FROM
(
SELECT Questions,PossibleAnswer, ((COUNT(PossibleAnswer) + 0.0) / 10 ) * 100 AS Percentage
FROM
(
SELECT A.AnswerID,B.Questions, B.QuestionID, C.PossibleAnswer
FROM TblSurveyCustomerAnswers A
INNER JOIN TblSurveyQuestion B
ON A.QuestionID = B.QuestionID
INNER JOIN TblSurveyAnswer C
ON A.AnswerID = C.AnswerID
WHERE A.CustomerID = 1
) AS SOURCE
GROUP BY Questions, PossibleAnswer
)C
PIVOT
( Max(Questions)
FOR PossibleAnswer IN (#ColumnName)
) AS PVTTable

Dynamic Pivot Columns with related tables in Sql Server

I am attempting to use a Pivot to dynamically create columns and am not getting the proper results I am looking for. I need to show all possible answers in its own column for each question (row), and there are multiple questions in a course. How can I generate each possible answer as a column name, and filter by courseId and the questionId for each row? The number of possible answers varies based on the question. Should this be done via cursor instead of pivot?
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX),
#courseID float(24) = 1
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(questionID)
FROM answers
INNER JOIN questions ON questions.questionID = answers.questionID
WHERE questions.courseId = #courseID
order by 1
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '');
set #query = 'SELECT * from
(
select Q.courseId, C.courseName, Q.question, Q.questionID, A.answer
from questions Q
inner join courses C ON Q.courseId = C.courseId
inner join answers A ON A.questionID = Q.questionID
where (Q.courseId = ''' + Str(#courseID) + ''')
) x
pivot
(
max(answer)
for questionId in (' + #cols + ')
) AS p'
execute(#query)
When running the above I am getting:
[1],[10],[11],[12],[13],[14],[15],[16],[2],[3],[4],[5],[6],[7],[8],[9]
As the generated columns which are the questionIds and not 'Answer 1, Answer 2 ... etc', and they are not displaying in order.
For the sake of brevity here are the related columns in the database tables:
Courses
courseId
Questions
questionId question courseId
Answers
AnswerId questionId answer
Any help is appreciated.
You didn't provide a many details on your table structure, but if you want the answers as the columns i.e. Answer1, Answer2, etc then you need to create the columns using row_number() against the number of answers per question similar to the following:
SET #cols = STUFF((SELECT ',' + QUOTENAME('Answer'+cast(seq as varchar(10)))
FROM
(
select row_number() over(partition by q.questionid
order by a.answer) seq
from answers a
INNER JOIN questions q
ON q.questionID = a.questionID
WHERE q.courseId = #courseID
) d
group by seq
order by seq
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '');
This creates a sequence for each answer per question and then that sequence number is used to create the new columns names. Then you will alter your PIVOT code to:
set #query = 'SELECT *
from
(
select Q.courseId, C.className, Q.question, Q.questionID, A.answer,
''Answer''+cast(row_number() over(partition by q.questionid
order by a.answer) as varchar(10)) seq
from questions Q
inner join classes C
ON Q.courseId = C.courseId
inner join answers A
ON A.questionID = Q.questionID
where (Q.courseId = ''' + Str(#courseID) + ''')
) x
pivot
(
max(answer)
for seq in (' + #cols + ')
) AS p'
execute(#query)

Count Distinct Values FROM Column

I have Written a query which returns multiple column.Out of which One Column contains repetitive entries.
FAULT_SHORT_NAME
ATM DOWN DUE TO LINK PROBLEM
ATM DOWN DUE TO LINK PROBLEM
ALL CASSETTES FAULTED
ALL CASSETTES FAULTED
ATM IS MARK DOWN
ATM IS MARK DOWN
Now I want to Modify My query in Such a way that it will show me Value Count as
ATM DOWN DUE TO LINK PROBLEM ALL CASSETTES FAULTED ATM IS MARK DOWN
2 2 2
There Can be Different "FAULT_SHORT_NAME" Values so Cant Hard Code them.My Original Query is
Select * From ATMStatus S Left Join ATM A on S.ATM=A.Code
Left Join EventMsg E On S.Fault=E.Code
Where A.ATMStatus=0 AND S.TicketBooked <> 0
FAULT_SHORT_NAME is Column of Table "EventMsg"
It looks like you want a PIVOT since you want the values as columns instead of rows. there are two ways to do this either a Static or dynamic pivot.
Static Pivot, you hard-code the values of the columns:
SELECT *
FROM
(
Select *
From ATMStatus S
Left Join ATM A
on S.ATM=A.Code
Left Join EventMsg E
On S.Fault=E.Code
Where A.ATMStatus=0
AND S.TicketBooked <> 0
) x
PIVOT
(
count(*)
for FAULT_SHORT_NAME in ([ATM DOWN DUE TO LINK PROBLEM],
[ALL CASSETTES FAULTED], [ATM IS MARK DOWN])
) p
Dynamic Pivot, the columns are generated at run-time:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(FAULT_SHORT_NAME)
from EventMsg
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query
= 'SELECT ' + #cols + ' from
(
Select *
From ATMStatus S
Left Join ATM A
on S.ATM=A.Code
Left Join EventMsg E
On S.Fault=E.Code
Where A.ATMStatus=0
AND S.TicketBooked <> 0
) x
pivot
(
count(*)
for FAULT_SHORT_NAME in(' + #cols + ')
) p '
execute(#query)
Both will produce the same results. If you provide additional details about the tables and some sample data, then I could provide a more exact example.