sql >> Database >  >> RDS >> Sqlserver

SQL kruistabquery

SELECT MIN(ro.OptionText) RowOptionText, MIN(co.OptionText) RowOptionText, COUNT(ca.AnswerID) AnswerCount
FROM tblQuestions rq 
CROSS JOIN tblQuestions cq 
JOIN tblOptions ro ON rq.QuestionID = ro.QuestionID
JOIN tblOptions co ON cq.QuestionID = co.QuestionID
LEFT JOIN tblAnswers ra ON ra.OptionID = ro.OptionID
LEFT JOIN tblAnswers ca ON ca.OptionID = co.OptionID AND ca.UserID = ra.UserID
WHERE rq.questionText = 'Gender'
AND cq.questionText = 'How happy are you?'
GROUP BY ro.OptionID, co.OptionID
ORDER BY ro.OptionID, co.OptionID

Dit moet op zijn minst in de buurt komen van wat u vroeg. Om dit in een draaipunt om te zetten, is dynamische SQL vereist, aangezien SQL Server vereist dat u de werkelijke waarde opgeeft die in een kolom wordt omgezet.

We voegen de vragen kruiselings samen en beperken de resultaten van elk van die vraagverwijzingen tot de enkele vraag voor respectievelijk de rijwaarden en kolomwaarden. Vervolgens voegen we de optiewaarden toe aan de respectieve vraagreferentie. We gebruiken LEFT JOIN voor de antwoorden voor het geval de gebruiker niet op alle vragen heeft gereageerd. En we voegen de antwoorden per UserID samen, zodat we de rijvraag en kolomvraag voor elke gebruiker matchen. De MIN op de optietekst is omdat we gegroepeerd en geordend hebben op OptionID om overeen te komen met uw weergegeven volgorde.

EDIT:Hier is een SQLFiddle

Voor wat het waard is, uw query is ingewikkeld omdat u het ontwerppatroon Entity-Attribute-Value gebruikt. Heel wat SQL Server-experts beschouwen dat patroon als problematisch en indien mogelijk te vermijden. Zie bijvoorbeeld https:/ /www.simple-talk.com/sql/t-sql-programming/avoiding-the-eav-of-destruction/ .

EDIT 2:Aangezien je mijn antwoord hebt geaccepteerd, is hier de dynamische SQL-pivotoplossing :) SQLFiddle

DECLARE @SqlCmd NVARCHAR(MAX)

SELECT @SqlCmd = N'SELECT RowOptionText, ' + STUFF(
    (SELECT ', ' + QUOTENAME(o.OptionID) + ' AS ' + QUOTENAME(o.OptionText)
    FROM tblOptions o 
    WHERE o.QuestionID = cq.QuestionID
    FOR XML PATH ('')), 1, 2, '') + ', RowTotal AS [Row Total]
FROM (
    SELECT ro.OptionID RowOptionID, ro.OptionText RowOptionText, co.OptionID ColOptionID,
       ca.UserID, COUNT(ca.UserID) OVER (PARTITION BY ra.OptionID) AS RowTotal
    FROM tblOptions ro
    JOIN tblOptions co ON ro.QuestionID = ' + CAST(rq.QuestionID AS VARCHAR(10)) + 
    ' AND co.QuestionID = ' + CAST(cq.QuestionID AS VARCHAR(10)) + '
    LEFT JOIN tblAnswers ra ON ra.OptionID = ro.OptionID
    LEFT JOIN tblAnswers ca ON ca.OptionID = co.OptionID AND ca.UserID = ra.UserID
    UNION ALL 
    SELECT 999999, ''Column Total'' RowOptionText, co.OptionID ColOptionID,
       ca.UserID, COUNT(ca.UserID) OVER () AS RowTotal
    FROM tblOptions ro
    JOIN tblOptions co ON ro.QuestionID = ' + CAST(rq.QuestionID AS VARCHAR(10)) + 
    ' AND co.QuestionID = ' + CAST(cq.QuestionID AS VARCHAR(10)) + '
    LEFT JOIN tblAnswers ra ON ra.OptionID = ro.OptionID
    LEFT JOIN tblAnswers ca ON ca.OptionID = co.OptionID AND ca.UserID = ra.UserID
) t
PIVOT (COUNT(UserID) FOR ColOptionID IN (' + STUFF(
    (SELECT ', ' + QUOTENAME(o.OptionID) 
    FROM tblOptions o 
    WHERE o.QuestionID = cq.QuestionID
    FOR XML PATH ('')), 1, 2, '') + ')) p
ORDER BY RowOptionID'
FROM tblQuestions rq 
CROSS JOIN tblQuestions cq 
WHERE rq.questionText = 'Gender' 
AND cq.questionText = 'How happy are you?'

EXEC sp_executesql @SqlCmd


  1. Is er in MS SQL Server een manier om een ​​kolom die als teller wordt gebruikt atomair te verhogen?

  2. Invoegen met behulp van de Where-clausule

  3. Bereken het totale aantal werkdagen tussen twee datums

  4. Hoe een getalvariabele declareren waar ik de telling van de tabel in mijn lus kan opslaan?