SQL GROUP BY "HAVING" the required rows - mysql

Is there a succinct way of using HAVING to check if the required rows are within the GROUP BY?
With the example date:
turtle_id name
1 Mike
2 Ralph
3 Leo
4 Don
and
turtle_id crush_for
1 Pizza
1 April Oneil
2 April Oneil
3 Pizza
3 April Oneil
4 Pizza
4 Pizza
4 Science
4 April Oneil
And the SQL:
SELECT turtle.name
FROM turtle
JOIN turtle_crush ON turtle_crush.turtle_id = turtle.turtle_id
WHERE turtle_crush.crush_for IN ('Pizza', 'April Oneil')
GROUP BY turtle.turtle_id
HAVING (a crush on both "Pizza" and "April Oneil")
I realize I could do something like HAVING COUNT(*) > 1, but that would give a false positive for Don (id 4) because he likes 'Pizza' twice.
Edit:
Just adding a WHERE clause will return Ralph where he doesn't have a crush_for 'Pizza'

This should work:
SELECT t.turtle_name
FROM turtle t
INNER JOIN (SELECT turtle_id
FROM turtle_crush
WHERE crush_for IN ('Pizza','April Oneil')
GROUP BY turtle_id
HAVING COUNT(DISTINCT crush_for) = 2) tc
on t.turtle_id = tc.turtle_id;
In this code, the subquery first filter the results where crush_for is either 'Pizza' or 'April Oneil'. Then it groups by turtle_id, with another condition of choosing those turtle_ids that have 2 different crush_for values (hence ensuring that you get only the turtle_ids that have both crushes). Then it's joined with the turtle table to get the name.

Put the list of crushes in the WHERE clause, group by turtle ID, count the distinct values of crush types then keep only the groups that have at least 2 values (or how many crushes you put in the query):
SELECT turtle.name
FROM turtle
INNER JOIN turtle_crush ON turtle_crush.turtle_id = turtle.turtle_id
WHERE crush_for IN ("Pizza", "April Oneil")
GROUP BY turtle.turtle_id
HAVING COUNT(DISTINCT crush_for) = 2

Related

how can combine two columns in the same table and count its unique occurrences

I have been struggling and would really appreciate some assistance:
I have two tables cars and rides
cars
car_id car_manuf car_model
1 Honda CRV
2 Honda Accord
3 Toyota Corolla
4 Toyota Camry
5 Ford Fusion
rides
ride_id car_id ride_destination
1 3 Boston
2 5 New York
3 5 Washington DC
4 1 California
5 2 Dallas
6 5 Canada
I would like to count the number of rides by each car type which will have the combination of car_manuf and car_model and should be sorted from most to fewest number of rides.
Output should be:
CarType-NumberofRides
Honda_CRV-1
Honda_Accord-1
Toyota_Corolla-1
Toyota_Camry-0
Ford_Fusion-3
Sorted output with most-few rides
CarType-NumberofRides
Toyota_Camry-0
Honda_Accord-1
Toyota_Corolla-1
Honda_CRV-1
Ford_Fusion-3
mycode:
select
c.car_manuf + '_' + c.car_model AS 'Car Type',
(select count(*) from rides r where r.car_id = c.car_id) AS 'Number of Rides'
from cars c;
I am kinda stuck here and not sure which direction I should go in regards to getting the correct output.
You have to use GROUP BY and an ORDER BY when COUNTing the occurences. I use CONCAT to concatenate the strings instead of a + sign. Makes clearer what is going on, and is not mistaken as an arithmetic operation.
SELECT CONCAT(c.car_manuf, '_', c.car_model) AS CarType, COUNT(r.car_id) AS NumberOfRides
FROM rides r
LEFT JOIN cars c ON (r.car_id = c.car_id)
GROUP BY CarType
ORDER BY NumberOfRides ASC
However this omits the 0 occurences.
If you want to see the 0s as well swap the table order to:
SELECT CONCAT(c.car_manuf, '_', c.car_model) AS CarType, COUNT(r.car_id) AS NumberOfRides
FROM cars c
LEFT JOIN rides r ON (r.car_id = c.car_id)
GROUP BY CarType
ORDER BY NumberOfRides ASC

How to join multiple queries with different tables and different column name

I want to join multiple queries with different tables and column name, along with I need to display the count of duplicate fields as shown below.
The queries are: (Proj_uid is common in all the tables which I need to match)
select proj_name,Agency,District,Division,Proj_status from tempproj
Need to join 2 tables to get the result that is payment80 and payment20 which contains billtype column with duplicate values, I want to count those value too
SELECT p.Proj_name,p.billtype, COUNT(1) as CNT
FROM payment80 p where billtype='civil'
GROUP BY Proj_name, billtype
(This is by using single table but I want this result by joining both payment80 and payment20 tables)
SELECT p.Proj_name,p.billtype, COUNT(1) as CNT
FROM payment80 p where billtype='Electric'
GROUP BY Proj_name, billtype
(The billtype values I want to count and just display a number of duplicate records)
Proj_Name billtype
------------------------
policegruha civil
gruhayojna Electric
policegruha civil
dcoffice civil
spoffice Electric
dcoffice civil
3) Select billtype from payment, here also I need count the duplicate values and display in billtype
Duplicate values will be in billtype which contains some thing like this:
Finally I want an output like this:
Proj_name Agency District Division Projstatus Civilbilltype Electricbilltype
policegruha kumar chitradurga davangere ongoing 3 1
gruhayojna khan ballary ballary completed 2 2
Atered john bangalore bangalore ongoing 2 4
dcoffice ravi mangalore mangalore ongoing 1 2
spoffice mary chitradurga davangere completed 3 4
hostel jack ballary ballary completed 3 3
univercity kumar bangalore bangalore ongoing 4 2
mess Raj mysore mysore ongoing 2 1
policestation khan mysore mysore ongoing 1 4
conferencehall Rosy davangere davangere ongoing 2 2
You are joining three separate tables. One is physical, tempproj, and the other two are virtual: they are aggregates.
This is the technique.
SELECT p.proj_name,p.Agency,p.District,p.Division,p.Proj_status,
Civilbills.billcount as Civilbills,
Electribills.billcount as Electricbills
FROM tempproj p
LEFT JOIN (
SELECT Proj_name, COUNT(*) as billcount
FROM payment80
where billtype='civil'
GROUP BY Proj_name
) Civilbills ON Civilbills.Proj_name = p.proj_name
LEFT JOIN (
SELECT Proj_name, COUNT(*) as billcount
FROM payment80
where billtype='Electric'
GROUP BY Proj_name
) Electricbills ON Electricbills.Proj_name = p.proj_name
Your requirement includes two separate aggregates from the payment80 table. The LEFT JOINs prevent suppression of project rows that lack any bills of either category.

SQL join with self

I have a table with ID numbers of people and then items of food they've ordered:
table "food_id"
food id
ham 1
cheese 2
turkey 2
ham 3
ham 4
bread 5
cheese 6
turkey 6
cheese 7
And I'd like to use SQL to figure out, for each id, the total number of other IDs who ordered at least one of the same food items. For the above example, the answer should be:
"result_table"
count id
3 1
3 2
3 3
3 4
1 5
3 6
3 7
The challenge is to avoid double counting here. For example, person number 2 got both cheese and turkey, so we want his final count to be 3 because person # 2, 6, and 7 got cheese, and person # 2 and 6 got turkey, and there are 3 unique IDs in this list of (2,6,7,2,6).
My initial thoughts were to first get a table with food items to distinct ID numbers, and then to join this table with the original table and then group by ID number and get a count of the distinct number of IDs. However, I'm a beginner to SQL and can't figure out how to implement the code correctly.
Any direction would be greatly appreciated.
To avoid the problem with the double counting you can concat both ids from the join and count only distinct combinations. I add a separator to make the combination unique with greater id values:
SELECT
COUNT(DISTINCT CONCAT(f1.id, ',', f2.id)) as count,
f1.id
FROM
food_id f1
INNER JOIN
food_id f2
ON
f1.food = f2.food
GROUP BY f1.id;
See demo
Like you said, you can do a self join. You can join by food, and count the number of distinct matching ids.
select
a.id, -- Person you're investigating
count(distinct b.id) as samefoodcount -- number of people sharing the same food
from
food_id a
inner join food_id b on b.food = a.food
group by
a.id
Here you can see the query in action: http://sqlfiddle.com/#!2/c53884/1
You can run the following query to get the desired output:
select MAX(T.total),id
from table_name, (select count(*) as total,food from table_name group by food) T
where table_name.food=T.food
group by id
Check the DEMO

MySQL order by points from 2nd table

So I have MySQL 3 tables, items (which in this case are lodging properties and the data is simplified below), amenities that the properties might offer, and amenities_index which is a list of item ids and amenity ids for each amenity offered. The end user can select any number of amenities they want and I want to return the results in order of the number of amenities that match what they are looking for. So, if they search for 3 different amenities, I want the items listed that offer all 3, then those that offer 2, 1 and finally the rest of the items. I have a query that I think is working for getting the results in the correct order, but I was hoping that I could also return a point value based on the matches, and that's where I'm running into trouble. My SQL skills are a bit lacking when it comes to more complex queries.
Here is an example query I have that returns the results in the correct order:
SELECT * FROM items
ORDER BY
(
SELECT count(*) AS points
FROM `amenities_index`
WHERE
(amenity_id = 1 || amenity_id = 2)
AND amenities_index.item_id = items.id
) DESC
And here is what the tables are structured like. Any help is appreciated.
items table
id name
1 location 1
2 location 2
3 location 3
4 location 4
amenities table
id name
1 fireplace
2 television
3 handicapped accessible
4 kitchenette
5 phone
amenities_index
item_id amenity_id
1 2
1 3
1 5
2 1
2 2
2 6
3 2
3 3
3 4
3 5
You want to move your expression into the select clause:
SELECT i.*,
(SELECT count(*) AS points
FROM `amenities_index` ai
WHERE amenity_id in (1, 2) AND
ai.item_id = i.id
) as points
FROM items i
ORDER BY points desc;
You can also do this as a join query with aggregation:
SELECT i.*, ai.points
FROM items i join
(select ai.item_id, count(*) as points
from amenities_index ai
where amenity_id in (1, 2)
) ai
on ai.item_id = i.id
ORDER BY ai.points desc;
In most databases, I would prefer this version over the first one. However, MySQL would allow the first in a view but not the second, so it has some strange limitations under some circumstances.

two queries give different results in the same database

Please help. I'm using MySQL 5.1.30 Community Edition.
I have four tables: nts, operator, country, cooperationtype
table `nts` has one column(`operatorId`) which is a foreign key to column `id` in table `operator` and one column(`voice`) which is a foreign key to column `id` in table cooperationtype
table operator has one column(`country_id`) which is a foreign key to column (`id`) in table country
I want to get the counts of operators and countries where all of the value of voice not equals to 'N/A' and grouped them by cooperationtype.id with this query:
SELECT cooperationtype.id AS cooptype,
COUNT(DISTINCT country_id) AS country, COUNT(DISTINCT operatorId) AS operator
FROM nts INNER JOIN operator ON operator.id = nts.operatorId INNER JOIN country ON operator.country_id = country.id
INNER JOIN cooperationtype ON cooperationtype.id = nts.voice
WHERE cooperationtype.code <> 'N/A' GROUP BY cooperationtype.id
I got this result:
cooptype country operator
1 128 348
2 11 11
3 15 17
The sum of this query is 154 countries and 376 operators.
But then when I want to get all of the counts of operators and countries where all of the value of voice not equals to 'N/A', regardless the of cooperationtype.id with this query:
SELECT COUNT(DISTINCT country_id) AS country, COUNT(DISTINCT operatorId) AS operator
FROM nts INNER JOIN operator ON operator.id = nts.operatorId INNER JOIN country ON operator.country_id = country.id
INNER JOIN cooperationtype ON cooperationtype.id = nts.voice
WHERE cooperationtype.code <> 'N/A'
I got this result:
country operator
133 372
My questions are:
Why is the sum of the result from the first query doesn't equal to the result from the second query?
Which one is the right result?
Data example:
voice country operator
1 US 1
1 US 2
1 UK 3
1 UK 4
2 US 1
2 US 2
For the first query, the data should generate:
cooptype country operator
1 2 4
2 2 2
For the second query, the data should generate:
country operator
2 4
Why is the sum of the result from the first query doesn't equal to the result from the second query?
Because you use COUNT(DISTINCT).
It counts distinct records group-wise.
Your first query counts two records with the same country but different cooptype twice (since it groups by cooptype), while the second one counts them once.
Which one is the right result?
Both are right.
For the given data:
cooptype country
1 US
1 US
1 UK
1 UK
2 US
2 US
the first query will return:
1 2
2 1
and the second will return
2
, since you have:
2 distinct countries in cooptype = 1 (US and UK)
1 distinct country in cooptype = 2 (US)
2 distinct countries overall (US and UK)
Which is "right" in your definition of "right", depends, well, on this definition.
If you just want the second query to match the results of the first one, use
SELECT COUNT(DISTINCT cootype, country_id) AS country,
COUNT(DISTINCT cooptype, operatorId) AS operator
FROM nts
INNER JOIN
operator
ON operator.id = nts.operatorId
INNER JOIN
country
ON operator.country_id = country.id
INNER JOIN
cooperationtype
ON cooperationtype.id = nts.voice
WHERE cooperationtype.code <> 'N/A'
but, again, this may be as wrong as your first query is.
For these data:
cooptype country operator
1 US 1
1 US 1
1 UK 2
1 UK 2
2 US 1
2 US 1
, what would be a correct resultset?