having an issue with my MySQL query that I can not figure out...
I'm trying to get a count of how many rows are in the table "questions" that are not in the table "answers" where the user_id matches
This is my MySQL at the moment and it returns 0 rows even though it should return 13...
SELECT COUNT(*) AS total
FROM questions
LEFT OUTER JOIN answers
ON questions.quest_id = answers.ans_question
JOIN users
ON answers.ans_user = users.user_id
WHERE answers.ans_question IS null
and questions.quest_level <= (SELECT user_level
FROM users
WHERE username = 'chris3spice')
and answers.ans_user = (SELECT user_id
FROM users
WHERE username = 'chris3spice');
This is my original query which returns how many are in questions that aren't in answers... but it doesn't take into account user_id but it does take into account the user_level so no issues there...
SELECT COUNT(*) AS total
FROM questions
LEFT OUTER JOIN answers
ON questions.quest_id = answers.ans_question
LEFT OUTER JOIN users
ON answers.ans_user = users.user_id
WHERE answers.ans_question IS null
and questions.quest_level <= (SELECT user_level
FROM users
WHERE username = 'chris3spice');
Here are my tables for reference
quest_id-----quest_text------quest_ans
1__________blah________blah
...
14_________blah________blah
.
.
ans_id-----ans_user------ans_quest
1__________1________1
...
14_________2________13
.
.
user_id-----user_name
1_________chris3spice
2_________testing
Try this - I moved some of your logic around to isolate the LEFT JOIN
SELECT COUNT(*) AS total
FROM users
JOIN questions
ON questions.quest_level <= users.user_level
LEFT OUTER JOIN answers
ON questions.quest_id = answers.ans_question
AND answers.ans_user = users.user_id
WHERE answers.ans_question IS null
AND users.username = 'chris3spice';
Related
I am trying to get the posts by the users that the userID (? in the query) follows, AND get the posts by ? user, themselves.
SELECT
posts.id AS postid, posts.user AS user, posts.images AS images, posts.post_created, posts.textvalue, posts.textpost,
users.username AS authorname
FROM posts
INNER JOIN follows ON (posts.user = follows.follows AND follows.user = ?) OR posts.user = ?
INNER JOIN users ON users.id = posts.user
ORDER BY posts.post_created DESC LIMIT 20
This query works, but for every post that is by ? it returns the row twice, when I remove OR posts.user = ? it works fine, but does not show the posts for the ? user id.
I believe you want:
SELECT p.*, u.username AS authorname
FROM posts p INNER JOIN
users u
ON u.id = p.user LEFT JOIN
follows f
ON p.user = f.follows AND f.user = ?
WHERE f.user IS NOT NULL OR -- a match on the "follows" condition
p.user = ? -- a match on the post condition
ORDER BY p.post_created DESC
LIMIT 20;
Your query is a but confusing. But basically you have this condition for posts from the user:
FROM posts p INNER JOIN
follows f
ON p.user = ?
(forget the OR part -- that only further increases the number of rows.)
There is no JOIN condition between the two tables, so you are getting a row for every user that follows the post.
My query looks like:
SELECT *
FROM users U
LEFT JOIN posts P ON P.userId = U.id AND P.userId IS NOT NULL;
Why the query also return result where userId is null ?
I know that for my needs I can use INNER JOIN to get only posts related to user but is so strange that LEFT JOIN support multiple conditions, but not work with NOT NULL conditions.
This is because "posts" does not contain the null-values and hence they can´t be filtered at that stage. The Null-values are only generated trough the join, when the server can´t find a corresponding row on the right table. So just put the not null in the where clause and it will work:
SELECT * FROM users U LEFT JOIN posts P ON P.userId = U.id WHERE userId IS NOT NULL;
(EDIT: You should use an inner join for productive work though, as it is the proper way and will give you much greater performance.)
You can also see all users who don´t have posts by inverting that:
SELECT * FROM users U LEFT JOIN posts P ON P.userId = U.id WHERE userId IS NULL;
You are outer joining the posts table. This means for every users record that has no match in posts you still get this record with all posts columns null.
So say you have a users record with userid = 5 and there is no posts record with id = 5.
ON P.userId = U.id AND P.userId IS NOT NULL
The two combined conditions are not met (there is no record with userid 5), so you get the users record with all posts columns set to null in your results.
Maybe you are simply looking for an inner join? All users records with their posts data?
This query:
SELECT *
FROM users U LEFT JOIN
posts P
ON P.userId = U.id AND P.userId IS NOT NULL;
Returns all rows in the users as well as all columns from posts, regardless of whether or not they match. This is true, regardless of whether the ON clause evaluates to TRUE or FALSE.
What you want is a WHERE. In addition, you should only select the columns from users:
SELECT u.*
FROM users U LEFT JOIN
posts P
ON P.userId = U.id
WHERE P.userId IS NOT NULL;
Note that you can also accomplish this using NOT IN or NOT EXISTS.
Because the LEFT JOIN must return every row from the left table by it's definition. The raw may be augmented with the data of the right table depending on the ON clause evaluation. So the following code must return a row.
select u.*, p.*
from (
select 1 as id
) u
left join (
-- no data at all
select 2 as id where 1=2
) p on 3 = 4 -- never is true
Try this
SELECT * FROM users U LEFT JOIN posts P ON P.userId = U.id
SELECT * FROM users U LEFT JOIN posts P ON P.userId = U.id where P.userId IS NOT NULL;
IS NOT NULL WITH JOINS
SELECT * FROM users
LEFT JOIN posts ON post.user_id = users.id
WHERE user_id IS NOT NULL;
I am trying to select the latest row from a LEFT JOIN not on the main query. This is my SQL:
SELECT *
FROM messages
LEFT JOIN users
ON messages.message_to = users.user_id
OR messages.message_user = users.user_id
LEFT JOIN message_replies
ON messages.message_id = message_replies.message_reply_main
LEFT JOIN user_personal_information
ON users.user_id =
user_personal_information.user_personal_information_user
I want to select the latest row from:
LEFT JOIN message_replies
ON messages.message_id = message_replies.message_reply_main
My column is called: message_reply_date - how can I use that to LEFT JOIN the latest row?
message_replies:
CREATE TABLE IF NOT EXISTS `message_replies` (
`message_reply_id` int(11) NOT NULL,
`message_reply_user` int(11) NOT NULL,
`message_reply_main` int(11) NOT NULL,
`message_reply_message` text NOT NULL,
`message_reply_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`message_reply_read` int(11) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
I am using this for the WHERE clause:
WHERE m.message_user = ?
OR m.message_to = ?
AND m.message_deleted=0
AND m.message_permdeleted=0
GROUP BY mr.message_reply_main
ORDER BY mr.message_reply_date DESC
If i understood the question, i'll do it this simple way:
SELECT *
FROM messages
LEFT JOIN users
ON messages.message_to = users.user_id OR messages.message_user = users.user_id
LEFT JOIN message_replies
ON messages.message_id = message_replies.message_reply_main
LEFT JOIN user_personal_information
ON users.user_id = user_personal_information.user_personal_information_user
WHERE message_replies.message_reply_date = (SELECT MAX(message_reply_date) FROM message_replies WHERE message_reply_main = messages.message_id)
/* AND more criterias */
No grouping in the main query but just using a MAX evaluation of message_reply_date in a subquery regarding the WHERE criteria on message_reply_date itself.
There is a "left join" method for getting the most recent message (as well as several others). But in keeping with the preferred join method of the question:
SELECT *
FROM messages m LEFT JOIN
users u
ON m.message_to = u.user_id OR m.message_user = u.user_id LEFT JOIN
message_replies mr
ON m.message_id = mr.message_reply_main LEFT JOIN
user_personal_information upi
ON u.user_id = upi.user_personal_information_user LEFT JOIN
message_replies mr2
ON mr2.message_reply_main = m.message_id AND
mr2.message_reply_date > mr.message_reply_date
WHERE mr2.message_reply_main IS NULL;
I also added table aliases because they make a query easier to write and to read.
The idea is to match to the table again, but only for later messages. Then, the WHERE clause checks that none exist -- so it has the latest one.
Give this a go.
SELECT *
FROM messages
LEFT JOIN users
ON messages.message_to = users.user_id
OR messages.message_user = users.user_id
LEFT JOIN ( SELECT *
, MAX(message_reply_date) OVER (PARTITION BY message_id) AS most_recent_message_reply_date
FROM message_replies ) message_replies
ON messages.message_id = message_replies.message_reply_main
AND message_replies.message_reply_date = message_replies.most_recent_message_reply_date
LEFT JOIN user_personal_information
ON users.user_id =
user_personal_information.user_personal_information_user
I've substituted your direct call to the message_replies table with a Select that displays the table and the maximum reply date grouped by the message_id. I've then included this in the join to filter all answers other than the one your looking for.
Have a go, any problem drop me a message, always happy to help.
What you want to do is emulate the rank & partition functionality seen in MSSQL & PL/SQL, essentially ranking on date & partitioning by message_reply_main.
This can be done in MySQL using an outer join & a count on your partition, while using a < or > in the join for the ranking criteria. This allows you to do your standard join, but also where rank = 1, returning the largest date.
SELECT *
FROM messages
LEFT JOIN users
ON messages.message_to = users.user_id
OR messages.message_user = users.user_id
LEFT JOIN (select mri.message_reply_main
, mri.message_reply_date
, mri.message_reply_message
, mri.message_reply_read
, count(mri2.message_reply_main) + 1 as rank
FROM message_replies mri
left join message_replies mri2 on mri.message_reply_date < mri2.message_reply_date
and mri.message_reply_main = mri2.message_reply_main
group by mri.message_reply_main, mri.message_reply_message
, mri.message_reply_read, mri.message_reply_date) mr ON messages.message_id = mr.message_reply_man
AND mr.rank = 1
LEFT JOIN user_personal_information
ON users.user_id =
user_personal_information.user_personal_information_user
the MYSQL query below combines a number of tables. However, as you can see, I would like to add a LEFT JOIN at the end on the receipt table. The query returns an error when I add the LEFT JOIN. Anybody know the best way to LEFT JOIN the receipt table to the rest of the query. Sorry if this is a newbie question. Thanks !!
SELECT user_name, expense_category, merchant_name, expense_cost, expense_date, expense_status, receipt_image, expense_comment
FROM users, expenses, merchants, receipts
WHERE ".$adminId." = expenses.admin_id
AND expenses.user_id = users.user_id
AND expenses.merchant_id = merchants.merchant_id
AND LEFT JOIN (receipts)
ON expenses.receipt_id = receipts.receipt_id
Here is a clean approach of doing it, note that I have added alias for the tables for better readability so you may use the alias name in the select statement to fetch the column from the proper table.
SELECT
u.user_name,
ex.expense_category,
mer.merchant_name,
ex.expense_cost,
ex.expense_date,
ex.expense_status,
re.receipt_image,
ex.expense_comment
FROM users u
JOIN expenses ex on ex.user_id = u.user_id
JOIN merchants mer on mer.merchant_id = ex.merchant_id
LEFT JOIN receipts re on re.receipt_id = ex.receipt_id
where
ex.admin_id = '$adminId'
Try this,
SELECT user_name, expense_category, merchant_name, expense_cost, expense_date, expense_status, receipt_image, expense_comment
FROM users, expenses, merchants, receipts
LEFT JOIN receipts ON expenses.receipt_id = receipts.receipt_id
WHERE ".$adminId." = expenses.admin_id
AND expenses.user_id = users.user_id
AND expenses.merchant_id = merchants.merchant_id
Use join clauses instead of where clause. I.e.
SELECT user_name, expense_category, merchant_name, expense_cost, expense_date, expense_status, receipt_image, expense_comment
FROM users
INNER JOIN expenses on users.user_id = expenses.expenses_id
INNER JOIN merchants on merchants.merchant_id = expenses.merchant_id
LEFT JOIN (receipts)
ON expenses.receipt_id = receipts.receipt_id
WHERE ".$adminId." = expenses.admin_id
Note that any columns from the receipts will be NULL in the select statement whenever there's no matching record.
I'm trying to get MySQL to return me all instructors who don't have a booking at a certain time. This query is essentially the search query. So return all instructors and their details that aren't already booked basically. Could someone help me finish it off?
SELECT
AddressTypes.AddressTypeName,
Addresses.*,
InstructorSettings.*,
Users.*,
BookedSlots.DateTime
FROM Users
LEFT OUTER JOIN InstructorSettings
ON Users.UserID = InstructorSettings.UserID
LEFT OUTER JOIN Addresses
ON Users.UserID = Addresses.UserID
INNER JOIN AddressTypes
ON Addresses.AddressTypeID = AddressTypes.AddressTypeID
LEFT OUTER JOIN BookedSlots
ON Users.UserID = BookedSlots.UserID
WHERE Users.AccountTYpe = 3 AND Addresses.PostCode1 IN ('l13') AND BookedSlots.DateTime <> '2013-04-25 11:00:00'
The query above doesn't return anything, but if I take out the "AND BookedSlots.DateTime <> '2013-04-25 11:00:00'" it returns all the instructors details fine.
Database
It looks to me as if what you're looking for is the lack of existence of records in BookedSlots for the specified time.
I'm not sure if this is considered MySQL best practices, but I typically accomplish that task not with a join, but with a sub-select or a temp table, like so:
SELECT
AddressTypes.AddressTypeName,
Addresses.*,
InstructorSettings.*,
Users.*,
FROM Users
LEFT OUTER JOIN InstructorSettings
ON Users.UserID = InstructorSettings.UserID
LEFT OUTER JOIN Addresses
ON Users.UserID = Addresses.UserID
INNER JOIN AddressTypes
ON Addresses.AddressTypeID = AddressTypes.AddressTypeID
WHERE Users.AccountType = 3 AND Addresses.PostCode1 IN ('l13')
AND Users.UserID not in (SELECT BookedSlots.InstructorID FROM BookedSlots
WHERE BookedSlots.DateTime = '2013-04-25 11:00:00')
EDIT: per comments, changed the select statement to disclude BookedSlots, and the sub-select to return InstructorId (which matches instructors instead of students on the booking).
To check that there aren't any bookings on a particular timeslot, include the timeslot in the join conditions and select where the timeslot id is null - like so:
SELECT
AddressTypes.AddressTypeName,
Addresses.*,
InstructorSettings.*,
Users.*,
BookedSlots.DateTime
FROM Users
LEFT OUTER JOIN InstructorSettings
ON Users.UserID = InstructorSettings.UserID
LEFT OUTER JOIN Addresses
ON Users.UserID = Addresses.UserID
INNER JOIN AddressTypes
ON Addresses.AddressTypeID = AddressTypes.AddressTypeID
LEFT OUTER JOIN BookedSlots
ON Users.UserID = BookedSlots.UserID AND
BookedSlots.DateTime = '2013-04-25 11:00:00'
WHERE Users.AccountTYpe = 3 AND
Addresses.PostCode1 IN ('l13') AND
BookedSlots.UserID is null