How to subtract Total from conditioned sum in SQL - sql

I want to do the following:
1) Find the total rows in a table
2) Find the total rows that meets a certain criteria.
3) Subtract (1) from (2).
Sample table Employees:
EmployeeID Nationality
1 Brazil
2 Korea
3 Germany
4 Brazil
5 Brazil
What I've tried:
SELECT count(EmployeeID) as Total from Employees
UNION
SELECT count(EmployeeID) as Brazilians from Employees
WHERE Nationality = 'Brazil'
Result:
Total
5
3
Row 1 will give me the total Employees. Row 2 will give me the Brazilian Employees.
I used UNION to see if I could subtract row 2 from row 1.
I could do this using CASE and SUM(), but that would require the row_number() function, which I can't use given that I'm using WebSQL. Is there another way to index these rows to be able to subtract?
Is there another approach I could use to solve this seemingly simple problem?

How about counting the rows that don't meet that criteria?
SELECT COUNT(EmployeedID) as non_brazilians
FROM Employees
WHERE Nationality <> 'Brazil';

You can use conditional aggregation:
select count(*) as TotalRows,
sum(case when Nationality = 'Brazil' then 1 else 0 end) as Brazilians,
sum(case when Nationality <> 'Brazil' then 1 else 0 end) as nonBrazilians
from Employee;
This assumes that Nationality is never NULL. If that is possible, the last condition should be:
sum(case when Nationality = 'Brazil' then 0 else 1 end) as nonBrazilians

Try this:
SELECT count(*) AS TotalRows
, (SELECT count(EmployeeID) FROM WHERE Nationality = 'Brazil') as Brazilians
, (count(*) - (SELECT count(EmployeeID) FROM WHERE Nationality = 'Brazil')) AS Subtract1From2
FROM Employee

Related

Select only values from one column in a table in SQL with condition

Is it possible to get only the countrys who just played in the pre round
Country Round
Germany Pre Round
Germany Quater final
Spain Pre Round
Portugal Pre Round
And I just want to get the countrys which only played in the pre round. So the result should look like this:
Country
Spain
Portugal
You can group by country and set the conditions in the having clause:
select country
from tablename
group by country
having count(*) = 1 and max(round) = 'Pre Round'
You can try the below using not exists
select country from c
where not exists
(select 1 from c as c1 where c.country=c1.country and roundval<>'Pre Round')
Two more for fun. The first is kind of a variation on #forpas', assigning a numeric value to each round, representing the progression through the rounds, and then getting the highest for the country (which would be simpler if the rounds were stored separately with a round number):
select country
from your_table
group by country
having max(case round
when 'Pre Round' then 1
when 'Quater final' then 2
when 'Semi final' then 3
when 'Final' then 4
end) = 1;
If you wanted to find countries that were in the quarters but not semis then you just need to change to = 2, etc.
The second is overkill here, but could be useful to look for more complicated combinations in other types of data:
select country
from your_table
pivot (
count(*) for round in (
'Pre Round' as pre, 'Quater final' as quarter, 'Semi final' as semi, 'Final' as final
)
)
where pre = 1 and quarter = 0 and semi = 0 and final = 0;
Obviously in your example you wouldn't ever have quarter as 0 and then either semi or final as 1 - you can't get to those rounds without playing the quarters; but for other data you might want a mix.
You could use a inner join on subquery for country wih round 're Round' and check for distinct count
select m.Contry
from my_table m
inner join (
select Country
from my_table
where round ='Pre Round'
) t on t.country = m.country
group by m.Country
having count(distinct m.round ) = 1

Add a Total Row and converting into percentages

I have a database with information about people's work condition and neighbourhood.
I have to display a chart of information in percentages like this:
Neighbourhood Total Employed Unemployed Inactive
Total 100 50 25 25
1 100 45 30 25
2 100 55 20 25
To do that, the code that I've made so far is:
select neighbourhood, Count (*) as Total,
Count(Case when (condition = 1) then 'employed' end) as employed,
Count (case when (condition = 2) then 'unemployed' end) as unemployed,
Count (Case when (condition =3) then 'Inactive' end) as Inactive
from table
group by neighbourhood
order by neighbourhood
the output for that code is (the absolut numbers are made up, they dont result in the percentages above):
Neighbourhood Total Employed Unemployed Inactive
1 600 300 200 100
2 450 220 159 80
So, I have to turn the absolut numbers in percentages and add the Total Row (suming the values from the neighbourhoods) but I all my efforts were a failure. I can't solve how to add that Total row nor how to have that total for each neighbourhood for calculating the percentages
I started studying SQL just two weeks ago so I apologize for any inconvenience. I tried my best to keep it simple (in my database are 15 neighbourhoods and it's ok if they are labeled by numbers)
Thanks
You need to UNION to the add the total row
select 'All' as neighbourhood, Count (*) as Total,
Count(Case when (condition = 1) then 1 end) as employed,
Count (case when (condition = 2) then 1 end) as unemployed,
Count (Case when (condition =3) then 1 end) as Inactive
from table
UNION all
select neighbourhood, Count (*) as Total,
Count(Case when (condition = 1) then 1 end) as employed,
Count (case when (condition = 2) then 1 end) as unemployed,
Count (Case when (condition =3) then 1 end) as Inactive
from table
group by neighbourhood
order by neighbourhood
You can add the total rows using grouping sets:
select neighbourhood, Count(*) as Total,
sum((condition = 1)::int) as employed,
sum((condition = 2)::int) as unemployed,
sum((condition = 3)::int) as Inactive
from table
group by grouping sets ( (neighbourhood), () )
order by neighbourhood;
If you want averages within each row, then use avg() rather than sum().

take Duplicated ID's out and Identify a new columns

I Joined 6 table together to gather all information that I need.
I want all Id's, Names, Birthdays, and Ethnicity.
Some Ids have 2 or more Ethnicity and that will cause a id be duplicated.
I am thinking of writing a sub query or can I just use a case statement since I have tried case statement before and works for another case but I can not apply it in this case.
what I have is:
ID NAME Birthdays Ethnicity
4000 Pedram 11/11/1999 Middle East
4001 Carlos 11/11/1920 Spanish
4001 Carlos 11/11/1920 Native American
4002 Asia 11/22/1986 Polish
4002 Asia 11/22/1986 Native American
4002 Asia 11/22/1986 White/caucassian
I want to say if any Id duplicated and ethnicity is different <> just give me this:
ID NAME Birthdays Ethnicity
4000 Pedram 11/11/1999 Middle East
4001 Carlos 11/11/1920 Multiracial
4002 Asia 11/22/1986 multiracial
PS : ethnicity is in a different table and I joined it to Person_table
PS : to be able to join ethnicity table to Person_table I needed to join 3 more tables that have pr keys that can related to each other.
PS : I tried CASE WHEN Count (Id) > 1 THEN 'Multiracial' ELSE Ethnicity END AS Ethnicity_2
and it Identify all ethnicity as Multiracial.
Any help Or thought will be appreciate.
You can use this:
WITH CTE AS
(
SELECT *,
N = COUNT(*) OVER(PARTITION BY ID),
RN = ROW_NUMBER() OVER(PARTITION BY ID ORDER BY Ethnicity)
FROM dbo.YourTable
)
SELECT ID,
NAME,
Birthdays,
CASE WHEN N > 1 THEN 'Multiracial' ELSE Ethnicity END Ethnicity
FROM CTE
WHERE RN = 1;
This one might not be the most efficient but it works. Just substitute your derived table for t below:
SELECT DISTINCT t.id, t.name,
CASE WHEN cnt = 1 THEN ethnicity
ELSE 'Multiracial' END AS ethnicity
FROM t
INNER JOIN
(SELECT id, COUNT(DISTINCT ethnicity) AS cnt
FROM t
GROUP BY id) sub
ON t.id = sub.id
Tested here: http://sqlfiddle.com/#!9/7473f/6
SELECT
id, name, Birthdays,
IIF(COUNT(DISTINCT Ethnicity) > 1, 'Multiracial', MIN(Ethnicity)) as Ethnicity
FROM
Table
GROUP BY
id, name, Birthdays
SELECT
id, name, Birthdays,
CASE WHEN COUNT(DISTINCT Ethnicity) > 1 THEN 'Multiracial' ELSE MIN(Ethnicity) END as Ethnicity
FROM
Table
GROUP BY
id, name, Birthdays

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;