I have a database about old cars, their owners, events and the attendants of those events as such:
Image of tables used :
PK: Primary Key
FK: Foreign Key
Now I need to get the amount of different events (eventId) each member (memberId) has attended.
Important note: a member can have multiple cars that can each attend events.
Here is one of my attemps:
select m.memberId, count(a.eventId).
from members m where m.memberId in (select c.memberId from cars c where m.memberId =
c.memberId and c.carId in
(select d.carId from attendants a where c.carId = d.carId))
order by m.memberId
I've also tried using joins and group by to get a correct result but I am getting nowhere.
Does anyone know how i need to form the subquery so that i can get the results needed?
So you want distinct events the member has attended. Member has cars which attend the events. Since different cars can attend same events, you need to take distinct from events:
select m.memberId, count(distinct a.eventId)
from members m
join cars c on c.memberId = m.memberId
join attendants a on a.carId = c.carId
group by m.memberId
Not sure if you are using that exact statement, but your syntax has several issues. You're using aliases that don't exist (d.CarID - what table is d?) and you don't have a group by statement at the end, which tells the engine what columns you want to preserve the values from. Try something like this:
select member_ID,
count(*)
from (
select distinct a.eventID, m.memberID
from attendants a
inner join cars c
on a.carID = c.car_ID
inner join members m
on c.memberID = m.memberID
group by a.eventID, m.memberID
)
group by memberID
The inner query gets what you want - a big list of all the members who have attended via the cars they own, deduped so that if they had two cars at the same event they are only recorded once - which is then counted for each member.
Related
I have two tables:
Members (member_id, member_gender)
Orders (order_id, member_id, order_amount)
I have to retrieve data about the amount of members, amount of buyers (members with at least 1 order), amount of orders
But unfortunately I have a hard time because when i try to join these two tables i recieve dublicates and I am unable to count distinctive members
So my initial code was:SELECT count(m.member_id) AS Amount_of_members ,count(o.order_id) ,sum(o.order_amount) FROM tbl_member m LEFT JOIN tbl_order o ON m.member_id = o.member_id
You can ask the db to count only unique occurrences of the member id
SELECT count(DISTINCT m.member_id) AS Amount_of_members
You can also run a subquery to group the orders up so there is only one row per member before you join to the members table, which means the members data won't be doubled up if a member has 2 orders, tripled up if they have 3 etc
SELECT
count(m.member_id) AS Amount_of_members ,
sum(x.count_orders) as total_count_of_orders,
sum(x.sum_orders) as total_sum_of_orders
FROM
tbl_member m
LEFT JOIN (
SELECT
o.member_id,
count(o.order_id) as count_orders ,
sum(o.order_amount) as sum_orders
FROM
tbl_order o
GROUP BY o.member_id
)x ON m.member_id = x.member_id
Generally the "squash the many side of a 1:M relationship down to one row before the join is done" is a helpful way to manage the data, especially if there are multiple joins that need to be made. Getting everything 1:1 means no duplicates pop up
I have two tables: customers and contracts. The common key between them is customer_id. I need to link these two tables to represent if my fictitious business is on contract with a customer.
The customer -> contract table has a one to many relationship, so a customer can have an old contract on record. I want the latest. This is currently handled by contract_id which is auto-incremented.
My query is supposed to grab the contract data based on customer_id and the max contract_id for that customer_id.
My query currently looks like this:
SELECT * FROM(
SELECT co.*
FROM contracts co
LEFT JOIN customers c ON co.customer_id = c.customer_id
WHERE co.customer_id ='135') a
where a.contract_id = MAX(a.contract_id);
The answer is probably ridiculously obvious and I'm just not seeing it.
Since the most recent contract will be the one with the highest a.contract_id, simply ORDER BY and LIMIT 1
SELECT * FROM(
SELECT co.*
FROM contracts co
LEFT JOIN customers c ON co.customer_id = c.customer_id
WHERE co.customer_id ='135') a
ORDER BY a.contract_id DESC
LIMIT 1
You can use NOT EXISTS() :
SELECT * FROM contracts c
LEFT JOIN customers co
ON(co.customer_id = c.customer_id)
WHERE co.customer_id = '135'
AND NOT EXISTS(SELECT 1 FROM contracts co2
WHERE co2.customer_id = co.customer_id
AND co2.contract_id > co.contract_id)
This will make sure it's the latest contract, it is dynamic for all customers, you can just remove WHERE co.customer_id = '135' and you will get all the results.
In general, you can't use an aggregation function on the WHERE clause, only on the HAVING() which will be usually combined with a GROUP BY clause.
I got 3 tables, actor (id,name), movie (id,name) and casts(aid,mid,role) (aid is the actor id and mid is the movie id). I was trying to get the output like this:
if an actor had more than 3 distinct roles in the same movie, print all the combinations, like:
-1.actor.name, movie.name, role1
-2.actor.name, movie.name, role2
-3.actor.name, movie.name, role3
My query is like this:
select a.name, m.name, x.role
from actor a,
movie m,
(select distinct role
from casts c
where c.aid =a.id and c.mid = m.id
group by c.aid and c.mid
having count(distinct role) >=3) as x;
But I got error message:
The multi-part identifier "m.id" could not be bound.
The multi-part identifier "a.id" could not be bound.
Please point out where my thought went wrong, I want to be able to do this next time. Thanks.
Your initial query is close, but the problem is that you can only return a single column from a subquery, whereas your casts table has a composite key* of two foreign key columns.
Instead, you can do the hard work in a derived table (as you've done in your initial subquery). The benefit of the derived table over the subquery is that you can then join the other tables back to on the two columns to return the friendly column names:
select a.name, m.name, c.`role`
from
(
select aid, mid
from casts
group by aid, mid
having count(distinct `role`) >= 3
) x
inner join actor a
on a.id = x.aid
inner join movie m
on m.id = x.mid
inner join casts c
on x.mid = c.mid and x.aid = c.aid;
* actually, it isn't really a key either, given that the same actor can have multiple roles in the same movie. But we are looking for unique combinations, so its unique after we do the GROUP BY on mid, aid
SqlFiddle here - Duplicate Roles are ignored, and the threshold of 3 roles, same movie is observed.
Do a join between the 3 tables, and have a sub-select to verify at least 3 different roles:
select a.name, m.name, c.role
from actor a
join movie m on a.id = m.aid
join casts c on m.id = c.mid and c.aid = a.id
where a.id in (select aid from casts
where aid = a.id and mid = m.id
group by aid, mid
having count(distinct role) >=3)
This query is working fine. It gives a count of contest entrants for whom the contact id in contest_entries is their origin_contact in the person table.
SELECT c.handle, COUNT(*)
FROM `contest_entry` ce,
person p,
contest c
WHERE
p.origin_contact = ce.contact AND
c.id = ce.contest
GROUP BY c.id
I want to now query how many of those records also have at least one record where the contact id matches in email_list_subscription_log BUT that table may have many log records for any one contact id.
How do I write a join that gives me a count that is not inflated by the multiple records?
Should I use a version of my first query to get all of the contact ids into a tmp table and just use that?
Not sure which field is contact id, but you can do something like this:
select c.handle,
count(*) as count
from `contest_entry` ce
inner join person p on p.origin_contact = ce.contact
inner join contest c on c.id = ce.contest
where exists (
select 1
from email_list_subscription_log l
where l.contact_id = ce.contact
)
group by c.id
You ought to deflate the email_list_subscription_log with DISTINCT or GROUP:
SELECT c.handle, COUNT(*)
FROM `contest_entry` ce
JOIN person p ON (p.origin_contact = ce.contact)
JOIN contest c ON (c.id = ce.contest)
JOIN (SELECT DISTINCT contact, email FROM email_list_subscription_log ) AS elsuniq
ON (ce.contact = elsuniq.contact)
[ WHERE ]
GROUP BY c.id
Using GROUP in the subquery you might count the number of records while still returning one row per element:
JOIN (SELECT contact, count(*) AS elsrecords FROM email_list_subscription_log
GROUPY BY contact) AS elsuniq
With this JOIN syntax, the WHERE is not necessary, but I kept it there if you need additional filtering.
I have what I'm sure is quite a remedial question here, but I can't for the life of me get this simple join to work.
Basically, I have 3 tables:
MEMBERS (first_name,last_name),
MEMBER_TO_GROUP(member_id,group_id)
PAYMENTS (member_id, date, amount).
I'm looking to grab all payments from members in a specific group. By using only two of the tables, I can find all PAYMENTS of a specific group, without MEMBER information, or I can find all MEMBER information without PAYMENT information. However, when I attempt to add the third table, bad data is returned (e.g. I get members not in the group). This is the basic query i'm using:
SELECT
p.*,
m.first_name,
m.last_name
FROM
members m,
payments p,
member_to_group mg
WHERE
mg.group_id = 12
AND mg.member_id = p.member_id
AND m.member_id = p.member_id
I'm not sure where the disconnect is, but any assistance would be most appreciated.
I think this should get what you want:
SELECT p.*,
m.first_name,
m.last_name
FROM payments p
INNER JOIN members m
ON m.member_id = p.member_id
INNER JOIN member_to_group mg
ON mg.member_id = m.member_id
WHERE mg.group_id = 12