I have tables which designed like survey questions.
Answer table store like below. One answer related with three questions. QuestionId 1 and QuestionId 2 store Yes or No answers. If QuestionId 1 and QuestionId 2 both of them answer Yes, I want QuesiontId 3 to sum count with column value otherwise alias should be Others. Which approach is better , could u please help :)
AnswerId | QuestionId | QuestionOptionText
---------+------------+-------------------
1 | 1 | No
1 | 2 | No
1 | 3 | 0
--------------------------------------
2 | 1 | Yes
2 | 2 | No
2 | 3 | 0
--------------------------------------
3 | 1 | No
3 | 2 | Yes
3 | 3 | 1-10
--------------------------------------
4 | 1 | Yes
4 | 2 | Yes
4 | 3 | 1-10
--------------------------------------
5 | 1 | Yes
5 | 2 | Yes
5 | 3 | 11-20
Result should be like that
1-10 | 11-20 | Other
-----+-------+-------
1 | 1 | 3
I think you need a self join of the three types of question, and then the summing logic is easier. A different table structure would probably have simplified things. Normally you would have each question in a separate column.
select
sum(iif(ans1 = 'Yes' and ans2 = 'Yes' and ans3 = '1-10',1,0)) as '1-10',
sum(iif(ans1 = 'Yes' and ans2 = 'Yes' and ans3 = '11-20',1,0)) as '11-20',
sum(iif(ans1 <> 'Yes' or ans2 = 'Yes',1,0)) as 'other'
from (
select QuestionOptionText as ans1 from t where QuestionId = 1
) as a1
inner join (
select QuestionOptionText as ans2 from t where QuestionId = 2
) as a2
on a1.AnswerId = a2.AnswerId
inner join (
select QuestionOptionText as ans3 from t where QuestionId = 3
) as a3
on a1.AnswerId = a3.AnswerId
Is this what you want?
select sum(case when QuestionOptionText = '1-10' then 1 else 0 end),
sum(case when QuestionOptionText = '11-20' then 1 else 0 end),
sum(case when QuestionOptionText not in ('1-10', '11-20') then 1 else 0 end)
from t
where QuestionId = 3;
You could do this with two levels of aggregation. First compute the outcome of each series of answerId, the pivot the resultset:
select
sum(case when res = '1-10' then 1 else 0 end) res_1_10,
sum(case when res = '11-20' then 1 else 0 end) res_1_20,
sum(case when res = 'Other' then 1 else 0 end) res_other
from (
select
case when max(case when QuestionId in (1, 2) then QuestionOptionText end) = 'Yes'
then max(case when QuestionId = 3 then QuestionOptionText end)
else 'Other'
end res
from mytable
group by answerId
) t
I have a survey table with many columns but i am focusing on these 2, survey_date and over_rating. i am not sure if it is possible to be done in a single query. I am using sql server 2012. This is my sample data.
select survey_date, overall_rating from survey
survey_date overall_rating
2017-01-06 15:09:51.940 6
2017-02-06 14:18:18.620 4
2017-05-07 16:03:12.037 7
2017-05-23 10:41:30.357 7
2017-05-23 10:41:30.357 5
2017-05-24 12:05:25.217 8
2017-06-01 09:03:47.727 7
2017-06-05 09:01:07.283 9
2017-06-05 09:28:12.597 6
2017-06-15 09:47:29.407 7
2017-07-06 12:10:50.003 2
2017-07-06 13:45:52.997 7
2017-08-06 14:00:35.403 5
2017-08-09 12:21:17.367 8
I need to count the occurrence for each rating 1-10, for each month, and sum it up. Example June 15, rating 10 have 1, rating 9 have 10, ...
This is the result table:
Month 10 9 8 7 6 5 4 3 2 1 Avg Score Total Total >=6 CSI
June'15 1 10 20 3 0 0 0 0 0 0 8 34 34 100%
July'15 1 16 14 0 0 0 0 0 1 0 9 32 31 99%
August'15 7 6 6 0 0 0 0 0 0 0 9 19 19 100%
September'15 0 2 2 0 0 0 0 0 0 0 9 4 4 100%
November'15 0 1 2 0 0 0 0 0 0 0 8 3 3 100%
December'15 0 7 3 4 2 0 0 0 0 0 8 16 16 100%
i have tried this query but is partly wrong as there is duplicate month for each rating:
select si.yr, si.mn,
case when si.overall_rating = 10 then count(si.overall_rating) else 0 end as '10',
case when si.overall_rating = 9 then count(si.overall_rating) else 0 end as '9',
case when si.overall_rating = 8 then count(si.overall_rating) else 0 end as '8',
case when si.overall_rating = 7 then count(si.overall_rating) else 0 end as '7',
case when si.overall_rating = 6 then count(si.overall_rating) else 0 end as '6',
case when si.overall_rating = 5 then count(si.overall_rating) else 0 end as '5',
case when si.overall_rating = 4 then count(si.overall_rating) else 0 end as '4',
case when si.overall_rating = 3 then count(si.overall_rating) else 0 end as '3',
case when si.overall_rating = 2 then count(si.overall_rating) else 0 end as '2',
case when si.overall_rating = 1 then count(si.overall_rating) else 0 end as '1',
sum(si.overall_rating) as month_count
from
(select YEAR(s.survey_date) yr, MONTH(s.survey_date) mn, s.overall_rating
from survey s where s.status='Submitted' and s.survey_date >= '2017-01-01' AND s.survey_date <= '2017-12-31'
group by YEAR(s.survey_date), MONTH(s.survey_date), s.overall_rating) si group by si.yr, si.mn, si.overall_rating;
Results:
yr mm 10 9 8 7 6 5 4 3 2 1 total
2017 1 0 0 0 0 1 0 0 0 0 0 6
2017 2 0 0 0 0 0 0 1 0 0 0 4
2017 5 0 0 0 0 0 1 0 0 0 0 5
2017 5 0 0 0 1 0 0 0 0 0 0 7
2017 5 0 0 1 0 0 0 0 0 0 0 8
2017 6 0 0 0 0 1 0 0 0 0 0 6
2017 6 0 0 0 1 0 0 0 0 0 0 7
2017 6 0 1 0 0 0 0 0 0 0 0 9
2017 7 0 0 0 0 0 0 0 0 1 0 2
2017 7 0 0 0 1 0 0 0 0 0 0 7
2017 8 0 0 0 0 0 1 0 0 0 0 5
2017 8 0 0 1 0 0 0 0 0 0 0 8
As you can see 5 and 6 are repeated for different rating. If anyone could tell me is it possible to be done in a single query. Thanks
I think I understand what you are trying to achieve here and you'll be pleased to know you are not far off. You have the right idea in using a conditional aggregate, but you need to wrap your conditional case expression in the aggregate, not the other way around. To do a conditional count, you can simply return 1 for a condition match and 0 for a no match and then sum up the result.
Doing this allows your group by to remain nice and simple:
declare #t table(survey_date datetime,overall_rating int);
insert into #t values ('2017-01-06 15:09:51.940',6),('2017-02-06 14:18:18.620',4),('2017-05-07 16:03:12.037',7),('2017-05-23 10:41:30.357',7),('2017-05-23 10:41:30.357',5),('2017-05-24 12:05:25.217',8),('2017-06-01 09:03:47.727',7),('2017-06-05 09:01:07.283',9),('2017-06-05 09:28:12.597',6),('2017-06-15 09:47:29.407',7),('2017-07-06 12:10:50.003',2),('2017-07-06 13:45:52.997',7),('2017-08-06 14:00:35.403',5),('2017-08-09 12:21:17.367',8);
select dateadd(m,datediff(m,0,survey_date),0) as [Month]
,sum(case when overall_rating = 10 then 1 else 0 end) as [10]
,sum(case when overall_rating = 9 then 1 else 0 end) as [9]
,sum(case when overall_rating = 8 then 1 else 0 end) as [8]
,sum(case when overall_rating = 7 then 1 else 0 end) as [7]
,sum(case when overall_rating = 6 then 1 else 0 end) as [6]
,sum(case when overall_rating = 5 then 1 else 0 end) as [5]
,sum(case when overall_rating = 4 then 1 else 0 end) as [4]
,sum(case when overall_rating = 3 then 1 else 0 end) as [3]
,sum(case when overall_rating = 2 then 1 else 0 end) as [2]
,sum(case when overall_rating = 1 then 1 else 0 end) as [1]
,count(overall_rating) as ScoresReturned
,sum(overall_rating) as TotalScore
,avg(cast(overall_rating as decimal(10,0))) as Average
from #t
group by dateadd(m,datediff(m,0,survey_date),0)
order by [Month];
Output:
+-------------------------+----+---+---+---+---+---+---+---+---+---+----------------+------------+----------+
| Month | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | ScoresReturned | TotalScore | Average |
+-------------------------+----+---+---+---+---+---+---+---+---+---+----------------+------------+----------+
| 2017-01-01 00:00:00.000 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 6 | 6.000000 |
| 2017-02-01 00:00:00.000 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 4 | 4.000000 |
| 2017-05-01 00:00:00.000 | 0 | 0 | 1 | 2 | 0 | 1 | 0 | 0 | 0 | 0 | 4 | 27 | 6.750000 |
| 2017-06-01 00:00:00.000 | 0 | 1 | 0 | 2 | 1 | 0 | 0 | 0 | 0 | 0 | 4 | 29 | 7.250000 |
| 2017-07-01 00:00:00.000 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 2 | 9 | 4.500000 |
| 2017-08-01 00:00:00.000 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 2 | 13 | 6.500000 |
+-------------------------+----+---+---+---+---+---+---+---+---+---+----------------+------------+----------+
select si.yr, si.mn,
case when si.overall_rating = 10 then count(si.overall_rating) else 0 end as '10',
case when si.overall_rating = 9 then count(si.overall_rating) else 0 end as '9',
case when si.overall_rating = 8 then count(si.overall_rating) else 0 end as '8',
case when si.overall_rating = 7 then count(si.overall_rating) else 0 end as '7',
case when si.overall_rating = 6 then count(si.overall_rating) else 0 end as '6',
case when si.overall_rating = 5 then count(si.overall_rating) else 0 end as '5',
case when si.overall_rating = 4 then count(si.overall_rating) else 0 end as '4',
case when si.overall_rating = 3 then count(si.overall_rating) else 0 end as '3',
case when si.overall_rating = 2 then count(si.overall_rating) else 0 end as '2',
case when si.overall_rating = 1 then count(si.overall_rating) else 0 end as '1',
sum(si.overall_rating) as month_count
from
(select YEAR(s.survey_date) yr, MONTH(s.survey_date) mn, s.overall_rating
from survey s where s.status='Submitted' and s.survey_date >= '2017-01-01' AND s.survey_date <= '2017-12-31'
group by YEAR(s.survey_date), MONTH(s.survey_date), s.overall_rating) si group by si.yr, si.mn;
You must remove si.overall_rating in Group By
Oh! I posted late, anyway you can try this otherwise.
DATA:
IF ( OBJECT_ID('tempdb..#temptable') IS NOT NULL )
BEGIN
DROP TABLE #temptable
END
CREATE TABLE #temptable
(
survey_date DATETIME ,
overall_rating NUMERIC(22,2)
)
INSERT INTO #temptable
( survey_date, overall_rating )
VALUES ( '2017-01-06 15:09:51.940', 6 ),
( '2017-02-06 14:18:18.620', 4 ),
( '2017-05-07 16:03:12.037', 7 ),
( '2017-05-23 10:41:30.357', 7 ),
( '2017-05-23 10:41:30.357', 5 ),
( '2017-05-24 12:05:25.217', 8 ),
( '2017-06-01 09:03:47.727', 7 ),
( '2017-06-05 09:01:07.283', 9 ),
( '2017-06-05 09:28:12.597', 6 ),
( '2017-06-15 09:47:29.407', 7 ),
( '2017-07-06 12:10:50.003', 2 ),
( '2017-07-06 13:45:52.997', 7 ),
( '2017-08-06 14:00:35.403', 5 ),
( '2017-08-09 12:21:17.367', 8 )
QUERY:
;
WITH CTE
AS ( SELECT DATENAME(month, survey_date) + ' '''
+ RIGHT(CAST(YEAR(survey_date) AS NVARCHAR(4)),
2) AS [Month] ,
ISNULL([1], 0) [1] ,
ISNULL([2], 0) [2] ,
ISNULL([3], 0) [3] ,
ISNULL([4], 0) [4] ,
ISNULL([5], 0) [5] ,
ISNULL([6], 0) [6] ,
ISNULL([7], 0) [7] ,
ISNULL([8], 0) [8] ,
ISNULL([9], 0) [9] ,
ISNULL([10], 0) [10],
Total,
Average
FROM ( SELECT survey_date ,
COUNT(overall_rating) overall_rating,
CAST(SUM(overall_rating) AS INT) Total,
AVG(overall_rating) Average
FROM ( SELECT DATEADD(MONTH,
DATEDIFF(MONTH,
0, survey_date),
0) survey_date ,
overall_rating
FROM #temptable
) T
GROUP BY t.survey_date
) PVT PIVOT ( SUM(overall_rating) FOR overall_rating IN ( [1],
[2], [3], [4],
[5], [6], [7],
[8], [9], [10] ) ) P
)
SELECT [Month] ,
ISNULL([1], 0) [1] ,
ISNULL([2], 0) [2] ,
ISNULL([3], 0) [3] ,
ISNULL([4], 0) [4] ,
ISNULL([5], 0) [5] ,
ISNULL([6], 0) [6] ,
ISNULL([7], 0) [7] ,
ISNULL([8], 0) [8] ,
ISNULL([9], 0) [9] ,
ISNULL([10], 0) [10],
Total,
Average
FROM CTE
RESULT:
Month 1 2 3 4 5 6 7 8 9 10 Total Average
----------------------- ------ ---- ----- ----- ----- ----- ---- ---- ----- ----- ----------- -------------
January '17 1 0 0 0 0 0 0 0 0 0 6 6.000000
February '17 1 0 0 0 0 0 0 0 0 0 4 4.000000
May '17 0 0 0 4 0 0 0 0 0 0 27 6.750000
June '17 0 0 0 4 0 0 0 0 0 0 29 7.250000
July '17 0 2 0 0 0 0 0 0 0 0 9 4.500000
August '17 0 2 0 0 0 0 0 0 0 0 13 6.500000
(6 row(s) affected)
I am currently having difficulty getting the correct values from my table. Here is my table
NOTE: The column Status has 3 possible values (Cleaned, Unclean, Closed)
+-----------+-------------+--------+------------+
|ApplicantID|ApplicantName| Status | HireDate |
+-----------+-------------+--------+------------+
| 1 | John Smith |Cleaned |08/26/2015 |
| 2 | Alex Murphy |Closed |09/12/2015 |
| 3 | Oliver David|Cleaned |01/11/2015 |
| 4 | Max Payne |Unclean |03/18/2015 |
+-----------+-------------+--------+------------+
The output I'm expecting and it should also be sorted by year.
For example I call all these records for the year 2015 which I get using the variable #Year.
NOTE: The column Total is the SUM of Cleaned and Unclean
+---------+-----------+-----------+----------+---------+
| Month | Cleaned | Unclean | Closed | Total |
+---------+-----------+-----------+----------+---------+
| January| 1 | 0 | 0 | 1 |
| February| 0 | 0 | 0 | 0 |
| March | 0 | 1 | 0 | 1 |
| April | 0 | 0 | 0 | 0 |
| May | 0 | 0 | 0 | 0 |
| June | 0 | 0 | 0 | 0 |
| July | 0 | 0 | 0 | 0 |
| August | 1 | 0 | 0 | 1 |
|September| 0 | 0 | 1 | 0 |
| October| 0 | 0 | 0 | 0 |
| November| 0 | 0 | 0 | 0 |
| December| 0 | 0 | 0 | 0 |
+---------+-----------+-----------+----------+---------+
I can't seem to get the right code, for the sql this is my current code.
SELECT Month(HireDate) AS Month, COUNT(*)
FROM Hires
GROUP BY Month(HireDate)
I know my coding is wrong, because it is incomplete.
Generate a list of numbers from 1 to 12 first to hold all months. Then do a LEFT JOIN on Hires to make sure all missing months are accounted for. Then use conditional aggregation for the totals:
SQL Fiddle
;WITH CteMonths AS(
SELECT * FROM(VALUES
(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)
)t(N)
)
SELECT
Month = DATENAME(MONTH, DATEADD(MONTH, N-1,0)),
Cleaned = SUM(CASE WHEN h.Status = 'Cleaned' THEN 1 ELSE 0 END),
Closed = SUM(CASE WHEN h.Status = 'Closed' THEN 1 ELSE 0 END),
Unclean = SUM(CASE WHEN h.Status = 'Unclean' THEN 1 ELSE 0 END),
Total = SUM(CASE WHEN h.Status IN('Cleaned', 'Unclean') THEN 1 ELSE 0 END)
FROM CteMonths m
LEFT JOIN Hires h
ON m.N = MONTH(h.HireDate)
--AND YEAR(h.HireDate) = #year --uncomment this line to filter for year.
GROUP BY m.N
ORDER BY m.N
If you want to include the YEAR:
SQL Fiddle
;WITH CteMonths AS(
SELECT * FROM(VALUES
(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)
)t(N)
),
CteYears(yr) AS(
SELECT DISTINCT YEAR(HireDate) FROM Hires
),
CteAllDates(dt) AS(
SELECT
DATEADD(MONTH, m.N - 1, DATEADD(YEAR, y.yr - 1900, 0))
FROM CteMonths m
CROSS JOIN CteYears y
)
SELECT
Year = YEAR(d.dt),
Month = DATENAME(MONTH, d.dt),
Cleaned = SUM(CASE WHEN h.Status = 'Cleaned' THEN 1 ELSE 0 END),
Closed = SUM(CASE WHEN h.Status = 'Closed' THEN 1 ELSE 0 END),
Unclean = SUM(CASE WHEN h.Status = 'Unclean' THEN 1 ELSE 0 END),
Total = SUM(CASE WHEN h.Status IN('Cleaned', 'Unclean') THEN 1 ELSE 0 END)
FROM CteAllDates d
LEFT JOIN Hires h
ON MONTH(d.dt) = MONTH(h.HireDate)
AND YEAR(d.dt) = YEAR(h.HireDate)
GROUP BY YEAR(d.dt), MONTH(d.dt), DATENAME(MONTH, d.dt)
ORDER BY YEAR(d.dt), MONTH(d.dt)
If you want to filter for year, say #year = 2015, you can replace the previous ctes with:
;WITH CteMonths AS(
SELECT * FROM(VALUES
(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)
)t(N)
),
CteAllDates(dt) AS(
SELECT
DATEADD(MONTH, m.N - 1, DATEADD(YEAR, #year - 1900, 0))
FROM CteMonths m
)...
I suggest to create TEMP table with values from 1 to 12 (numbers of months) and JOIN your table with TEMP table. To achieve values as columns names you can use PIVOT or CASE. You can do It in following:
INSERT INTO #Months VALUES
(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)
SELECT DATENAME(MONTH, DATEADD(MONTH, m.Id-1, 0)) AS [Month]
, SUM(CASE WHEN [Status] = 'Cleaned' THEN 1 ELSE 0 END) AS [Cleaned]
, SUM(CASE WHEN [Status] = 'Closed' THEN 1 ELSE 0 END ) AS [Closed]
, SUM(CASE WHEN [Status] = 'Unclean' THEN 1 ELSE 0 END) AS [Unclean]
, SUM(CASE WHEN [Status] IN ('Unclean', 'Cleaned') THEN 1 ELSE 0 END) AS [Total]
FROM #Test t
RIGHT JOIN #Months m ON m.Id = MONTH(t.HireDate)
GROUP BY m.Id
OUTPUT
+---------+-----------+-----------+----------+---------+
| Month | Cleaned | Unclean | Closed | Total |
+---------+-----------+-----------+----------+---------+
| January | 1 | 0 | 0 | 1 |
| February| 0 | 0 | 0 | 0 |
| March | 0 | 1 | 0 | 1 |
| April | 0 | 0 | 0 | 0 |
| May | 0 | 0 | 0 | 0 |
| June | 0 | 0 | 0 | 0 |
| July | 0 | 0 | 0 | 0 |
| August | 1 | 0 | 0 | 1 |
|September| 0 | 0 | 1 | 0 |
| October | 0 | 0 | 0 | 0 |
| November| 0 | 0 | 0 | 0 |
| December| 0 | 0 | 0 | 0 |
+---------+-----------+-----------+----------+---------+
DEMO
You can test It at: SQL FIDDLE