SQL Server : get Count() of a related table column where some condition - sql

Given tables CollegeMajors
| Id | Major |
|----|-------------|
| 1 | Accounting |
| 2 | Math |
| 3 | Engineering |
and EnrolledStudents
| Id | CollegeMajorId | Name | HasGraduated |
|----|----------------|-----------------|--------------|
| 1 | 1 | Grace Smith | 1 |
| 2 | 1 | Tony Fabio | 0 |
| 3 | 1 | Michael Ross | 1 |
| 4 | 3 | Fletcher Thomas | 1 |
| 5 | 2 | Dwayne Johnson | 0 |
I want to do a query like
Select
CollegeMajors.Major,
Count(select number of students who have graduated) AS TotalGraduated,
Count(select number of students who have not graduated) AS TotalNotGraduated
From
CollegeMajors
Inner Join
EnrolledStudents On EnrolledStudents.CollegeMajorId = CollegeMajors.Id
and I'm expecting these kind of results
| Major | TotalGraduated | TotalNotGraduated |
|-------------|----------------|-------------------|
| Accounting | 2 | 1 |
| Math | 0 | 1 |
| Engineering | 1 | 0 |
So the question is, what kind of query goes inside the COUNT to achieve the above?

Select CollegeMajors.Major
, COUNT(CASE WHEN EnrolledStudents.HasGraduated= 0 then 1 ELSE NULL END) as "TotalNotGraduated",
COUNT(CASE WHEN EnrolledStudents.HasGraduated = 1 then 1 ELSE NULL END) as "TotalGraduated"
From CollegeMajors
InnerJoin EnrolledStudents On EnrolledStudents.CollegeMajorId = CollegeMajors.Id
GROUP BY CollegeMajors.Major

You can use the CASE statement inside your COUNT to achieve the desired result.Please try the below updated query.
Select CollegeMajors.Major
, COUNT(CASE WHEN EnrolledStudents.HasGraduated= 0 then 1 ELSE NULL END) as "TotalNotGraduated",
COUNT(CASE WHEN EnrolledStudents.HasGraduated = 1 then 1 ELSE NULL END) as "TotalGraduated"
From CollegeMajors
InnerJoin EnrolledStudents On EnrolledStudents.CollegeMajorId = CollegeMajors.Id
GROUP BY CollegeMajors.Major

You can try this for graduated count:
Select Count(*) From EnrolledStudents group by CollegeMajorId having HasGraduated = 1
And change 1 to zero for not graduated ones:
Select Count(*) From EnrolledStudents group by CollegeMajorId having HasGraduated = 0

Related

What Clause would most optimally create this query?

So I don't have much experience with SQL, and am trying to learn. An interview question I came across had this question. I'm trying to learn more SQL but maybe I'm missing a piece of info to solve this? Or maybe I'm approaching the problem wrong.
This is the question:
We have following two tables , below is their info:
POLICY (id as int, policy_content as varchar2)
POLICY_VOTES (vote as boolean, policy_id as int)
Write a single query that returns the policy_id, number of yes(true) votes and number of no(false) votes with a row for each policy up for a vote stored
My first thought when approaching this was to use a WITH clause to get the policy_ids and use an inner join to get the votes for yes and no but I can't find a way to make it work, which is what leads me to believe that there's another clause in SQL I'm not aware of or couldn't find that would make it easier. Either that or I'm thinking of the problem in the wrong way.
Good question.
I cannot answer too specifically, since you did not specify a DBMS, but what you will want to do is count or situationally sum based on criteria. When you use an aggregate function like that, you also need GROUP BY.
Here are two example tables I made with test data:
policy
| id | policy_content |
|----|----------------|
| 1 | foo |
| 2 | foo |
| 3 | foo |
| 4 | foo |
| 5 | foo |
policy votes
| vote | policy_id |
|------|-----------|
| yes | 1 |
| no | 1 |
| yes | 2 |
| yes | 2 |
| no | 3 |
| no | 3 |
| no | 4 |
| yes | 4 |
| yes | 5 |
| yes | 5 |
Using the below query:
SELECT
policy_votes.policy_id,
SUM(CASE WHEN vote = 'yes' THEN 1 ELSE 0 END) AS yes_votes,
SUM(CASE WHEN vote = 'no' THEN 1 ELSE 0 END) AS no_votes
FROM
policy_votes
GROUP BY
policy_votes.policy_id
You get:
| POLICY_ID | YES_VOTES | NO_VOTES |
|-----------|-----------|----------|
| 1 | 1 | 1 |
| 2 | 2 | 0 |
| 4 | 1 | 1 |
| 5 | 2 | 0 |
| 3 | 0 | 2 |
Here is an SQL Fiddle for you to try it out.
Try this:
select p.id, p.content,
Count(case when pv.vote='true' then 1 end) as number_of_yes,
Count(case when pv.vote='false' then 1 end) as number_of_no
From policy p join policy_votes pv
On(p.id = pv.policy_id)
Group by p.id, p.content
Cheers!!

Group-by if all field results are true

I have this query:
SELECT
tbl_ord.ord.table,
tbl_ord.ord.n_ord,
player.confirm
FROM
tbl_ord
INNER JOIN
tbl_players ON tbl_ord.player_id = tbl_players.player_ids
WHERE
id_shop = 3
ORDER BY
n_ord ASC
result:
+-----------+-------+---------+
| ord_table | n_ord | confirm |
+-----------+-------+---------+
| 10 | 2 | 1 |
| 10 | 2 | 0 |
| 8 | 3 | 1 |
| 8 | 3 | 1 |
| 4 | 5 | 1 |
| 4 | 5 | 1 |
+-----------+-------+---------+
I'd like get only result with all confirmed users by group-by on ord_table
+-----------+-------+---------+
| ord_table | n_ord | confirm |
+-----------+-------+---------+
| 4 | 5 | 1 |
| 8 | 3 | 1 |
+-----------+-------+---------+
Thanks!
You can probably use not exists:
SELECT o.ord_table, o.ord.n_ord,
1 as confirm
FROM tbl_ord o
WHERE NOT EXISTS (SELECT 1
FROM tbl_players p
WHERE o.player_id = p.player_ids AND
?.id_shop = 3 AND -- not sure what table this comes from
p.confirm = 0
)
ORDER BY o.n_ord ASC;
The advantage of this approach is that it avoids aggregating at the outer level. This, in turn, means that it can make better use of indexes, including using an index to potentially avoid sorting.
Use HAVING:
SELECT
tbl_ord.ord.table,
tbl_ord.ord.n_ord,
MIN(player.confirm)
FROM tbl_ord
INNER JOIN tbl_players ON tbl_ord.player_id = tbl_players.player_ids
WHERE id_shop = 3
GROUP BY tbl_ord.ord.table, tbl_ord.ord.n_ord,
HAVING MIN(player.confirm) = 1
ORDER BY n_ord ASC

SQL SELECT GROUP BY and join the row with certain value

I have two tables:
tbl_car
id_c | name car
1 | VW
2 | Audi
3 | Ferrari
tbl_auto_accessorize
id_a | id_c | fuel | gpl | abs
1 | 1 | 0 | 1 | 0
2 | 1 | 1 | 0 | 1
3 | 2 | 0 | 1 | 1
4 | 2 | 1 | 0 | 1
5 | 3 | 0 | 1 | 0
I have this SQL:
SELECT id_a,id_c,abs
FROM tbl_car LEFT JOIN
tbl_auto_accessorize
ON tbl_accessorize.id_c = tbl_auto.id_c
GROUP BY id_c
the return in column abs is 0 because the first row is 0 but the same id_c have another row with 1.
I need a result equal to 1 because at least one is 1 of the same car.
Result:
id_c | abs
1 | 1
2 | 1
3 | 0
I think you might want max():
SELECT c.id_c, MAX(aa.abs)
FROM tbl_car c LEFT JOIN
tbl_auto_accessorize aa
ON aa.id_c = c.id_c
GROUP BY c.id_c;
As a rule, all the columns in an aggregation query should either be in the GROUP BY or be the arguments to aggregation functions. That is why I removed id_a.

Counting on multiple columns

I have a table like this:
+------------+---------------+-------------+
|store_number|entrance_number|camera_number|
+------------+---------------+-------------+
| 1 | 1 | 1 |
| 1 | 1 | 2 |
| 2 | 1 | 1 |
| 2 | 2 | 1 |
| 2 | 2 | 2 |
| 3 | 1 | 1 |
| 4 | 1 | 1 |
| 4 | 1 | 2 |
| 4 | 2 | 1 |
| 4 | 3 | 1 |
+------------+---------------+-------------+
In summary the stores are numbered 1 and up, the entrances are numbered 1 and up for each store, and the cameras are numbered 1 and up for each entrance.
What I want to do is count how many how many entrances in total, and how many cameras in total for each store. Producing this result from the above table:
+------------+---------------+-------------+
|store_number|entrances |cameras |
+------------+---------------+-------------+
| 1 | 1 | 2 |
| 2 | 2 | 3 |
| 3 | 1 | 1 |
| 4 | 3 | 4 |
+------------+---------------+-------------+
How can I count on multiple columns to produce this result?
You can do this with a GROUP BY and a COUNT() of each item:
Select Store_Number,
Count(Distinct Entrance_Number) as Entrances,
Count(Camera_Number) As Cameras
From YourTable
Group By Store_Number
From what I can tell from your expected output, you're looking for the number of cameras that appear, whilst also looking for the DISTINCT number of entrances.
This will work as well,
DECLARE #store TABLE
( store_number INT,entrance_number INT,camera_number INT)
INSERT INTO #store VALUES(1,1,1),(1,1,2),(2,1,1),(2,2,1),
(2,2,2),(3,1,1),(4,1,1),(4,1,2),(4,2,1),(4,3,1)
SELECT AA.s store_number, BB.e entrances,AA.c cameras FROM (
SELECT s,COUNT(DISTINCT c) c FROM ( SELECT store_number s,
CONVERT(VARCHAR,store_number) + CONVERT(VARCHAR,entrance_number) +
CONVERT(VARCHAR,camera_number) c FROM #store ) A GROUP BY s ) AA
LEFT JOIN
( SELECT s,COUNT(DISTINCT e) e FROM ( SELECT store_number s,
CONVERT(VARCHAR,store_number) + CONVERT(VARCHAR,entrance_number) e
FROM #store ) B GROUP BY s ) BB ON AA.s = BB.s
Hope it helped. :)

Horizontal Count SQL

I apologize if this is a duplicate question but I could not find my answer.
I am trying to take data that is horizontal, and get a count of how many times a specific number appears.
Example table
+-------+-------+-------+-------+
| Empid | KPI_A | KPI_B | KPI_C |
+-------+-------+-------+-------+
| 232 | 1 | 3 | 3 |
| 112 | 2 | 3 | 2 |
| 143 | 3 | 1 | 1 |
+-------+-------+-------+-------+
I need to see the following:
+-------+--------------+--------------+--------------+
| EmpID | (1's Scored) | (2's Scored) | (3's Scored) |
+-------+--------------+--------------+--------------+
| 232 | 1 | 0 | 2 |
| 112 | 0 | 2 | 1 |
| 143 | 2 | 0 | 1 |
+-------+--------------+--------------+--------------+
I hope that makes sense. Any help would be appreciated.
Since you are counting data across multiple columns, it might be easier to unpivot your KPI columns first, then count the scores.
You could use either the UNPIVOT function or CROSS APPLY to convert your KPI columns into multiple rows. The syntax would be similar to:
select EmpId, KPI, Val
from yourtable
cross apply
(
select 'A', KPI_A union all
select 'B', KPI_B union all
select 'C', KPI_C
) c (KPI, Val)
See SQL Fiddle with Demo. This gets your multiple columns into multiple rows, which is then easier to work with:
| EMPID | KPI | VAL |
|-------|-----|-----|
| 232 | A | 1 |
| 232 | B | 3 |
| 232 | C | 3 |
| 112 | A | 2 |
Now you can easily count the number of 1's, 2's, and 3's that you have using an aggregate function with a CASE expression:
select EmpId,
sum(case when val = 1 then 1 else 0 end) Score_1,
sum(case when val = 2 then 1 else 0 end) Score_2,
sum(case when val = 3 then 1 else 0 end) Score_3
from
(
select EmpId, KPI, Val
from yourtable
cross apply
(
select 'A', KPI_A union all
select 'B', KPI_B union all
select 'C', KPI_C
) c (KPI, Val)
) d
group by EmpId;
See SQL Fiddle with Demo. This gives a final result of:
| EMPID | SCORE_1 | SCORE_2 | SCORE_3 |
|-------|---------|---------|---------|
| 112 | 0 | 2 | 1 |
| 143 | 2 | 0 | 1 |
| 232 | 1 | 0 | 2 |