Counting values in columns - sql

What I am looking for is to group by and count the total of different data in the same table and have them show in two different columns. Like below.
Data in table A
Fields:
Name Type
Bob 1
John 2
Bob 1
Steve 1
John 1
Bob 2
Desired result from query:
Name Type 1 Type 2
Bob 2 1
John 1 1
Steve 1 0

This will do the trick in SQL Server:
SELECT
name,
SUM( CASE type WHEN 1 THEN 1 ELSE 0 END) AS type1,
SUM( CASE type WHEN 2 THEN 1 ELSE 0 END) AS type2
FROM
myTable
GROUP BY
name

No time to write the code, but the Case statement is what you want here. SImply havea value of 1 if it meets the case and zero if it deosn't. Then you can sum the columns.

Use two separate GROUP BY subqueries.
SELECT Name, a.Count1, b.Count2
from myTable
JOIN
(SELECT Name, SUM(Type) AS Count1 FROM myTable GROUP BY Name WHERE Type=1) AS a ON a.Name = myTable.Name
(SELECT Name, SUM(Type) FROM myTable GROUP BY Name WHERE Type=2) AS b ON b.Name = myTable.Name

You're looking for a CrossTab solution. The above solutions will work, but you'll come unstuck if you want a general solution and have N types.
A CrossTab solution will solve this for you. If this is for quickly crunching some numbers then dump your data into Excel and use the native Pivot Table feature.
If it's for a RDBMS in an app, then it depends upon the RDBMS. MS SQL 2005 and above has a crosstab syntax. See:
http://www.databasejournal.com/features/mssql/article.php/3521101/Cross-Tab-reports-in-SQL-Server-2005.htm

#Seb has a good solution, but it's server-dependent. Here's an alternate using subselects that should be portable:
select
name,
(select count(type) from myTable where type=1 and name=a.name) as type1,
(select count(type) from myTable where type=2 and name=a.name) as type2
from
myTable as a
group by
name

Related

Categorising "group by" groups by their contents

I have a view which is a product of two joined tables:
ID Type
1 A
2 A
2 B
3 B
There can only be two values in Type column: A or B.
I would like to aggregate IDs into three categories: Catgegory_A, Category_B and Category_AB. If the ID is associated only with type A, it is assigned Category_A, if the ID is associated with types A and B it is associated with Categry_AB. Based on these rules, the view above should be categorised as follows:
ID Category
1 Category_A
2 Category_AB
3 Category_C
Is it possible to write an SQL query to achieve this?
I would name them differently, but the logic is:
select id,
(case when min(category) = max(category)
then 'Category_' || min(category)
else 'Category_AB'
end)
from t
group by id;
Independently of Gordon's answer, I came up with the following...
SELECT ID,
CASE
WHEN COUNT(*) > 1 THEN 'AB'
ELSE MAX(Type)
END AS Category
FROM Products
GROUP BY ID
See SQLFiddle to run :)

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;

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 Query where a specific column should have two specific values but have just one

Sorry for the cryptic title, but i didn't find a better question
I have a Table with lets say these Data:
Table Article
ID ArticleNumber Type
1 10 1
2 10 3
3 20 1
4 30 1
5 30 3
I'm looking for the 3. Row where no type 3 article exists but a type 1 article exists.
I think it have to be a very easy SQL query but i can't find a solution...
To get all ArticleNumbers for which exists a row with type = 1 but there is no row with type = 3, use this:
SELECT
*
FROM
Article a
WHERE
a.`Type` = 1
AND NOT EXISTS (
SELECT * FROM
Article a2
WHERE
a2.`Type` = 3
AND a2.ArticleNumber = a.ArticleNumber
)
You can select the first ID by aggregating the original data (see subquery), then you can filter those records where could be any anomalies.
If you want, you can change the aggregation to COUNT, then you can compare the count for each types.
SELECT
ArticleNumber
FROM (
-- Conditional aggregation to find the first ID for each type per ArticleNumber
SELECT
ArticleNumber
, MIN(CASE WHEN Type = 1 THEN ID ELSE NULL END) AS Type_1
, MIN(CASE WHEN Type = 3 THEN ID ELSE NULL END) AS Type_3
GROUP BY
ArticleNumber
) AS X
WHERE
Type_1 IS NULL
OR Type_2 IS NULL
Note You can change the above query to use the HAVING keyword instead of subquery if you want.
Another variant, using MINUS to remove the articles with Type 3:
select ID, ArticleNumber from Article where type = 1
MINUS
select ID, ArticleNumber from Article where type = 3
UPDATE
After re-reading your question, I guess you want to also have those with Type = 3, but where no row with Type = 1 exists. For this, you can use COUNT DISTINCT:
select * from (
select ID, ArticleNumber, count(distinct type) as type_cnt
from Article
group by ID, ArticleNumber
)
where type_cnt < 2
You mean you want all rows where type is not 3?
That would be this statement:
SELECT
*
FROM
`Article`
WHERE
`type` != 3;
After re-reading your question I understand your problem. I would go with Eriks solution.