I'm working on a custom forum system and I'm trying to figure out how to put a thread on the top of the list if a user posts in it.
I've got this for my query
SELECT
user_threads.threadID,
user_threads.title,
user_threads.uid,
user_threads.postDate,
thread_messages.posted
FROM
user_threads,
thread_messages
WHERE
parent = :parent
GROUP BY
user_threads.title
ORDER BY
thread_messages.posted
DESC
Which doesn't appear to be working. if I post in a new thread, it remains where it is on the list.
You need to join the tables by the threadID. Also, if you just want one row per thread, you need to use the date of the last post.
SELECT
user_threads.threadID,
user_threads.title,
user_threads.uid,
user_threads.postDate,
MAX(thread_messages.posted) AS last_post
FROM
user_threads
LEFT JOIN
thread_messages ON thread_messages.threadID = user_threads.threadID
WHERE
parent = :parent
GROUP BY
user_threads.threadID
ORDER BY
last_post DESC
I used LEFT JOIN so that threads will be shown even if they don't have anything in thread_messages. If this is not needed, you can use a regular JOIN (aka INNER JOIN).
You are missing the JOIN condition. Currently you are getting the cross product of user_threads and thread_messages. That means you got rows for each thread and post in the result.
Try adding something link this
FROM
user_threads
JOIN
thread_messages USING(threadID)
to get each thread only once in your result set.
Use aliases for the tables 'user_threads' and 'thread_messages' i.e.
SELECT
A.threadID,
A.title,
A.uid,
A.postDate,
B.posted
FROM
user_threads A,
thread_messages B
WHERE
parent = :parent
//also do it with 'parent' field if parent is a field of user_threads
//A.parent else thread_messages B.parent
GROUP BY
A.title
ORDER BY
B.posted
DESC
You need to tie the tables together. You can do this by adding something like user_threads.threadID = thread_messages.threadID (second column name is guessed and needs to be checked) to the WHERE or by rewriting the query like this:
SELECT
user_threads.threadID,
user_threads.title,
user_threads.uid,
user_threads.postDate,
thread_messages.posted
FROM
user_threads
JOIN
thread_messages ON thread_messages.threadID = user_threads.threadID
WHERE
parent = :parent
GROUP BY
user_threads.title
ORDER BY
thread_messages.posted
DESC
And also not, that you probably should replace
GROUP BY
user_threads.title
by something like
GROUP BY
user_threads.threadID
or
GROUP BY
user_threads.threadID, user_threads.title
as sooner or later your thread-titles may be reused while the ID should be "forever" unique. Also threadID will most probably lead to a faster query as the database only needs to compare numbers versus text of various lengths.
Related
I have a two tables, one of the table is called participants_tb while the second is called allocation_tb. On the participants_tb, I have my columns as participant_id, name, username.
Under the allocation_tb, I have my columns as allocation_id, sender_username, receiver_username, done. The column done holds any of these three numbers: 0, 1, 2.
I used this sql statement to fetch my values
SELECT *, COUNT(done) d
FROM participants_tb
JOIN allocation_tb ON (username=receiver_username)
WHERE done = 0 || done = 1
GROUP BY receiver_username
It worked very well, the problem I have is that, I want it to also include the information of participants that are in the participants_tb but not in the allocation_tb. I tried to use the left outer join but it did not work as expected because I want it to include participants that are only in the participants_tb but not in the allocation_tb, since the done in the where clause is in the allocation_tb, it won't include those information.
You seem to want:
SELECT p.*, COUNT(a.done) as d
FROM participants_tb p LEFT JOIN
allocation_tb a
ON p.username = a.receiver_username) AND
a.done IN (0, 1)
GROUP BY p.participant_id;
Notes:
The LEFT JOIN keeps all participants.
The GROUP BY needs to be on the first table.
You can use SELECT p.* with the GROUP BY -- assuming that the GROUP BY key is unique (or the primary key).
All columns should be qualified.
IN is an easier way to express your logic.
Basically, I have a table which contains two fields: [id, other] which have user tokens stored in them. The goal of my query is to select a random user that has not been selected before. Once the user is selected it is stored in the table shown above. So if Jack selects Jim randomly, Jack cannot select Jim again, and on the flip side, Jim cannot select Jack.
Something like this is what comes to mind:
SELECT * FROM users
WHERE (SELECT * FROM selected WHERE (id=? AND other=?) OR (id=? AND other=?));
Well, first of all I've read that uses sub-queries like this is extremely inneficient, and I'm not even sure if I used the correct syntax, the problem is however, that I have numerous tables in my scenario which I need to filter by, so it would look more like this.
SELECT * FROM users u
WHERE (SELECT * FROM selected WHERE (id=? AND other=?) OR (id=? AND other=?))
AND (SELECT * FROM other_table WHERE (id=? AND other=?) OR (id=? AND other=?))
AND (SELECT * FROM diff_table WHERE (id=? AND value=?))
AND u.type = 'BASIC'
LIMIT = 1
I feel like there's a much, much more efficient way of handling this.
Please note: I don't want a row returned at all if the users id is present in any of the nested queries. Returning "null" is not sufficient. The reason I have the OR clause is because the user's id can be stored in either the id or the other field, so we need to check both.
I am using Postgre 9.5.3, but I added the MySQL tag as the code is mostly backwards comptable, Fancy Postgre only solutions are accepted(if any)
You can left join to another table, which produces nulls where no record is found:
Select u.* from users u
left selected s on s.id = u.id or s.other = u.other
where s.id is null
The or in a join is different, but should work. Example is kinda silly...but as long as you understand the logic. Left join first table to second table, where second table column is not null means there was atleast one record found that matched the join conditions. Where second table column is null means no record was found.
And you are right...avoid the where field = (select statement) logic when you can, poor performer there.
Use an outer join filtered on missed joins:
SELECT * FROM users u
LEFT JOIN selected s on u.id in (s.id, s.other) and ? in (s.id, s.other)
WHERE u.id != ?
AND s.id IN NULL
LIMIT 1
I know other posts talk about this, but I haven't been able to apply anything to this situation.
This is what I have so far.
SELECT *
FROM ccParts, ccChild, ccFamily
WHERE parGC = '26' AND
parChild = chiId AND
chiFamily = famId
ORDER BY famName, chiName
What I need to do is see the total number of ccParts with the same ccFamily in the results. Then, sort by the total.
It looks like this is close to what you want:
SELECT f.famId, f.famName, pc.parCount
FROM (
SELECT c.chiFamily AS famId, count(*) AS parCount
FROM
ccParts p
JOIN ccChild c ON p.parChild = c.chiId
WHERE p.parGC ='26'
GROUP BY c.chiFamily
) pc
JOIN ccFamily f ON f.famId = pc.famId
ORDER BY pc.parCount
The inline view (between the parentheses) is the headliner: it does your grouping and counting. Note that you do not need to join table ccFamily there to group by family, as table ccChild already carries the family information. If you don't need the family name (i.e. if its ID were sufficient), then you can stick with the inline view alone, and there ORDER BY count(*). The outer query just associates family name with the results.
Additionally, MySQL provides a non-standard mechanism by which you could combine the outer query with the inline view, but in this case it doesn't help much with either clarity or concision. The query I provided should be accepted by any SQL implementation, and it's to your advantage to learn such syntax and approaches first.
In the SELECT, add something like count(ccParts) as count then ORDER BY count instead? Not sure about the structure of your tables so you might need to improvise.
SELECT submissions.created_at, guests.accepted
FROM submission
INNER JOIN guests
ON guests.event_id = 1
ORDER BY submissions.created_at ASC;
Currently this query is taking a long time to run (may not even return everything).
What I am trying to accomplish:
Grab the field submissions.created at and guests.accepted (the two are linked by a submission_id)
given a certain event_id (event 1 for example).
What is wrong with my query?
You forgot to give the JOIN condition in your query. Try this:
SELECT submissions.created_at, guests.accepted
FROM submission s
INNER JOIN guests g on g.event_id = s.submissions_id
where guests.event_id = 1
ORDER BY submissions.created_at ASC;
SELECT submissions.created_at, guests.accepted
FROM submission
INNER JOIN guests
ON guests.(column to match against) = submissions.(column to match on)
where guests.event_id=1
ORDER BY submissions.created_at ASC;
As many others here have already said, your join is a little goofed. It's attempting to join the one row that matches in the guests table against every single row in the submission column.
You would need to do your join like this :
Inner join guest on guest.event_id = submissions_id
where guest.event_id = 1
When you join you need to join on two columns from different table (most of the time id columns)
You can read the MySQL example for more explanation. And here is the link to the MySQL reference manual
I have three tables that are structured like this:
http://i41.tinypic.com/2bt9aq.png
What I am trying to do is retrieve the joke id, title, and average rating of all jokes in a certain category and order them alphabetically. I have this query:
$result = mysql_query("
SELECT jokedata.id AS joke_id,
jokedata.joketitle AS joke_title,
SUM(ratings.rating) / COUNT(ratings.rating) AS average
FROM jokedata
INNER JOIN ratings ON ratings.content_type = 'joke' AND ratings.relative_id = jokedata.id
WHERE jokecategory = '$cur_category'
GROUP BY jokedata.id
ORDER BY jokedata.joketitle
LIMIT $offset, $jokes_per_page
");
However it is not selecting any jokes.
What is wrong with that query? Thankyou.
First, you probably want to use AVG() instead of SUM()/COUNT().
Your problem is the inner join - if no ratings where submitted for a joke then that joke would not be returned as only jokes with a rating value match the inner join.
I would recommend using a left join instead, or even a sub-select. While I normally prefer JOINs as they are usually faster, I would have tried something like this:
SELECT id AS joke_id,
joketitle AS joke_title,
(
SELECT AVG(rating) AS avgrating FROM ratings
WHERE content_type = 'joke' AND relative_id = joke_id
GROUP BY relative_id
) AS average
FROM jokedata
WHERE jokecategory = '$cur_category'
GROUP BY id
ORDER BY joketitle
LIMIT $offset, $jokes_per_page
An inner join will not return a row if one of the joins cannot be fulfilled. So odds are good that the criteria ratings.relative_id = jokedata.id is causing the query to return 0 jokes. Try replacing the INNER JOIN with LEFT JOIN and you'll see how many of the jokedata's rows don't have matching id's in ratings.relative_id.
I would first narrow down the problem by taking out everything except the join and seeing what the result is:
SELECT *
FROM jokedata
INNER JOIN ratings ON ratings.content_type = 'joke' AND ratings.relative_id = jokedata.id
If this gives you results, I would then add back in the WHERE criteria. Do this step by step, and whenever you get a query where you have no rows returned, then look at the previous set of results, and think about what is happening when you add the additional criteria. Being able to see this "intermediate" set of results will help you identify and understand the problem.
Continue adding back to the query piece-wise until you start getting no results, at least then you've narrowed down to what aspect of the query is causing the problem.