I need to do a query from 2 tables using count function - sql

The query contains 4 columns: the full name of the doctor, the number of male patients, the number of female patients, and the total number of patients seen by that doctor.
My problem is that I dont know how to count the number of males and females
I am only suppoused to use COUNT, GROUP BY and basic DML (cant use case when)
data in the table PACIENTE
er diagram
data in table medico

This depends on which database you are using specifically. One possible way to write this is:
SELECT
doc_name,
COUNT(CASE WHEN PAT_SEX = 'M' THEN 1 END) males,
COUNT(CASE WHEN PAT_SEX = 'F' THEN 1 END) females
FROM
...
Another common syntax for this is:
COUNT(IF PAT_SEX = 'M' THEN 1 ENDIF)
Some databases support this directly:
COUNTIF(PAT_SEX = 'M')
If you would really like to avoid any kind of conditional, then you could add gender to your groups but then you will have two rows for each doctor:
SELECT
doc_name,
pat_sex,
count(*)
FROM
...
GROUP BY
doc_name,
pat_sex

Related

Difference in output from two SQL queries

What is the difference between the two SQL queries below other than Query2 returning an additional field? Are there any possible scenarios where the output of the two queries would be different (other than the additional field in Query2)
Query1:
SELECT Field1, COUNT(*)
FROM Table1
GROUP BY Field1
HAVING COUNT(*) > 1
Query2:
SELECT Field1, Field2, COUNT(*)
FROM Table1
GROUP BY Field1, Field2
HAVING COUNT(*) > 1
Absolutely, these are different. Query2's Group By clause specifies an extra field. That means when the results are aggregated, they will be aggregated for the combined unique values of Field1 AND Field2. That is, two records are aggregated if and only if both Field1 and Field2 are equal.
For example:
SELECT Profession, Count(*)
FROM People
GROUP BY Profession
HAVING Count(*) > 1
will return a list of professions with associated counts like:
Software Developer, 10
PM, 5
Tester, 2
whereas:
SELECT Profession, Gender, Count(*)
FROM People
GROUP BY Profession, Gender
HAVING Count(*) > 1
will return a list of professions broken out by gender like:
Software Developer, Male, 5
Sofware Developer, Female, 5
PM, Male, 3
PM, Female, 2
Tester, Male, 2
Edit with additional requested information:
You can retrieve counts of professions with rows for both genders via:
SELECT Profession, Count(*)
FROM People
GROUP BY Profession
HAVING SUM(case Gender when 'Female' then 1 else 0 end) > 0 AND SUM(case Gender when 'Male' then 1 else 0 end) > 0
It gets a bit hairy (need subqueries) if you also need associated gender counts
Extra group by clause in query 2 filters records.To know more look at below example.
test data:
id name
1 a
2 b
3 a
4 a
So when I say group by name,sql first filters out distinct records for name which goes like below for the below query
select name,sum(id)
from test
group by name
--first filter out distinct values for group by column (here name)
a
b
--next for each distinct record ,how many values fall into that category..
a 1 a
4 a
3 a
b 2 b
So from the above groups ,now you can calculate any aggregations on the group in our case,it is sum,so next output will go some thing like this
a 8
b 2
As you can see from above output,you also can calculate,any aggregation on group (here a and b values) ,like give me count(id),len(name) on group like below
select name,len(name),sum(id)
from test
group by name
The same thing happens when you group by another field,lets say like below
select id,name
from
test
group by id,name
so in above case,sql first filters alldistinct records for id,name
1 a
2 b
3 a
4 a
next step is to get records which fall for each group
groupby columns --columns which fall into this
1 a 1 a
2 b 2 b
3 a 3 a
4 a 4 a
Now you can calculate aggergations on above groups.hope this helps in visualizing your group by.further having will eliminate groups after group by phase,where will eliminate record before group by phase

Selecting count by row combinations

I'm strugling with what on the first sight appeared to be simple SQL query :)
So I have following table which has three columns: PlayerId, Gender, Result (all of type integer).
What I'm trying to do, is to select distinct players of gender 2 (male) with number of each results.
There are about 50 possible results, so new table should have 51 columns:
|PlayerId | 1 | 2 | 3 | ... | 50 |
So I would like to see how many times each individual male (gender 2) player got specific result.
*** In case question is still not entirely clear to you: After each game I insert a row with a player ID, gender and result (from 1 - 50) player achieved in that game. Now I'd like to see how many times each player achieved specfic results.
If there are 50 results and you want them in columns, then you are talking about a pivot. I tend to do these with conditional aggregation:
select player,
sum(case when result = 0 then 1 else 0 end) as result_00,
sum(case when result = 1 then 1 else 0 end) as result_01,
. . .
sum(case when result = 50 then 1 else 0 end) as result_50
from t
group by player;
You can choose a particular gender if you like, with where gender = 2. But why not calculate all at the same time?
try
select player, result, count(*)
from your_table
where Gender = 2
group by player, result;
select PleyerId from tablename where result = 'specific result you want' and gender = 2 group by PleyerId
The easiest way is to use pivoting:
;with cte as(Select * from t
Where gender = 2)
Select * from cte
Pivot(count(gender) for result in([1],[2],[3],....,[50]))p
Fiddle http://sqlfiddle.com/#!3/8dad5/3
One note: keeping gender in scores table is a bad idea. Better make a separate table for players and keep gender there.

SQL Count Expressions

I am trying to create a table to will count the occurrences of each position for various offices.
So if my data is as follows:
Office Position
A Manager
A Supervisor
A Entry Level
A Entry Level
B Manager
B Entry Level
I would want my code to return:
Office Managers Supervisors EntryLevel
A 1 1 2
B 1 0 1
I have my code below. The issue is that this code counts the total amount of occurrences, not the unique count to each office. The results are as follows
A 2 1 3
B 2 1 3
CREATE TABLE OfficeTest AS
SELECT DISTINCT Office,
(Select COUNT(Position) FROM OfficeData WHERE Make_Name = 'Manager') as Managers,
(Select COUNT(Position) FROM OfficeData WHERE Make_Name = 'Supervisor') as Supervisors,
(Select COUNT(Position) FROM OfficeData WHERE Make_Name = 'Entry Level') as EntryLevel
FROM OfficeData
GROUP BY Office;
Any ideas on how to fix this?
The easiest way I can think of doing this is like this:
SELECT Office,
COUNT(CASE WHEN Make_Name = 'Manager' THEN Position END) AS Managers,
COUNT(CASE WHEN Make_Name = 'Supervisor' THEN Position END) AS Supervisors,
COUNT(CASE WHEN Make_Name = 'Entry Level' THEN Position END) AS EntryLevel
FROM OfficeData
GROUP BY Office
COUNT ignores MISSING values; if the Position is not the one specified in the CASE clause, it will return a MISSING value and won't be counted. This way each case considers only the value of Position you compare.
Another option, as stated in the comments, would be pivoting the table. The SAS equivalent is the TRANSPOSE procedure. I don't have a SAS system to create and test a query using it, but here's the documentation in case you want to check it out.
Just to flush out Danny's comment a bit, the SUM code would look like:
proc sql;
CREATE TABLE want AS
SELECT office,
SUM( (position='Manager') ) as Managers,
SUM( (position='Supervisor') ) as Supervisors,
SUM( (position='Entry Level') ) as EntryLevel
FROM OfficeData
GROUP BY office
;quit;
The (position='Manager') bit resolves to 0 or 1, depending on if its true for the current record. I find the SUM version a lot more concise and legible, but both should work for your situation. Plus, its easily extensible to more than one criteria, like (postion='Manager')*(sex='F') to count only female managers.
SUM with CASE statement should resolve the issue. Below is a reference code
proc sql;
create table result as
select age
, sum(case sex when 'F' then 1 else 0 end) as Female
, sum(case sex when 'M' then 1 else 0 end) as Male
from sashelp.class
group by age;
quit;
proc print data=result;run;

Apply COUNT function on a subgroup of groups

I made up this weird example trying to illustrate what I want to do (it's kind of stupid, but bear with me):
Consider the following table:
EMPLOYEES
married, certified and religious are just boolean fields (in case of Oracle, they are of type NUMBER(1,0)).
I need to come up with SQL that displays for each hire_year, count of married, certified and religious employees within the following salary categories:
A SALARY > 2000
B SALARY BETWEEN 1000 AND 2000
C SALARY < 1000
Based on the above dataset, here is what I expect to get:
So far, I've only come up with the following SQL:
SELECT
COUNT(CASE WHEN married = 1 THEN 1 END) as MARRIED,
COUNT(CASE WHEN certified = 1 THEN 1 END) as certified,
COUNT(CASE WHEN religious = 1 THEN 1 END) as religious,
hire_year
FROM employees
GROUP BY hire_year;
The result of executing this SQL is:
Which is almost what I need, but I also need to divide these counters further down into the groups based on a salary range.
I guess that some analytic function, that divides groups into the buckets based on some SQL expression would help, but I can't figure out which one. I tried with NTILE, but it expects a positive constant as a parameter, rather than an SQL expression (such as SALARY BETWEEN X and Y).
Nope, no need for analytic functions; they're difficult to have in the same query as an aggregate function anyway.
You're looking for the case statement again, you just have to put it in the GROUP BY.
select hire_year
, sum(married) as married
, sum(certified) as certified
, sum(religious) as religious
, case when salary > 2000 then 'A'
when salary >= 1000 then 'B'
else 'C' end as salary_class
from employees
group by hire_year
, case when salary > 2000 then 'A'
when salary >= 1000 then 'B'
else 'C' end
Note that I've changed your count(case when...) to sum(). This is because you're using a boolean 1/0 so this'll work in the same manner but it's a lot cleaner.
For the same reason I've ignored your between in your salary calculation; there's no particular need for it as if the salary is greater than 2000 the first CASE has already been fulfilled.

How to count 2 different data in one query

I need to calculate sum of occurences of some data in two columns in one query. DB is in SQL Server 2005.
For example I have this table:
Person: Id, Name, Age
And I need to get in one query those results:
1. Count of Persons that have name 'John'
2. Count of 'John' with age more than 30 y.
I can do that with subqueries in this way (it is only example):
SELECT (SELECT COUNT(Id) FROM Persons WHERE Name = 'John'),
(SELECT COUNT (Id) FROM Persons WHERE Name = 'John' AND age > 30)
FROM Persons
But this is very slow, and I'm searching for faster method.
I found this solution for MySQL (it almost solve my problem, but it is not for SQL Server).
Do you know better way to calculate few counts in one query than using subqueries?
Using a CASE statement lets you count whatever you want in a single query:
SELECT
SUM(CASE WHEN Persons.Name = 'John' THEN 1 ELSE 0 END) AS JohnCount,
SUM(CASE WHEN Persons.Name = 'John' AND Persons.Age > 30 THEN 1 ELSE 0 END) AS OldJohnsCount,
COUNT(*) AS AllPersonsCount
FROM Persons
Use:
SELECT COUNT(p.id),
SUM(CASE WHEN p.age > 30 THEN 1 ELSE 0 END)
FROM PERSONS p
WHERE p.name = 'John'
It's always preferable when accessing the same table more than once, to review for how it can be done in a single pass (SELECT statement). It won't always be possible.
Edit:
If you need to do other things in the query, see Chris Shaffer's answer.