I am trying to create a pivot table that lists the time frames (responseText) as columns with the average answer for each one under that particular column based on the question asked. I am trying to do this with a PIVOT function - to no avail.
I have the following table
I first create a temp table and populate it with values from a stored procedure, then I am building a dynamic query
CREATE TABLE #Data
(
QuestionText varchar(400),
ResponseText varchar(200),
AverageAnswer float
)
INSERT INTO #Data
EXECUTE AverageDemographicGroupAnswer #GroupID, #Survey, #Demographic
--SELECT * FROM #Data
DECLARE #PivotQuery NVARCHAR(MAX)
DECLARE #Cols varchar(max)
SET #Cols = STUFF((SELECT distinct ',' + QUOTENAME(ResponseText,'[]') FROM dbo.SurveyResponses WHERE QuestionID = #Demographic FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),1,1,'')
--SELECT #Cols
SET #PivotQuery = 'SELECT QuestionText, '+ #Cols +' FROM
(
SELECT QuestionText, ResponseText, AverageAnswer FROM #Data
)x
PIVOT
( AverageAnswer FOR ResponseText in (' + #Cols + ')
)p'
--select #PivotQuery
EXECUTE(#PivotQuery)
DROP TABLE #Data
Which produces this query
SELECT QuestionText, [At least 1 year but less than 3 years],[At least 3 years but less than 5 years],[Less than 1 year],[More than 5 years] FROM (SELECT QuestionText, AverageAnswer, ResponseText FROM #Data) AS SourceTable
PIVOT( AverageAnswer FOR ResponseText in ([At least 1 year but less than 3 years],[At least 3 years but less than 5 years],[Less than 1 year],[More than 5 years])) AS PivotTable
but it is returning this error
Incorrect syntax near the keyword 'FOR'.
This is my first time creating a pivot and I am not sure what is wrong
Related
I have a dynamic pivot table in ms sql server.
The column names are dates and appear in random order.
I would like to order the dates(columns) so they appear chronologically, the oldest being first and the most recent being last so I can trend the data by date.
I cannot figure out how to order the columns.
The date variable name is chartaccessdate.
Any help is appreciated.
Here is my code
declare #columnnames nvarchar(max)=''
declare #sql nvarchar(max)=''
select #columnnames += quotename(chartaccessdate) + ','
from (select distinct chartaccessdate from #temp ) as s
set #columnnames=left(#columnnames,len(#columnnames)-1)
set #sql=
'select * from (
select --phase,
unit,OccupationCode occode
,OccupationCode , chartaccessdate from #temp
) as a
Pivot
( count(OccupationCode)
FOR [chartaccessdate] IN (
'+#columnnames +
'
-- "2021-08-23"
--,"2021-08-24"
--,"2021-08-21"
--,"2021-08-22"
) )as pvt '
execute sp_executesql #sql
Rather than SELECT DISTINCT, one quick option would be:
select top 1000 chartaccessdate from #temp group by chartaccessdate order by 1
I have two tables. I write a query. Date is dynamic. I can select any date. `
select a.MP,a.CP,a.Frequency,a.Time,CONVERT(varchar(12),b.date,101) as
EntryDate,b.actualtime from mpcp a, DailyData b
where a.UserID=1 and a.MpCpId=b.MpCpId and
CONVERT(varchar(12),b.EntryDate,101) between
CONVERT(varchar(12),GETDATE()-5,101) and
CONVERT(varchar(12),GETDATE()+25,101)`
Output
But i want output like
Assuming that i am storing your result in one temp table and imaging data i created one data for your requirement
try this one whether it is useful or not
create table #piv
(
mp varchar(10),
cp varchar(10),
freq varchar(10),
time int,
entryd date,
acuralize int
)
insert into #piv values
('don','asper','da',30,getdate(),0),
('dwm','donl','da',10,getdate(),3),
('qar','qpr','da',15,getdate(),5),
('qar','qpr','da',15,'01-16-17',5),
('qar','qpr','da',15,'01-15-17',5),
('qar','qpr','da',15,'01-16-17',5)
SELECT * FROM #piv
Declare #SQL varchar(max) = Stuff((Select Distinct ',' + QuoteName(entryd) From #piv Order by 1 For XML Path('')),1,1,'')
Select #SQL = '
Select *,' + #SQL + '
From #piv
Pivot (max(time) For [entryd] in (' + #SQL + ') ) p'
Exec(#SQL);
You can create a dynamic pivot. A good example is available on this link.
Good Luck !
DECLARE #cols VARCHAR(max),#sql VARCHAR(max)
SELECT #cols=ISNULL(#cols+',[','[')+ CONVERT(VARCHAR,a.EntryDate,101)+']'
FROM mpcp a, DailyData b
where a.UserID=1 and a.MpCpId=b.MpCpId and DATEDIFF(d,GETDATE(),b.EntryDate) BETWEEN -5 AND 25
GROUP BY a.time
SET #sql='
SELECT * FROM (
SELECT a.MP,a.CP,a.Frequency,a.Time,CONVERT(varchar(12),b.date,101) AS EntryDate,b.actualtime
FROM mpcp a, DailyData b
WHERE a.UserID=1 and a.MpCpId=b.MpCpId and where a.UserID=1 and a.MpCpId=b.MpCpId and DATEDIFF(d,GETDATE(),b.EntryDate) BETWEEN -5 AND 25
) AS t
PIVOT (MAX(actualtime) FOR EntryDate IN ('+#cols+') )'
EXEC(#sql)
I have a dataset like this
[student][datetime][dictionary][value]
[1234 ][datetime][1 ][a ]
[1234 ][datetime][2 ][b ]
I want to create a dataset like this
[student][1 ][2 ]
[1234 ][a ][b ]
I understand that SQL Server pivots are a way to accomplish this. I've written the below code to attempt to pivot the data however, it's naturally not grouped.
So my results end up like this:
[student][datetime][1 ][2 ]
[1234 ][datetime][null ][b ]
[1234 ][datetime][a ][null ]
How can I best group these? I don't care about having the other source columns such as datetime in the final dataset, only the matrix of dictionaries and values
Thank you
DECLARE #columns AS VARCHAR(MAX);
DECLARE #sql AS VARCHAR(MAX);
SELECT #columns = substring((Select DISTINCT ',' + QUOTENAME(dictionary) FROM syuservalues FOR XML PATH ('')),2, 1000);
SELECT #sql =
'SELECT Pivoted.*
FROM syuservalues
PIVOT
( MAX(value)
FOR dictionary IN( ' + #columns + ' )) as Pivoted where student = 327392'; ' Will eventually want it for all students
EXECUTE(#sql);
a and b is brought to two rows because, you have added [datetime] column for pivot and the [datetime] value for a and b will be different.
SAMPLE TABLE
CREATE TABLE #TEMP([student] INT,[datetime] DATETIME,[dictionary] INT,[value] VARCHAR(30))
INSERT INTO #TEMP
SELECT 1234,'2015-02-01',1,'a'
UNION ALL
SELECT 1234, '2015-02-01',2,'b'
Declare a variable to get columns for pivot dynamically
DECLARE #cols NVARCHAR (MAX)
SELECT #cols = COALESCE (#cols + ',[' + CAST([dictionary] AS VARCHAR(10)) + ']',
'[' + CAST([dictionary] AS VARCHAR(10)) + ']')
FROM
(
SELECT DISTINCT [dictionary]
FROM #TEMP
) PV
ORDER BY CAST([dictionary] AS INT)
Now pivot it. I have written the logic inside.
DECLARE #query NVARCHAR(MAX)
SET #query = '-- Your pivoted result is here
SELECT *
FROM
(
-- Select data before pivot. We are not taking [datetime] column because
-- it will not bring ur expected result
SELECT student,[dictionary],value
FROM #TEMP
) x
PIVOT
(
-- Value in each dynamic column
MIN(value)
-- Tell the columns to pivot
FOR [dictionary] IN (' + #cols + ')
) p
ORDER BY student;'
EXEC SP_EXECUTESQL #query
For this kind of problem, you can just use an arbitrary aggregate function so that you get rid of the nulls. I usually use max because it can be used on both numeric and string data types.
select pivoted.student, [1] = max([1]), [2] = max([2])
from syuservalues
pivot(max(value) for dictionary in(...)) pivoted
group by pivoted.student
I have a one to many relationship between Course and Facilitator. The foreign key is in the Course table. How do I select a facilitator as distinct and have its multiple course IDs as columns next to the facilitator.
SELECT dbo.Facilitator.Fac_ID, dbo.Facilitator.Fac_Name, dbo.Course.Course_ID
FROM dbo.Course
RIGHT JOIN dbo.Facilitator ON dbo.Course.FK_Facilitator = dbo.Facilitator.Fac_ID
order by dbo.Facilitator.Fac_Name asc
returns:
instead, I want:
For pivoting, the column names should be defined. In your case you you do not have such a column name, as per your question. So we create sample column names like 1,2 etc.
First of all, get the column names for pivot
DECLARE #cols NVARCHAR (MAX)
SELECT #cols = COALESCE (#cols + ',[' + COLUMNNAME + ']', '[' + COLUMNNAME + ']')
FROM
(
-- Generates random column names numerically from 1,2 etc
SELECT DISTINCT CAST(ROW_NUMBER() OVER(PARTITION BY ID ORDER BY (SELECT 0))AS VARCHAR(4))COLUMNNAME
FROM #TEMP
) PV
ORDER BY CAST(COLUMNNAME AS INT)
Now pivot the result. I have written the logic inside.
DECLARE #query NVARCHAR(MAX)
SET #query = '-- This outer query forms your pivoted result
SELECT * FROM
(
-- Source data for pivoting
SELECT ID,NAME,VALUE,
CAST(ROW_NUMBER() OVER(PARTITION BY ID ORDER BY (SELECT 0))AS VARCHAR(4)) COLUMNNAME
FROM #TEMP
) x
PIVOT
(
--Defines the values in each dynamic columns
MIN(VALUE)
-- Get the names from the #cols variable to show as column
FOR COLUMNNAME IN (' + #cols + ')
) p
ORDER BY NAME;'
EXEC SP_EXECUTESQL #query
Click here to view result
The SELECT statement below is from a stored procedure I am using to retrieve data which is subsequently sent to JSON:
SELECT *
FROM (
SELECT CAST(DateTimeUTC as SmallDateTime) as [DateTime], DataValue, VariableID
FROM DataValues
WHERE SiteID = #siteID and VariableID BETWEEN 9 and 10 and DateTimeUTC >= DATEADD (day, #pastDays, getdate())
) TableDate
PIVOT (SUM(DataValue) FOR VariableID IN ([9],[10])) PivotTable ORDER BY [DateTime]
What I would like to accomplish is to modify the procedure to accept a column range to pivot. In the above example I am only retrieving values for two variables. What if I want to retrieve VariableID 1 through 10, or 1 through 50? There has to be a way to retrieve 1 through 10 in a way other than:
VariableID IN ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10])
The Using PIVOT and UNPIVOT on MSDN doesn't mention a way to specify a range.
I realize that it is possible to use dynamic SQL in a stored procedure, but given my own knowledge of SQL and the knowledge of those who will have to maintain this long term I am hesitant to introduce additional complexity by using dynamic SQL. There is a conceptually similar question on here, see TSQL Pivot Long List of Columns, and it was answered with a dynamic SQL solution.
Unfortunately, the PIVOT function does not have the ability to generate the list of columns, without using dynamic sql.
So your SQL code will be similar to this:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(VariableID)
from DataValues
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT [DateTime], ' + #cols + ' from
(
SELECT CAST(DateTimeUTC as SmallDateTime) as [DateTime],
DataValue,
VariableID
FROM DataValues
WHERE SiteID = '+cast(#siteID as varchar(10))+'
-- and VariableID BETWEEN 9 and 10 -- remove the variableID filter
and DateTimeUTC >= DATEADD (day, +'cast(#pastDays as varchar(10))+', getdate())
) x
pivot
(
SUM(DataValue)
for VariableID in (' + #cols + ')
) p '
execute(#query)
The key to this is the following code which generates the list of variableIds to become columns:
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(VariableID)
from DataValues
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
This will create the list of all distinct variable id's. This list is then added to the SQL query string to return.