Survey data can be messy. You’ve got responses scattered across dozens or hundreds of rows, multiple choice answers, rating scales, and so on. And the challenge of turning all that raw data into something stakeholders can actually understand. A well-designed survey dashboard can transform those individual responses into a grid that shows patterns instantly. For example, which questions are getting strong agreement, where opinions diverge, and what trends emerge across different respondent groups.
Why Use a Survey Dashboard?
The human brain processes comparative data better in matrix form than in lists. When you see a grid showing five questions with their response distributions side-by-side, you immediately spot which questions have consensus and which show division. Survey dashboards put all the key metrics in one view, eliminating the need to mentally track patterns across multiple reports or tables.
But let’s be clear about what we’re building here. There are plenty of purpose-built survey platforms with polished dashboards, drag-and-drop interfaces, and sophisticated visualizations. Those tools are great for many scenarios. But if you’re a SQL developer working with survey data that’s already in your database, building your own dashboard queries might be all you need.
Sample Data for Examples
Let’s build some examples with an employee engagement survey. This is a common scenario where you need to see patterns across questions and understand how different groups of employees feel.
We’ll use the following data:
-- Create the survey responses table
CREATE TABLE SurveyResponses (
ResponseID INT PRIMARY KEY,
ResponseDate DATE NOT NULL,
Department VARCHAR(50),
Question VARCHAR(200),
Rating VARCHAR(30)
);
-- Populate with sample data
INSERT INTO SurveyResponses (ResponseID, ResponseDate, Department, Question, Rating)
VALUES
(1, '2025-03-10', 'Engineering', 'I feel valued at this company', 'Agree'),
(2, '2025-03-10', 'Engineering', 'I have the tools I need to succeed', 'Strongly Agree'),
(3, '2025-03-10', 'Engineering', 'Communication from leadership is clear', 'Neutral'),
(4, '2025-03-10', 'Engineering', 'Work-life balance is respected', 'Agree'),
(5, '2025-03-11', 'Sales', 'I feel valued at this company', 'Strongly Agree'),
(6, '2025-03-11', 'Sales', 'I have the tools I need to succeed', 'Agree'),
(7, '2025-03-11', 'Sales', 'Communication from leadership is clear', 'Disagree'),
(8, '2025-03-11', 'Sales', 'Work-life balance is respected', 'Neutral'),
(9, '2025-03-12', 'Marketing', 'I feel valued at this company', 'Agree'),
(10, '2025-03-12', 'Marketing', 'I have the tools I need to succeed', 'Neutral'),
(11, '2025-03-12', 'Marketing', 'Communication from leadership is clear', 'Disagree'),
(12, '2025-03-12', 'Marketing', 'Work-life balance is respected', 'Strongly Agree'),
(13, '2025-03-13', 'Engineering', 'I feel valued at this company', 'Strongly Agree'),
(14, '2025-03-13', 'Engineering', 'I have the tools I need to succeed', 'Agree'),
(15, '2025-03-13', 'Engineering', 'Communication from leadership is clear', 'Neutral'),
(16, '2025-03-13', 'Engineering', 'Work-life balance is respected', 'Agree'),
(17, '2025-03-14', 'Sales', 'I feel valued at this company', 'Agree'),
(18, '2025-03-14', 'Sales', 'I have the tools I need to succeed', 'Strongly Agree'),
(19, '2025-03-14', 'Sales', 'Communication from leadership is clear', 'Neutral'),
(20, '2025-03-14', 'Sales', 'Work-life balance is respected', 'Disagree'),
(21, '2025-03-15', 'Marketing', 'I feel valued at this company', 'Neutral'),
(22, '2025-03-15', 'Marketing', 'I have the tools I need to succeed', 'Agree'),
(23, '2025-03-15', 'Marketing', 'Communication from leadership is clear', 'Strongly Disagree'),
(24, '2025-03-15', 'Marketing', 'Work-life balance is respected', 'Agree'),
(25, '2025-03-16', 'Engineering', 'I feel valued at this company', 'Agree'),
(26, '2025-03-16', 'Engineering', 'I have the tools I need to succeed', 'Strongly Agree'),
(27, '2025-03-16', 'Engineering', 'Communication from leadership is clear', 'Agree'),
(28, '2025-03-16', 'Engineering', 'Work-life balance is respected', 'Strongly Agree');
I used SQL Server when putting this together, but most of the examples should work across various RDBMSs with little to no modification.
Basic Response Distribution Matrix
The most fundamental survey dashboard shows questions as rows and rating categories as columns, with response counts in each cell. This gives you an immediate sense of how people answered each question.
SELECT
Question,
SUM(CASE WHEN Rating = 'Strongly Agree' THEN 1 ELSE 0 END) AS [Strongly Agree],
SUM(CASE WHEN Rating = 'Agree' THEN 1 ELSE 0 END) AS Agree,
SUM(CASE WHEN Rating = 'Neutral' THEN 1 ELSE 0 END) AS Neutral,
SUM(CASE WHEN Rating = 'Disagree' THEN 1 ELSE 0 END) AS Disagree,
SUM(CASE WHEN Rating = 'Strongly Disagree' THEN 1 ELSE 0 END) AS [Strongly Disagree],
COUNT(*) AS Total
FROM SurveyResponses
GROUP BY Question
ORDER BY Question;
Result:
Question Strongly Agree Agree Neutral Disagree Strongly Disagree Total
-------------------------------------- -------------- ----- ------- -------- ----------------- -----
Communication from leadership is clear 0 1 3 2 1 7
I feel valued at this company 2 4 1 0 0 7
I have the tools I need to succeed 3 3 1 0 0 7
Work-life balance is respected 2 3 1 1 0 7
This matrix immediately shows which questions have strong positive sentiment and which ones have more mixed or negative responses. If you see most responses clustering in “Strongly Agree” and “Agree” for one question but spread across all categories for another, you know where you have consensus and where you have problems.
Percentage Distribution Matrix
Raw counts can be useful, but percentages make it easier to compare questions that might have different response volumes. This is especially important if some questions were optional or if you’re comparing across time periods with different participation rates.
WITH QuestionTotals AS (
SELECT
Question,
COUNT(*) AS TotalResponses
FROM SurveyResponses
GROUP BY Question
)
SELECT
sr.Question,
CAST(SUM(CASE WHEN Rating = 'Strongly Agree' THEN 1 ELSE 0 END) * 100.0 / qt.TotalResponses AS DECIMAL(5,1)) AS [Strongly Agree %],
CAST(SUM(CASE WHEN Rating = 'Agree' THEN 1 ELSE 0 END) * 100.0 / qt.TotalResponses AS DECIMAL(5,1)) AS [Agree %],
CAST(SUM(CASE WHEN Rating = 'Neutral' THEN 1 ELSE 0 END) * 100.0 / qt.TotalResponses AS DECIMAL(5,1)) AS [Neutral %],
CAST(SUM(CASE WHEN Rating = 'Disagree' THEN 1 ELSE 0 END) * 100.0 / qt.TotalResponses AS DECIMAL(5,1)) AS [Disagree %],
CAST(SUM(CASE WHEN Rating = 'Strongly Disagree' THEN 1 ELSE 0 END) * 100.0 / qt.TotalResponses AS DECIMAL(5,1)) AS [Strongly Disagree %]
FROM SurveyResponses sr
JOIN QuestionTotals qt ON sr.Question = qt.Question
GROUP BY sr.Question, qt.TotalResponses
ORDER BY sr.Question;
Result:
Question Strongly Agree % Agree % Neutral % Disagree % Strongly Disagree %
-------------------------------------- ---------------- ------- --------- ---------- -------------------
Communication from leadership is clear 0 14.3 42.9 28.6 14.3
I feel valued at this company 28.6 57.1 14.3 0 0
I have the tools I need to succeed 42.9 42.9 14.3 0 0
Work-life balance is respected 28.6 42.9 14.3 14.3 0
Now you can see at a glance that maybe 60% of respondents agree or strongly agree with one question while only 30% feel that way about another, even if the absolute numbers differ.
Department Comparison Matrix
Somtimes you might want to see how different groups responded to the same questions. A department breakdown can reveal whether sentiment is consistent across the organization or if certain teams have different experiences.
SELECT
Question,
SUM(CASE WHEN Department = 'Engineering' AND Rating IN ('Strongly Agree', 'Agree') THEN 1 ELSE 0 END) AS [Engineering Positive],
SUM(CASE WHEN Department = 'Engineering' THEN 1 ELSE 0 END) AS [Engineering Total],
SUM(CASE WHEN Department = 'Sales' AND Rating IN ('Strongly Agree', 'Agree') THEN 1 ELSE 0 END) AS [Sales Positive],
SUM(CASE WHEN Department = 'Sales' THEN 1 ELSE 0 END) AS [Sales Total],
SUM(CASE WHEN Department = 'Marketing' AND Rating IN ('Strongly Agree', 'Agree') THEN 1 ELSE 0 END) AS [Marketing Positive],
SUM(CASE WHEN Department = 'Marketing' THEN 1 ELSE 0 END) AS [Marketing Total]
FROM SurveyResponses
GROUP BY Question
ORDER BY Question;
Result:
Question Engineering Positive Engineering Total Sales Positive Sales Total Marketing Positive Marketing Total
-------------------------------------- -------------------- ----------------- -------------- ----------- ------------------ ---------------
Communication from leadership is clear 1 3 0 2 0 2
I feel valued at this company 3 3 2 2 1 2
I have the tools I need to succeed 3 3 2 2 1 2
Work-life balance is respected 3 3 0 2 2 2
This format shows both positive response counts and total responses for each department, letting you quickly calculate positive response rates.
Favorable vs Unfavorable Summary
Sometimes you want to simplify the view by collapsing ratings into just three categories: favorable, neutral, and unfavorable. This makes patterns even easier to spot.
SELECT
Question,
SUM(CASE WHEN Rating IN ('Strongly Agree', 'Agree') THEN 1 ELSE 0 END) AS Favorable,
SUM(CASE WHEN Rating = 'Neutral' THEN 1 ELSE 0 END) AS Neutral,
SUM(CASE WHEN Rating IN ('Disagree', 'Strongly Disagree') THEN 1 ELSE 0 END) AS Unfavorable,
COUNT(*) AS Total,
CAST(SUM(CASE WHEN Rating IN ('Strongly Agree', 'Agree') THEN 1 ELSE 0 END) * 100.0 / COUNT(*) AS DECIMAL(5,1)) AS [Favorable %]
FROM SurveyResponses
GROUP BY Question
ORDER BY CAST(SUM(CASE WHEN Rating IN ('Strongly Agree', 'Agree') THEN 1 ELSE 0 END) * 100.0 / COUNT(*) AS DECIMAL(5,1)) DESC;
Result:
Question Favorable Neutral Unfavorable Total Favorable %
-------------------------------------- --------- ------- ----------- ----- -----------
I feel valued at this company 6 1 0 7 85.7
I have the tools I need to succeed 6 1 0 7 85.7
Work-life balance is respected 5 1 1 7 71.4
Communication from leadership is clear 1 3 3 7 14.3
This view is sorted by favorable percentage, so your strongest questions appear first and your problem areas sink to the bottom. This format can be great for executive summaries where you want to highlight what’s working and what needs attention.
Net Promoter Score Style Matrix
For questions using numeric scales, you might want to calculate net scores. While our example uses Likert scales, the concept applies.
Let’s create a scored view where Strongly Agree = 2, Agree = 1, Neutral = 0, Disagree = -1, and Strongly Disagree = -2.
SELECT
Question,
SUM(CASE
WHEN Rating = 'Strongly Agree' THEN 2
WHEN Rating = 'Agree' THEN 1
WHEN Rating = 'Neutral' THEN 0
WHEN Rating = 'Disagree' THEN -1
WHEN Rating = 'Strongly Disagree' THEN -2
ELSE 0
END) AS NetScore,
COUNT(*) AS Responses,
CAST(SUM(CASE
WHEN Rating = 'Strongly Agree' THEN 2
WHEN Rating = 'Agree' THEN 1
WHEN Rating = 'Neutral' THEN 0
WHEN Rating = 'Disagree' THEN -1
WHEN Rating = 'Strongly Disagree' THEN -2
ELSE 0
END) * 1.0 / COUNT(*) AS DECIMAL(4,2)) AS AvgScore
FROM SurveyResponses
GROUP BY Question
ORDER BY CAST(SUM(CASE
WHEN Rating = 'Strongly Agree' THEN 2
WHEN Rating = 'Agree' THEN 1
WHEN Rating = 'Neutral' THEN 0
WHEN Rating = 'Disagree' THEN -1
WHEN Rating = 'Strongly Disagree' THEN -2
ELSE 0
END) * 1.0 / COUNT(*) AS DECIMAL(4,2)) DESC;
Result:
Question NetScore Responses AvgScore
-------------------------------------- -------- --------- --------
I have the tools I need to succeed 9 7 1.29
I feel valued at this company 8 7 1.14
Work-life balance is respected 6 7 0.86
Communication from leadership is clear -3 7 -0.43
This gives you a single number per question that summarizes overall sentiment, making it easy to rank questions by performance.
Time-Based Comparison Matrix
If you’re running surveys regularly, you’ll probably want to see how responses change over time. Let’s add some historical data (from the previous month) and compare periods:
INSERT INTO SurveyResponses (ResponseID, ResponseDate, Department, Question, Rating)
VALUES
(29, '2025-02-10', 'Engineering', 'I feel valued at this company', 'Neutral'),
(30, '2025-02-10', 'Engineering', 'I have the tools I need to succeed', 'Agree'),
(31, '2025-02-10', 'Engineering', 'Communication from leadership is clear', 'Disagree'),
(32, '2025-02-10', 'Engineering', 'Work-life balance is respected', 'Neutral'),
(33, '2025-02-12', 'Sales', 'I feel valued at this company', 'Agree'),
(34, '2025-02-12', 'Sales', 'I have the tools I need to succeed', 'Neutral'),
(35, '2025-02-12', 'Sales', 'Communication from leadership is clear', 'Strongly Disagree'),
(36, '2025-02-12', 'Sales', 'Work-life balance is respected', 'Disagree');
Now we can compare February vs March favorable response rates:
SELECT
Question,
CAST(SUM(CASE WHEN MONTH(ResponseDate) = 2 AND Rating IN ('Strongly Agree', 'Agree') THEN 1 ELSE 0 END) * 100.0
/ NULLIF(SUM(CASE WHEN MONTH(ResponseDate) = 2 THEN 1 ELSE 0 END), 0) AS DECIMAL(5,1)) AS [Feb Favorable %],
CAST(SUM(CASE WHEN MONTH(ResponseDate) = 3 AND Rating IN ('Strongly Agree', 'Agree') THEN 1 ELSE 0 END) * 100.0
/ NULLIF(SUM(CASE WHEN MONTH(ResponseDate) = 3 THEN 1 ELSE 0 END), 0) AS DECIMAL(5,1)) AS [Mar Favorable %],
CAST(SUM(CASE WHEN MONTH(ResponseDate) = 3 AND Rating IN ('Strongly Agree', 'Agree') THEN 1 ELSE 0 END) * 100.0
/ NULLIF(SUM(CASE WHEN MONTH(ResponseDate) = 3 THEN 1 ELSE 0 END), 0) AS DECIMAL(5,1))
- CAST(SUM(CASE WHEN MONTH(ResponseDate) = 2 AND Rating IN ('Strongly Agree', 'Agree') THEN 1 ELSE 0 END) * 100.0
/ NULLIF(SUM(CASE WHEN MONTH(ResponseDate) = 2 THEN 1 ELSE 0 END), 0) AS DECIMAL(5,1)) AS [Change]
FROM SurveyResponses
WHERE MONTH(ResponseDate) IN (2, 3)
GROUP BY Question
ORDER BY Question;
Result:
Question Feb Favorable % Mar Favorable % Change
-------------------------------------- --------------- --------------- ------
Communication from leadership is clear 0 14.3 14.3
I feel valued at this company 50 85.7 35.7
I have the tools I need to succeed 50 85.7 35.7
Work-life balance is respected 0 71.4 71.4
The change column shows whether sentiment is improving or declining for each question. This can be a great option for measuring whether initiatives are working or if problems are getting worse.
Detailed Department Breakdown Matrix
For a more granular view, you can create a matrix that shows the full rating distribution for each department-question combination:
SELECT
Department,
Question,
SUM(CASE WHEN Rating = 'Strongly Agree' THEN 1 ELSE 0 END) AS [SA],
SUM(CASE WHEN Rating = 'Agree' THEN 1 ELSE 0 END) AS [A],
SUM(CASE WHEN Rating = 'Neutral' THEN 1 ELSE 0 END) AS [N],
SUM(CASE WHEN Rating = 'Disagree' THEN 1 ELSE 0 END) AS [D],
SUM(CASE WHEN Rating = 'Strongly Disagree' THEN 1 ELSE 0 END) AS [SD]
FROM SurveyResponses
GROUP BY Department, Question
ORDER BY Department, Question;
Result:
Department Question SA A N D SD
----------- -------------------------------------- -- - - - --
Engineering Communication from leadership is clear 0 1 2 1 0
Engineering I feel valued at this company 1 2 1 0 0
Engineering I have the tools I need to succeed 2 2 0 0 0
Engineering Work-life balance is respected 1 2 1 0 0
Marketing Communication from leadership is clear 0 0 0 1 1
Marketing I feel valued at this company 0 1 1 0 0
Marketing I have the tools I need to succeed 0 1 1 0 0
Marketing Work-life balance is respected 1 1 0 0 0
Sales Communication from leadership is clear 0 0 1 1 1
Sales I feel valued at this company 1 2 0 0 0
Sales I have the tools I need to succeed 1 1 1 0 0
Sales Work-life balance is respected 0 0 1 2 0
This provides a complete overview into how each department responded to each question. You can use abbreviated column headers (like we’ve done here) to keep it manageable.
Dynamic Survey Dashboard
You can make survey dashboards fully dynamic so they adapt to whatever rating scales you’re using:
DECLARE @SQL NVARCHAR(MAX);
DECLARE @Columns NVARCHAR(MAX);
-- Build column list based on actual ratings in the data
SELECT @Columns = STRING_AGG(
'SUM(CASE WHEN Rating = ''' + Rating +
''' THEN 1 ELSE 0 END) AS [' + Rating + ']',
', '
) WITHIN GROUP (ORDER BY
CASE Rating
WHEN 'Strongly Agree' THEN 1
WHEN 'Agree' THEN 2
WHEN 'Neutral' THEN 3
WHEN 'Disagree' THEN 4
WHEN 'Strongly Disagree' THEN 5
ELSE 6
END)
FROM (
SELECT DISTINCT Rating
FROM SurveyResponses
) AS Ratings;
-- Build and execute the query
SET @SQL = '
SELECT
Question,
' + @Columns + ',
COUNT(*) AS Total
FROM SurveyResponses
GROUP BY Question
ORDER BY Question;
';
EXEC sp_executesql @SQL;
Result:
Question Strongly Agree Agree Neutral Disagree Strongly Disagree Total
-------------------------------------- -------------- ----- ------- -------- ----------------- -----
Communication from leadership is clear 0 1 3 3 2 9
I feel valued at this company 2 5 2 0 0 9
I have the tools I need to succeed 3 4 2 0 0 9
Work-life balance is respected 2 3 2 2 0 9
This automatically adjusts if you’re using different rating scales (like 1-5 numeric scales, Yes/No questions, or custom categories) without changing your query code.
Response Rate by Department
Sometimes you might find that some questions are only being answered by a certain department, while others keep relatively quiet. If you don’t account for this, you could draw the wrong conclusions. We can deal with this by showing participation by department:
SELECT
Department,
SUM(CASE WHEN Question = 'I feel valued at this company' THEN 1 ELSE 0 END) AS [Q1 Responses],
SUM(CASE WHEN Question = 'I have the tools I need to succeed' THEN 1 ELSE 0 END) AS [Q2 Responses],
SUM(CASE WHEN Question = 'Communication from leadership is clear' THEN 1 ELSE 0 END) AS [Q3 Responses],
SUM(CASE WHEN Question = 'Work-life balance is respected' THEN 1 ELSE 0 END) AS [Q4 Responses],
COUNT(DISTINCT ResponseID) / 4 AS [Avg Per Question]
FROM SurveyResponses
GROUP BY Department
ORDER BY Department;
Result:
Department Q1 Responses Q2 Responses Q3 Responses Q4 Responses Avg Per Question
----------- ------------ ------------ ------------ ------------ ----------------
Engineering 4 4 4 4 4
Marketing 2 2 2 2 2
Sales 3 3 3 3 3
This helps you identify if certain departments are underrepresented in your survey results, which could bias your conclusions.