SQL statement for join but not in other table - mysql

I have two tables: customer and mailing :
+==========+ +=============+
| customer | | mailing |
+==========+ +=============+
| id | | id |
+----------+ +-------------+
| name | | customer_id |
+----------+ +-------------+
| mailing_id |
+-------------+
Every time I send a mailing to a customer, I add a row in the mailings table with that mailing id. One mailing can be sent to multiple customers.
I want to have a sql call that returns all customers that have not yet received a certain mailing. How to ?
I am using mysql

select * from customer where id not in (
select customer_id from mailing where mailing_id = #mailing_id
)

Something like
Select c.ID, c.Name
From Customers C
left Join mailing m On c.id = m.customer_id
where m.customer_id is null

SELECT * FROM customers c
JOIN mailings m
ON c.id = m.id
WHERE NOT EXISTS (
SELECT id
FROM mailings i
WHERE i.id = c.id
GROUP BY i.id
)

What you describe is called an ANTI JOIN. Usually there are three different ways for formulating it in SQL: A NOT IN condition with a subquery, a NOT EXISTS condition with a correlated subquery, or a LEFT JOIN with a NOT NULL condition.
So for your query the possibilities are:
SELECT *
FROM customer
WHERE id NOT IN
( SELECT customer_id
FROM mailing)
SELECT *
FROM customer c
WHERE NOT EXISTS
( SELECT customer_id
FROM mailing m
WHERE m.customer_id = c.id)
SELECT *
FROM customer c
LEFT JOIN mailing m ON c.id = m.customer_id
WHERE m.customer_id IS NULL
This blog post compares the different possibilities with MySQL 5.1. According to it, LEFT JOIN / IS NULL and NOT IN are faster than NOT EXISTS.
However, you should try for yourself which one is the fastest. That always depends on your data, indexes, ...

Related

How to join the same table more than once to select different columns?

I need to find out users who have either made or received a booking.
I have two tables that look like this:
Users:
+----+
| id |
+----+
| 1 |
| 2 |
| 3 |
+----+
Bookings:
+----+-----+-----+
| id | rid | oid |
+----+-----+-----+
| 1 | 1 | 2 |
| 2 | 2 | 1 |
| 3 | 3 | 4 |
+----+-----+-----+
A booking has two users, a 'rider' (rid), and an 'owner' (oid).
The rider and owner can't be the same for each booking but riders can also be owners.
My output should be a list of user IDs that correspond with users who have made or received a booking.
So far I have written
select u.id, b1.rid, b2.oid
from users u
left join bookings b1
on u.id = b1.rid
left join bookings b2
on u.id = b2.oid;
And various other permutations, but I'm not getting the desired result. Any help would be appreciated.
You want all User IDs that are either in Bookings.rid or Bookdings.oid. So you could do something like:
select
users.id
from
users
where
users.id in (select bookings.rid from bookings)
or
users.id in (select bookings.oid from bookings);
You should be able to utilize a UNION clause here.
However, you don't define what the "time window" is, so I am not sure we can come up with a complete solution for you. However, try something like the following:
SELECT
users.id,
bookings.rid,
bookings.oid
FROM
users
LEFT JOIN bookings ON users.id = bookings.rid
UNION ALL
SELECT
users.id,
bookings.rid,
bookings.oid
FROM
users
LEFT JOIN bookings ON users.id = bookings.oid
My output should be a list of user IDs that correspond with users who have made or received a booking.
To do that, you only need to look at the bookings table :
SELECT DISTINCT rid id FROM bookings
UNION ALL SELECT DISTINCT oid FROM bookings
The DISTINCT removes the duplicates returned by each query, and the UNION ALL removes duplicates across both queries.
If you are looking to filter by time frame :
SELECT DISTINCT rid id FROM bookings WHERE some_date BETWEEN :start_date AND :end_date
UNION ALL SELECT DISTINCT oid FROM bookings WHERE some_date BETWEEN :start_date AND :end_date
Where some_date is the field that contains the booking date, and :start_date/end_date are the beginning and the end of the date interval.
I guess there is a name column in Users table.
If you want this too then:
select users.id, users.name from (
select rid userid from bookings
union
select oid userid from bookings
) t inner join users
on users.id = t.userid
group by users.id, users.name
See the demo
If not you only need to scan the bookings table:
select distinct userid from (
select rid userid from bookings
union
select oid userid from bookings
) t
See the demo

Count comments and get average rating from mysql

I just can't figure out how to get average rating and count comments from my mysql database.
I have 3 tables (activity, rating, comments) activity contains the main data the "activities", rating holds the ratings and comments - of course, the ratings.
activity_table
id | title |short_desc | long_desc | address | lat | long |last_updated
rating_table
id | activityid | userid | rating
comment_table
id | activityid | userid | rating
I'm now trying to the data from activity plus the comment_counts and average_rating in one query.
SELECT activity.*, AVG(rating.rating) as average_rating, count(comments.activityid) as total_comments
FROM activity LEFT JOIN
rating
ON activity.aid = rating.activityid LEFT JOIN
comments
ON activity.aid = comments.activityid
GROUP BY activity.aid
...doesn't do the job. It gives me the right average_rating, but the wrong amount of comments.
Any ideas?
Thanks a lot!
You are aggregating along two different dimensions. The Cartesian product generated by the joins affects the aggregation.
So, you should aggregate before the joins:
SELECT a.*, r.average_rating, COALESCE(c.total_comments, 0) as total_comments
FROM activity a LEFT JOIN
(SELECT r.activityid, AVG(r.rating) as average_rating
FROM rating r
GROUP BY r.activityid
) r
ON a.aid = r.activityid LEFT JOIN
(SELECT c.activityid, COUNT(*) as total_comments
FROM comments c
GROUP BY c.activityid
) c
ON a.aid = c.activityid;
Notice that the outer GROUP BY is no longer needed.

how to select one table then join another table mysql

so i have two tables.
users
users_id | firstname | lastname
10001 | mike | lapiz
10002 | tom | jerry
profile
profile_id | department | specialization
10001 | Health Dept | Heart
10002 | Brain Dept | Brain
maybe you're curious why i separate the name of the user and the profile.. i have my on reasons for that.. what i wanted to do is to select my all fields from profile then join the users table
what i want to be the result is
users_id | firstname | lastname | profile_id | department |specialization
10001 | mike | lapiz | 1001 | health dept | heart
this is my query..
$sql = SELECT a.profile_id,a.department,a.specialization FROM `tbl_profile` AS a LEFT JOIN (SELECT users_id,firstname,lastname FROM `tbl_users`) AS b ON a.profile_id = b.users_id
what happen is it only display the profile table.. it is not displaying the other table.. and when i tried to
LEFT JOIN (SELECT b.users_id,b.firstname,b.lastname FROM `tbl_users`) AS b
it give me an error unknown column b.users_id
You misunderstand how a join works.
FROM tbl_profile JOIN tbl_users ON ...
joins the two tables, i.e. combines records on the given condition in ON.
FROM tbl_profile JOIN (SELECT * FROM tbl_users)
does exactly the same. It makes no difference if you join a table directly or join the records of the table, because this means exactly the same.
FROM tbl_profile JOIN (SELECT users_id, firstname, lastname FROM tbl_users)
again does the very same thing. Only that you restrict the columns you can use in the query to the three stated columns. So if there existed more columns in the table, you could not use them in the query's select or where or order by clause anymore.
So a join means just combining records. Which columns you want to show, you put in the select clause:
SELECT * FROM tbl_profile JOIN tbl_users ON ...
selects all columns from both tables.
SELECT p.department FROM tbl_profile p JOIN tbl_users u ON ...
selects only the department.
You want:
SELECT * FROM tbl_users u JOIN tbl_profile p ON p.profile_id = u.user_id
A LEFT JOIN by the way is an outer join where you keep the records from the left table in your results even when there is no match in the right table. In your query you said that you wanted to show profile records too that have no match in the users table, which was certainly not intended.
You should use inner join not nested inner join
$sql = SELECT a.profile_id,a.department,a.specialization,b.users_id,b.firstname,
b.lastname FROM tbl_profile AS a inner join tbl_users b
ON a.profile_id = b.users_id
As you want to list all the columns in both the tables with LEFT OUTER JOIN, the following query will serve your purpose:
SELECT * FROM users LEFT OUTER JOIN profile on users.users_id = profile.profile_id
You can use the alias as well if you want as following:
SELECT * FROM users u LEFT OUTER JOIN profile p on u.users_id = p.profile_id

mysql multi count() in one query

I'm trying to count several joined tables but without any luck, what I get is the same numbers for every column (tUsers,tLists,tItems). My query is:
select COUNT(users.*) as tUsers,
COUNT(lists.*) as tLists,
COUNT(items.*) as tItems,
companyName
from users as c
join lists as l
on c.userID = l.userID
join items as i
on c.userID = i.userID
group by companyID
The result I want to get is
---------------------------------------------
# | CompanyName | tUsers | tlists | tItems
1 | RealCoName | 5 | 2 | 15
---------------------------------------------
what modifications do i have to do to my query to get those results?
Cheers
Try this
SELECT u.userID, companyName,
Count(DISTINCT l.listid) as tLists, Count(DISTINCT i.items) as tItems
FROM users u
LEFT JOIN lists l ON u.userID=l.userID
LEFT JOIN items i ON u.userID=i.userID
GROUP BY u.companyID
You can do it by using sub query
select (select count(*) from users where userID=YourUserID) tUsers,
(select count(*) from lists where userID=YourUserID) as tLists,
(select count(*) from items where userID=YourUserID) as tItems,
companyName
from company group by companyID

Combine Multiple Query Results in MySQL (by column)

I have 4 different queries and each of them return individual unique set of Results.
I need to combine the Query Results with using a single query.
my sample queries are:
1. select cls.* from (calls as cls inner join calls_users as clsusr on cls.id=clsusr.call_id) inner join users as usr on usr.id=cls.assigned_user_id where cls.assigned_user_id='seed_max_id'
2. select mtn.* from (meetings as mtn inner join meetings_users as mtnusr on mtn.id=mtnusr.meeting_id) inner join users as usr on usr.id=mtn.assigned_user_id where mtn.assigned_user_id='seed_max_id'
3. select tsk.* from tasks as tsk inner join users as usr on usr.id=tsk.assigned_user_id where tsk.assigned_user_id='seed_max_id'
4. select nts.* from (notes as nts inner join accounts as acnts on acnts.id=nts.parent_id) inner join users as usr on usr.id=acnts.assigned_user_id where acnts.assigned_user_id='seed_max_id'
I tried the following way, but it didn't work
Combine: SELECT tbl1.*, tbl2.*
from (select cls.* from (calls as cls inner join calls_users as clsusr on cls.id=clsusr.call_id) inner join users as usr on usr.id=cls.assigned_user_id where cls.assigned_user_id='seed_max_id') as tbl1
left outer join
(select mtn.* from (meetings as mtn inner join meetings_users as mtnusr on mtn.id=mtnusr.meeting_id) inner join users as usr on usr.id=mtn.assigned_user_id where mtn.assigned_user_id='seed_max_id') as tbl2
using(assigned_user_id)
i also tried right outer join and other inner joins
I am really stuck, if any one know the solution then please help.
I need the similar result like How can I join two tables with different number of rows in MySQL?.
Data Sample:
From Query 1:
+-------------------------------------------+------------------+-
| Call Name | Call Description |
+-------------------------------------------+------------------+-
| Discuss Review Process | NULL |
| Get More information on the proposed deal | NULL |
| Left a message | NULL |
| Discuss Review Process | NULL |
+-------------------------------------------+------------------+
From Query 2:
+-----------------------+-----------------------------------------------------------
| Meeting Name | Meeting Description
+-----------------------+-----------------------------------------------------------
| Review needs | Meeting to discuss project plan and hash out the details o
| Initial discussion | Meeting to discuss project plan and hash out the details o
| Demo | Meeting to discuss project plan and hash out the details o
| Discuss pricing | Meeting to discuss project plan and hash out the details o
| Review needs | Meeting to discuss project plan and hash out the details o
+-----------------------+-----------------------------------------------------------
i need to combine the columns like the following:
+-------------------------------------------+------------------+-------------------+-------------------+
| Call Name | Call Description |Meeting Name |Meeting Description|
+-------------------------------------------+------------------+-------------------+-------------------+
| Discuss Review Process | NULL |Review needs |Meeting to discuss |
| Get More information on the proposed deal | NULL |Initial discussion |Meeting to discuss |
| Left a message | NULL |Demo |Meeting to discuss |
| NULL | NULL |Discuss pricing |Meeting to discuss |
| NULL | NULL |Review needs |Meeting to discuss |
+-------------------------------------------+------------------+-------------------+-------------------+
The best you can do is a UNION or UNION ALL but this requires them to have the same type and number of columns. For example:
SELECT 'Customer' AS type, id, name FROM customer
UNION ALL
SELECT 'Supplier', id, name FROM supplier
UNION ALL
SELECT 'Employee', id, full_name FROM employee
The column names don't have to match. The aliases from the first part will be used for the rest.
I'll also add that instead of:
select cls.* from (calls as cls inner join calls_users as clsusr on cls.id=clsusr.call_id) inner join users as usr on usr.id=cls.assigned_user_id where cls.assigned_user_id='seed_max_id'
you should remove the unnecessary subquery and just do:
SELECT c.*
FROM calls c
JOIN calls_users cu ONc.id = cu.call_id
WHERE c.assigned_user_id = 'seed_max_id'
There's no need for the extra complexity and the above is eminently more readable.
I assume you want your example to return a single row combining the corresponding entries from all these tables. Try this and tell us if it worked:
select * from users as usr
left outer join (calls as cls
inner join calls_users as clsusr
on cls.id = clsusr.call_id)
on usr.id = cls.assigned_user_id
left outer join (meetings as mtn
inner join meetings_users as mtnusr
on mtn.id = mtnusr.meeting_id)
on usr.id = mtn.assigned_user_id
left outer join tasks as tsk
on usr.id = tsk.assigned_user_id
left outer join (notes as nts
inner join accounts as acnts
on acnts.id=nts.parent_id)
on usr.id = acnts.assigned_user_id
where user.id = 'seed_max_id'