Stored procedure to count then divide a column - sql

I don't exactly know how to title this question. But I am looking to create a stored procedure or procedures to create a new table with averages. I have 19 sites that I have collected survey data from. I want to count each column two but with two different conditions.
E.g.
SELECT COUNT(ColumnName)
FROM TableName
WHERE ColumnName = 3
SELECT COUNT(ColumnName)
FROM TableName
WHERE ColumnName = 4
From there I would like to add those two numbers together then divide by another count for another column in the table.
Basically I want to know how many surveys have the answer 3 and 4 then divide them by how many surveys were answered. Also keep in mind I want numbers based on each site.

Use group by:
select columnname
from tablename
where columnname in (3, 4)
group by columname;

You seems want :
select sum(case when col in (3,4) then 1 else 0 end) / count(*)
from table t

So I have gotten a bit closer to what I want trying to achieve but it is still not doing what I want it do. This is what I have come up with but I don't know how to get to divide by the sum. SELECT (SELECT COUNT() FROM Resident_Survey WHERE CanbealonewhenIwish = 3 and Village = 'WP' and Setting = 'LTC')+ (SELECT COUNT() FROM Resident_Survey WHERE CanbealonewhenIwish = 4 and Village = 'WP' and Setting = 'LTC')/ (SELECT COUNT(*) FROM Resident_Survey WHERE Village = 'WP' and Setting = 'LTC') AS ICanbealonewhenIwish

I figured it out. I was looking to create this query.
SELECT 100.0 *
COUNT(CASE WHEN Privacyisrespected IN (3,4)
THEN 1
ELSE NULL END) /
COUNT(*) AS Myprivacyisrespected
FROM Resident_Survey
WHERE Village = 'WP'
and Setting = 'LTC'

Related

How to get three different types of score for each text in one row in SQL Server?

I am a new SQL developer and I am trying to write a query that will retrieve the following results from three tables in the database:
ID Text Score #1 Score #2 Score #3
The schema for the three tables are:
T_TextScore Table: Id, TextId, Label, Score, TypeId
T_Text: Id, Text
T_Status Table: Id, Type
The T_TextScore table contains three different types of scroes for each text. I was able to retrieve the scores for all texts but I am still unable to show each type of score for each text in one row as illustrated above. So could you please tell me how I can get the desired result?
Here's the query I have and I think it is not efficient in terms of performance as well:
SELECT T_TextScore.TextId, T_Text.Text, T_TextScore.Label, T_TextScore.Score
FROM T_TextScore INNER JOIN
T_Text ON T_TextScore.TextId = T_Text.Id
WHERE (T_TextScore.TypeId = 3)
UNION
SELECT T_TextScore.TextId, T_Text.Text, T_TextScore.Label, T_TextScore.Score
FROM T_TextScore INNER JOIN
T_Text ON T_TextScore.TextId = T_Text.Id
WHERE (T_TextScore.TypeId = 4)
UNION
SELECT T_TextScore.TextId, T_Text.Text, T_TextScore.Label, T_TextScore.Score
FROM T_TextScore INNER JOIN
T_Text ON T_TextScore.TextId = T_Text.Id
WHERE (T_TextScore.TypeId = 5);
UPDATE:
After using the query suggested by #Craig Young, I got two rows for each text and I don't know why. Could you please explain to me why?
enter image description here
You want conditional aggregation. My best guess is simply:
SELECT ts.TextId,
MAX(CASE WHEN ts.TypeId = 3 THEN ts.Score END) as Score_1,
MAX(CASE WHEN ts.TypeId = 4 THEN ts.Score END) as Score_2,
MAX(CASE WHEN ts.TypeId = 5 THEN ts.Score END) as Score_3
FROM T_TextScore ts
GROUP BY ts.TextId;
Your query isn't doing quite what you asked for. The following would be a much better way of doing what you're currently doing:
SELECT ts.TextId, t.Text, ts.Label, ts.Score
FROM T_TextScore ts /* Table alias makes query much more readable */
INNER JOIN T_Text t ON
ts.TextId = t.Id
WHERE ts.TypeId IN (3, 4, 5)
However, the first part of your question suggests you actually want to pivot your data?
If so you can use PIVOT syntax. Or manual pivoting:
SELECT ts.TextId,
/* Use aggregate function to get only 1 per TextId */
MAX(t.Text) AS Text, MAX((ts.Label) AS Label,
/* Simply move ts.Score to the correct column to be summed. */
SUM(CASE WHEN ts.TypeId = 3 THEN ts.Score ELSE 0 END) AS Score3,
SUM(CASE WHEN ts.TypeId = 4 THEN ts.Score ELSE 0 END) AS Score4,
SUM(CASE WHEN ts.TypeId = 5 THEN ts.Score ELSE 0 END) AS Score5
FROM T_TextScore ts
INNER JOIN T_Text t ON
ts.TextId = t.Id
WHERE ts.TypeId IN (3, 4, 5)
GROUP BY ts.TextId
NOTE: PIVOT syntax is a little more succinct. But strangely I have seen it run slightly slower than manual pivot on occasion. So if performance is important, you'll have to benchmark.
Based on your comment:
After using the query suggested by #Craig Young, I got two rows for each text and I don't know why.
You probably either removed the GROUP BY clause, or included Text and Label in the GROUP BY. This made me realise that I'd forgotten to deal with these 2 columns which weren't part of either aggregate or GROUP BY.
I've updated my query above appropriately. However, I should point out lack of sample data makes it tricky to determine exactly what you're trying to achieve - particularly with the Label column which could be different per Score Type.

Count instances of value (say, '4') in several columns/ rows

I have survey responses in a SQL database. Scores are 1-5.
Current format of the data table is this:
Survey_id, Question_1, Question_2, Question_3
383838, 1,1,1
392384, 1,5,4
393894, 4,3,5
I'm running a new query where I need % 4's, % 5's ... question doesn't matter, just overall.
At first glance I'm thinking
sum(iif(Question_1 =5,1,0)) + sum(iif(Question_2=5,1,0)) .... as total5s
sum(iif(Question_1=4,1,0)) + sum(iif(Question_2=4,1,0)) .... as total4s
But I am unsure if this is the quickest or most elegant way to achieve this.
EDIT: Hmm on first test this query already appears not to work correctly
EDIT2: I think I need sum instead of count in my example, will edit.
You have to unpivot the data and calculate the % responses thereafter. Because there are a limited number of questions, you can use union all to unpivot the data.
select 100.0*count(case when question=4 then 1 end)/count(*) as pct_4s
from (select survey_id,question_1 as question from tablename
union all
select survey_id,question_2 from tablename
union all
select survey_id,question_3 from tablename
) responses
Another way to do this could be
select 100.0*(count(case when question_1=4 then 1 end)
+count(case when question_2=4 then 1 end)
+count(case when question_3=4 then 1 end))
/(3*count(*))
from tablename
With unpivot as #Dudu suggested,
with unpivoted as (select *
from tablename
unpivot (response for question in (question_1,question_2,question_3)) u
)
select 100.0*count(case when response=4 then 1 end)/count(*)
from unpivoted

Getting SUM from 2 different tables into one result

I have been trying to get this to work for 12 hrs now and I cannot :-( Can someone please show me how I can get the ssnumber to group and get the total for each ssnumber.
Here is what I have now. In Table number 1 I have this code
SELECT
UNIT_NO, SUM(RATEB) AS TOTALRTE
FROM TABLE1
WHERE
TRUCK_PAID = 1
AND PICK_UP_DATE >= '(fromdate)'
AND PICK_UP_DATE <= '(todate)'
GROUP BY
UNIT_NO
ORDER BY
UNIT_NO
But table number 2 is where the ssnumber column is, so what I'm trying to do is the rateB sum from all of the loads for each unit_no and then group them and then go into table number 2 and group the ssnumber with the unit number from table number 1 and sum the rateB from table number 1.
Something like this (see below) but its not working :-(
SELECT
UNIT_NO, SUM(RATEB)
FROM
TABLE1
WHERE
TRUCK_PAID = 1
AND PICK_UP_DATE >= '(fromdate)'
AND PICK_UP_DATE <= '(todate)'
GROUP BY
UNIT_NO
JOIN
TABLE TABLE1.UNIT_NO = TABLE2.UNIT_NO GROUP BY TABLE2.SS_NUM
or
SELECT
UNIT_NO, SUM(RATEB) AS TOTALRATE
FROM
TABLE1
GROUP BY
UNIT_NO
JOIN
TRUCKS ON (TABLE1.UNIT_NO = TABLE2.UNIT_NO)
GROUP BY
TABLE2.SSNUMBER
Thank you guys so much for any help...
As requested, it is hard to really understand what you are trying to accomplish without more info about table2 and maybe an example of what you are expecting. However, what I got from your description is that you are trying to accomplish something like this?
SELECT UNIT_NO, TOTALRTE, TOTALLDSRTE
FROM
(
SELECT UNIT_NO,SUM(RATEB) AS TOTALRTE
FROM LOADS
GROUP BY UNIT_NO
) AS tbl1
JOIN
(
SELECT SS_NUM, SUM(RATEB) AS TOTALLDSRTE
FROM LOADS
GROUP BY SS_NUM
) AS tbl2
ON tbl1.UNIT_NO = tbl2.SS_NUM
I would suggest instead of getting data from two select queries in one select query, try to fetch them as separate queries. This saves a lot of time. That, or you can create a table for the result and update the result of each query into the table.

How return a count(*) of 0 instead of NULL

I have this bit of code:
SELECT Project, Financial_Year, COUNT(*) AS HighRiskCount
INTO #HighRisk
FROM #TempRisk1
WHERE Risk_1 = 3
GROUP BY Project, Financial_Year
where it's not returning any rows when the count is zero. How do I make these rows appear with the HighRiskCount set as 0?
You can't select the values from the table when the row count is 0. Where would it get the values for the nonexistent rows?
To do this, you'll have to have another table that defines your list of valid Project and Financial_Year values. You'll then select from this table, perform a left join on your existing table, then do the grouping.
Something like this:
SELECT l.Project, l.Financial_Year, COUNT(t.Project) AS HighRiskCount
INTO #HighRisk
FROM MasterRiskList l
left join #TempRisk1 t on t.Project = l.Project and t.Financial_Year = l.Financial_Year
WHERE t.Risk_1 = 3
GROUP BY l.Project, l.Financial_Year
Wrap your SELECT Query in an ISNULL:
SELECT ISNULL((SELECT Project, Financial_Year, COUNT(*) AS hrc
INTO #HighRisk
FROM #TempRisk1
WHERE Risk_1 = 3
GROUP BY Project, Financial_Year),0) AS HighRiskCount
If your SELECT returns a number, it will pass through. If it returns NULL, the 0 will pass through.
Assuming you have your 'Project' and 'Financial_Year' where Risk_1 is different than 3, and those are the ones you intend to include.
SELECT Project, Financial_Year, SUM(CASE WHEN RISK_1 = 3 THEN 1 ELSE 0 END) AS HighRiskCount
INTO #HighRisk
FROM #TempRisk1
GROUP BY Project, Financial_Year
Notice i removed the where part.
By the way, your current query is not returning null, it is returning no rows.
Use:
SELECT x.Project, x.financial_Year,
COUNT(y.*) AS HighRiskCount
INTO #HighRisk
FROM (SELECT DISTINCT t.project, t.financial_year
FROM #TempRisk1
WHERE t.Risk_1 = 3) x
LEFT JOIN #TempRisk1 y ON y.project = x.project
AND y.financial_year = x.financial_year
GROUP BY x.Project, x.Financial_Year
The only way to get zero counts is to use an OUTER join against a list of the distinct values you want to see zero counts for.
SQL generally has a problem returning the values that aren't in a table. To accomplish this (without a stored procedure, in any event), you'll need another table that contains the missing values.
Assuming you want one row per project / financial year combination, you'll need a table that contains each valid Project, Finanical_Year combination:
SELECT HR.Project, HR.Financial_Year, COUNT(HR.Risk_1) AS HighRiskCount
INTO #HighRisk HR RIGHT OUTER JOIN ProjectYears PY
ON HR.Project = PY.Project AND HR.Financial_Year = PY.Financial_Year
FROM #TempRisk1
WHERE Risk_1 = 3
GROUP BY HR.Project, HR.Financial_Year
Note that we're taking advantage of the fact that COUNT() will only count non-NULL values to get a 0 COUNT result for those result set records that are made up only of data from the new ProjectYears table.
Alternatively, you might only one 0 count record to be returned per project (or maybe one per financial_year). You would modify the above solution so that the JOINed table has only that one column.
Little longer, but what about this as a solution?
IF EXISTS (
SELECT *
FROM #TempRisk1
WHERE Risk_1 = 3
)
BEGIN
SELECT Project, Financial_Year, COUNT(*) AS HighRiskCount
INTO #HighRisk
FROM #TempRisk1
WHERE Risk_1 = 3
GROUP BY Project, Financial_Year
END
ELSE
BEGIN
INSERT INTO #HighRisk
SELECT 'Project', 'Financial_Year', 0
END
MSDN - ISNULL function
SELECT Project, Financial_Year, ISNULL(COUNT(*), 0) AS HighRiskCount
INTO #HighRisk
FROM #TempRisk1
WHERE Risk_1 = 3
GROUP BY Project, Financial_Year

MySQL find count query

I have table called stats. In am inserting yes or no in the table, and I want to show the number of yes count and the number of no count.
Can somebody please help me with the query?
select yn, count(*)
from stats
group by yn;
Try something like this
SELECT SUM(CASE WHEN recommend = 'Y' THEN 1 ELSE 0 END) YesCount,
SUM(CASE WHEN recommend = 'N' THEN 1 ELSE 0 END) NoCount,
COUNT(*) TotalCount
FROM Stats
This is exactly what the GROUP BY clause and aggregate functions are for in SQL. The following should be what you need and more efficient then a CASE statement. It returns a table with two columns: recommend and no (which is the count of identical values in the recommend column. If what you said above is true, then this should return at most two rows.
SELECT recommend, count(*) AS no FROM stats GROUP BY recommend