Selecting count by row combinations - sql

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.

Related

SQL Group By and changing the grouped values

I have a table like this:
1 | Madrid | 45000
2 | Berlin | 35000
3 | Berlin | 65000
Now I want to show a result like this:
1 | Madrid | 45000
2 | Berlin | "Different Values"
So basically I want to use a "Group By" and if it is grouped, then change the value of some columns to a manual string.
I thought about using a view, update all values in this view to the string where i have duplications of the grouped column and then use the real query.
I even thought about implementing an assembly into the sql server that does this, but I don't find any good tutorials on this, only that you can do it.
Or has someone an even better idea? (The real tables used here are huge and the sql query does take sometimes up to 3 minutes to perform, so I made this example simple and I didn't wanted to work here with counts on every column to group, because that could take more than just a few minutes.
Something like this should work
select min(id) as id,
name,
case when count(*) = 1
then cast(sum(value) as varchar)
else 'Different values'
end as value
from your_table
group by name
I would do this as:
select min(id) as id, city,
(case when min(value) = max(value)
then cast(max(value) as varchar(255))
else 'Different Values'
end) as result
from t
group by city;
In fact, I might use something more informative than "different values", such as the range:
select min(id) as id, city,
(case when min(value) = max(value)
then cast(max(value) as varchar(255))
else cast(min(value) as varchar(255)) + '-' + cast(max(value) as varchar(255))
end) as result
from t
group by city;

SQL - joining two queries against same table for grid output

I should probably be able to figure this out from other questions/answers I've read here, but I just can't get anything to work today. Any help is really appreciated.
I have two queries, counting the instances of "GOOD" feedback, and "BAD" feedback from a single table. I just want to join them so that I can see something like below
ID | GOOD | BAD
121 | 0 | 7
123 | 5 | 0
287 | 32 | 8
I'm running numerous queries from VBA, if that matters, and the 0's can just be null. I can clean that stuff up in VBA.
Query 1:
select ID, count(*)
from HLFULL
where DEPT= 'HLAK'
and feedback = 'GOOD'
group by ID
Query 2:
select ID, count(*)
from HLFULL
where DEPT= 'HLAK'
and feedback = 'BAD'
group by ID
I've tried UNION, UNION ALL, JOIN, INNER JOIN, OUTER JOIN, aggregations, etc.
You can do conditional aggregation like this:
select ID,
count(case when feedback = 'GOOD' then 1 end) as Good,
count(case when feedback = 'BAD' then 1 end) as Bad
from HLFULL
where DEPT = 'HLAK'
and feedback in ('GOOD', 'BAD')
group by ID
You should be able to get the result using conditional aggregation. This type of query uses a CASE expression along with your aggregate function to get multiple columns:
select ID,
count(case when feedback = 'GOOD' then Id end) as Good,
count(case when feedback = 'BAD' then Id end) as Bad
from HLFULL
where DEPT= 'HLAK'
group by ID

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;

How to select id's that have rows for all values from a set

I want to select all Id from a table that have rows for both programs 'basketball' and 'football'
Given a table like this:
Id program
1 basketball
2 football
3 basketball
2 basketball
1 football
4 football
5 basketball
How can I get a result like this:
id
1
2
Since you want to return the id's that have both values football and basketball, you can use the following to get the result:
select id
from yt
where program in ('basketball', 'football')
group by id
having count(distinct program) = 2;
See SQL Fiddle with Demo.
Since can also be done by joining on your table multiple times:
select t1.id
from yt t1
inner join yt t2
on t1.id = t2.id
where t1.program = 'basketball'
and t2.program = 'football';
See SQL Fiddle with Demo
I think aggregation is the most generalizable approach for this:
select id
from table
group by id
having sum(case when program = 'Football' then 1 else 0 end) > 0 and
sum(case when program = 'Basketball' then 1 else 0 end) > 0
The sum() statement are counting the number of rows that have "football" and "basketball" respectively. When present, the number is greater than 0.
You can do this with IN or OR syntax:
SELECT id
FROM table
WHERE program = 'basketball'
OR program = 'football';
If you want to only get the first two results, add LIMIT 2 to the end.
By the way, it's really bad practice to have a table without a primary key, there is no way to index this table so performance would be very bad.

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.