I have 3 InnoDB tables: emails, websites and subscriptions.
emails table has id and email columns.
websites table has id and address columns.
subscriptions table has id, email_id and website_id columns.
What I'm tring to do is supply an email and return a table with columns address and subscribed. The former is a list of all the addresses in the websites table and the latter gets value 1 if the supplied email address has an occurence in the subscriptions table with website_id set to that website, or 0 otherwise. But I'm willing to retain all the websites even if the user is not found.
The point I'm stuck is where I should change the value of the virtual column subscribed from 0 to 1 when that email has that record.
Here's my query so far. Does anybody know how to do this?
SELECT `address`, "0" AS `subscribed`
/* 0 becomes 1 for the websites email has subscribed to */
FROM `websites` a
LEFT JOIN (
SELECT s.`website_id` FROM `subscriptions` s
RIGHT JOIN (
SELECT `id` AS `email_id` FROM `emails`
WHERE `email`='someone#mail.com' LIMIT 1) e
ON s.`email_id`=e.`email_id`) l
ON l.`website_id`=a.`id`
And here are the example outputs for the desired values for the subscribed column:
If email is not found in the emails table all the rows get value 0
If email is found in the emails table...
if it is not found in subscriptions table all the rows get value 0
if it is found in subscriptions table, the appropriate address rows get value 1
Let me know if I couldn't wxplain it well. Does anytbody know what I should alter in my query?
Thanks in advance.
SELECT w.address, CASE WHEN s.id IS NULL THEN 0 ELSE 1 END AS subscribed
FROM websites w
LEFT JOIN subscriptions s
INNER JOIN emails e
ON s.email_id = e.id
AND e.email = 'someone#mail.com'
ON w.id = s.website_id
You could also come up with the subscribed value this way, which is a bit more concise but also somewhat less obvious.
SELECT w.address, COALESCE(s.id/s.id, 0) AS subscribed
FROM websites w
LEFT JOIN subscriptions s
INNER JOIN emails e
ON s.email_id = e.id
AND e.email = 'someone#mail.com'
ON w.id = s.website_id
Related
Suppose I have 3 different tables relationships as following
1st is tbl_users(id,gender,name)
2nd is tbl_feeds(id,user_id,feed_value)
3rd is tbl_favs(id,user_id,feed_id)
where id is primary key for every table.
Now suppose I want to get data where those feeds should come which is uploaded by Gender=Male users with one field in every row that should say either the user who is calling this query marked that particular feed as favourite or not.
So final data of result should be like following :
where lets say the person who is calling this query have user_id=2 then is_favourite column should contain 1 if that user marked favourite that particular feed otherwise is_favourite should contain 0.
user_id feed_id feed_value is_favourite gender
1 2 xyz 1 M
2 3 abc 0 M
3 4 mno 0 M
I hope you getting my question , I m able to get feeds as per gender but problem is I m facing problem to get is_favourite flag as per particular user for every feed entry.
I hope some one have these problem before and I can get help from those for sure.
I would be so thankful if some one can resolve my this issue.
Thanks
Something like this should work:
SELECT
u.id AS user_id.
fe.id AS feed_id,
fe.feed_value,
IFNULL(fa.is_favourite, 0),
u.gender
FROM
tbl_users u
JOIN
tbl_feeds fe ON (fe.user_id = u.id)
LEFT JOIN
tbl_favs fa ON (
fa.user_id = u.id
AND
fa.feed_id = fe.id
)
In order to link your tables, you need to find the most common link between them all. This link is user_id. You'll want to create a relationship between all tables with JOIN in order to make sure each and every user has data.
Now I don't know if you're planning on making sure all tables have data with the user_id. But I would use INNER JOIN as it will ONLY show records of that user_id without nulls. If the other tables could POSSIBLY (Not always guaranteed) you should use a LEFT JOIN based on the tables that is it possible with.
Here is an SQLFiddle as an example. However, I recommend you name your ID fields as appropriate to your table's name so that way, there is no confusion!
To get your isFavorite I would use a subquery in order to validate and verify if the user has it selected as a favorite.
SELECT
u.userid,
u.gender,
f.feedsid,
f.feedvalue,
(
SELECT
COUNT(*)
FROM
tbl_favs a
WHERE
a.userid = u.userid AND
a.feedsid = f.feedsid
) as isFavorite
FROM
tbl_users u
INNER JOIN
tbl_feeds f
ON
u.userid = f.userid
~~~~EDIT 1~~~~
In response to your comment, I have updated the SQLFiddle and the query. I don't believe you really need a join now based on the information given. If you were to do a join you would get unexpected results since you would be trying to make a common link between two tables that you do not want. Instead you'll want to just combine the tables together and do a subquery to determine from the favs if it is a favorite of the user's.
SQLFiddle:
SELECT
u.userid,
f.feedsid,
u.name,
u.gender,
f.feedvalue,
(
SELECT
COUNT(*)
FROM
tbl_favs a
WHERE
a.userid = u.userid AND
a.feedsid = f.feedsid
) as isFavorite
FROM
tbl_users u,
tbl_feeds f
ORDER BY
u.userid,
f.feedsid
I have a simple database with three tables. In the database I have a table for users of my system, a table for applications to a competition, and an intermediary table that allows me to track which users have selected which applications to view.
Table 1 = users (user_id, username, first, last, etc...)
Table 2 = applications (application_id, company_name, url, etc...)
Table 3 = picks (pick_id, user_id, application_id, picked)
I am trying to write an SQL query that will show all the applications that have been submitted and if any individual application has been selected by a user will show that it has been "picked" (1=picked, 0=not picked).
So for user_id = 1 I'd like to see:
Column Names (application_id, company_name, picked)
1, Foo, 1
2, Bar, 1
3, Alpha, Null
4, Beta, Null
I tried it with the following query:
SELECT applications.application_id, applications.company_name, picks.picked
FROM applications
LEFT JOIN picks ON applications.application_id = picks.application_id
ORDER BY applications.application_id ASC
Which is returning this:
1, Foo, 1
1, Foo, 1
2, Bar, null
3, Alpha, null
4, Beta, null
I have a second user (user_id = 2) that also picked application 1 ("Foo") which I know is returning the second row.
Then I tried to limit the scope by specifying user_id = 1 here:
SELECT applications.application_id, applications.company_name, picks.picked
FROM applications
LEFT JOIN picks ON applications.application_id = picks.application_id
WHERE user_id = 1
ORDER BY applications.application_id ASC
Now I'm only getting:
1, Foo, 1
Any suggestions on how I can get what I'm looking for? Again, ideally for a single user I'd like to see:
Column Names (application_id, company_name, picked)
1, Foo, 1
2, Bar, 1
3, Alpha, Null
4, Beta, Null
You have a so-called join table in your database schema. In your case it's called picks. This allows you to create a many-to-many relationship between your users and applications.
To use that join table correctly you need to join all three tables. These queries are easier to write if you use table aliases (applications AS a, etc.)
SELECT a.application_id, a.company_name, p.picked, u.user_id, u.username
FROM applications AS a
LEFT JOIN picks AS p ON a.application_id = p.application_id
LEFT JOIN users AS u ON p.user_id = u.user_id
ORDER BY a.application_id, u.user_id
This will give you a list of all applications with the users who have made them. If no users are related to an application, the LEFT JOIN operations will retain the application row and you'll see NULL values for columns from the picks and users table.
Now, if you add a WHERE p.something = something or u.something = something clause to this query in an attempt to narrow down the presentation, it has the effect of converting the LEFT JOIN clauses into INNER JOIN clauses. That is, you won't retain the applications rows that don't have matching rows in the other tables.
If you want to retain those unmatched rows in your result set, put the condition in the first ON clause instead of the WHERE clause, like so.
SELECT a.application_id, a.company_name, p.picked, u.user_id, u.username
FROM applications AS a
LEFT JOIN picks AS p ON a.application_id = p.application_id AND p.user_id = 1
LEFT JOIN users AS u ON p.user_id = u.user_id
ORDER BY a.application_id, u.user_id
Edit Many join tables like your picks table are set up with a composite primary key, in your example (application_id, user_id). That ensures just one row per possible relationship between the tables being joined. In your case you have the potential for multiple such rows.
To use only the most recent of those rows (the one with the highest pick_id) takes a little more work. You need a subquery (virtual table) to extract it, and to retrieve the appropriate value of picked so your query works. So now things get interesting.
SELECT MAX(pick_id) AS pick_id,
application_id, user_id
FROM picks
GROUP BY application_id, user_id
retrieves the unique relationship pair. That is good. But next we have to fetch the picked column detail value from those rows. That takes another join, using the MAX value of pick_id, like so
SELECT q.application_id, q.user_id, r.picked
FROM (
SELECT MAX(pick_id) AS pick_id,
application_id, user_id
FROM picks
GROUP BY application_id, user_id
) AS q
JOIN picks AS r ON q.pick_id = r.pick_id
So, we need to substitute this little virtual table (subquery) in place of the pick AS p table in the original query. That looks like this.
SELECT a.application_id, a.company_name, p.picked, u.user_id, u.username
FROM applications AS a
LEFT JOIN (
SELECT q.application_id, q.user_id, r.picked
FROM (
SELECT MAX(pick_id) AS pick_id,
application_id, user_id
FROM picks
GROUP BY application_id, user_id
) AS q
JOIN picks AS r ON q.pick_id = r.pick_id
) AS p ON a.application_id = p.application_id AND p.user_id = 1
LEFT JOIN users AS u ON p.user_id = u.user_id
ORDER BY a.application_id, u.user_id
Some developers prefer to create VIEW objects for subqueries like the one here, rather than creating a club sandwich of a query like this one. It's not called Structured Query Language on a foolish whim, eh? These subqueries sometimes can be elements of a structure.
I have 3 databases setup for a Twitter-clone website.
Table 'users':
email | firstname | lastname | hash_password
Link Table 'friends':
email | friend
This lists all users and their friends. Friendships are 1-way (like Twitter's "Following"/"Followers").
friends table is a link table to list all users' friends. So both friends.user and friends.friend are foreign keys to users.email.
Table 'messages':
timestamp | user | content
What is an SQL query to retrieve all messages from a user and his friends?
I've tried:
SELECT
'timestamp',
user,
content
FROM
messages
INNER JOIN friends ON messages.user = friends.friend
ORDER BY 'timestamp' DESC;
This seems to join them correctly, but how can I only get the messages for a specific user's (email address) friends. Right now this just returns all messages.
Thanks.
The quoting of timestamp should be with backticks, otherwise MySQL will assume a string literal value.
Otherwise, to return messages from both the user and all friends, you may use a UNION. One half returns the user's messages, the other half returns friends' messages. You need to add the user's email to your join condition:
/* First part of the UNION returns friends messages */
SELECT
`timestamp`,
user,
content
FROM
messages
/* The join ON clause specifies the main user email */
INNER JOIN friends
ON messages.user = friends.friend
AND friends.email = 'email#example.com'
UNION ALL
/* Other part of the UNION just gets all messages for the main user by email addr */
SELECT
`timestamp`,
user,
content
FROM
messages
WHERE user = 'email#example.com'
/* ORDER BY applies to everything */
ORDER BY `timestamp` DESC;
And if you want to join in the users info (firstname/lastname) here, the easiest way to go about it would be to wrap the entire thing in a subquery and join.
SELECT
users.*,
messages_sub.content,
messages_sub.`timestamp`
FROM
users
JOIN (/* The entire thing from above */) AS messages_sub ON users.email = messages_sub.user
ORDER BY `timestamp`
It can also be done with a UNION of the literal email address you want to find and the friend list, producing only one outer query. This is a little trickier, but may be ultimately faster. It will also be less confusing to bring in other columns from the users table here. I'll add the names:
SELECT
`timestamp`,
user,
firstname,
lastname,
content
FROM
messages
INNER JOIN (
/* Union query produces a list of email addrs -
the person you're looking for plus all his friends
Starts with string literal for the main user
*/
SELECT 'email#example.com' AS email
UNION
/* Plus all his friends into one list joined against messages */
SELECT friend AS email FROM friends WHERE email = 'email#example.com'
) user_and_friends ON messages.user = user_and_friends.email
/* Join against the users table for name info */
INNER JOIN users ON user_and_friends.email = users.email
ORDER BY `timestamp` DESC
I have a customer table and an address table. Each customer can have multiple entries in the address table, but only one of those entries can be marked as 'primary'. I've put together the query to pull up the customer and their primary address as follows:
SELECT * FROM customers LEFT JOIN addresses
ON customers.cust_id = addresses.cust_id
WHERE customers.status = 1 AND addresses.primary = 1
I've found a flaw in that if the customer has not yet added an address to their account, they will not appear because there is no 'primary' address.
What's the best way to work around this?
SELECT *
FROM customers
LEFT JOIN addresses
ON customers.cust_id = addresses.cust_id
AND 1 = addresses.primary
WHERE customers.status = 1
Try modifying the query to AND (addresses.primary = 1 OR addresses.primary IS NULL)
Simply include the left side data when there is no address.
In the example below I use the primary field, but any field of the addesses table
is null in a left join clause when there is no matching key.
SELECT * FROM customers LEFT JOIN addresses
ON customers.cust_id = addresses.cust_id
WHERE customers.status = 1 AND
(addresses.primary = 1 OR addresses.primary IS Null)
I have two tables (MAILS & customers) in my MySQL db both having a column email storing email addresses.
Now I'd like to select every row from these two tables where emails.email!=customers.email.
When I do a simple SELECT * FROM emails, customers WHERE emails.email != customers.email, I get each emails.email listed with a data from the customers table.
How do I do that? Can I use DISTINCT but apply it only to the data coming from the customers table?
If you're wanting a unique list of email addresses found in either the emails table or the customers table where there is not a match between them on the email field this will do the trick:
SELECT DISTINCT COALESCE(e.Email, c.Email) as Email
FROM Emails e FULL OUTER JOIN Customers c ON e.Email = c.Email
WHERE e.Email IS NULL OR c.Email IS NULL
Coincidentally, this type of circumstance is one of the very few situations in which I have ever found the FULL OUTER JOIN to be of particular use...
A 'not-equals' join is very seldom helpful unless there is in fact another column that can be joined for equality. So, if you have a PersonID column in each of the two tables, then you can do:
SELECT m.*, c.*
FROM mails AS m
JOIN customers AS c ON c.PersonID = m.PersonID
WHERE c.email != m.email
But without that extra condition, you have what is close to a Cartesian Product of the two tables, where almost every row in one table is matched with almost every row in the other (because of the not equals condition). Without the not equals condition, the result set would be only slightly larger.