ORDER BY within a distinct inner join - php

Was wondering if people could give me a hand here. I am currently updating some SQL code for a plugin I maintain, and I have hit a problem.
What I am trying to do is fetch data from one table, join it with another and then return the data to my PHP app. That is working, however - I want to order the data returned in descending order - this is proving difficult and doesn't seem to want to play ball.
SELECT DISTINCT s.userid, s.id, u.user_nicename, u.user_login, u.display_name, u.id
FROM wp_bbpas as s INNER JOIN wp_users as u
ON s.userid = u.id
ORDER BY s.id DESC
That returns everything in wp_bbpas and joins it with the wp_users table, but I actually only want to grab unique/distinct values.
Am I doing something wrong here?

Since s.id appears to be the unique key of wp_bbpas, it should be left out of the SELECT list to reduce the result to distinct users.

Related

MYSQL Join with Null value returned

I have a query thats joining two table, using the GROUP_CONCAT to get a comma separated list which is then being mapped to an array in an object
SQL:
$sql = "SELECT *,
GROUP_CONCAT(climb_attributes.attribute_id) as climb_attributes
FROM climbs
LEFT JOIN climb_attributes ON
(climbs.id = climb_attributes.climb_id)
GROUP BY climb_id
ORDER BY climbs.id";
PHP
$all_climb_profiles[$climb->id]->attributes = explode(",", $climb->climb_attributes);
Nearly working perfectly, except I currently only get back results IF the climb_attributes table contains the climb id. Essentially a climb can still exist even if it doesn't have any attributes, but at the moment it has to have an attribute to be returned in the results.
I also need to join it to another table to get the attribute name for the attribute id...if you can help with that as well that would be great, I'm hoping I can figure that out though.
First, you should not be using * to select from all tables when using group by. You can safely take all the columns from the climb table.
The problem is that you are aggregating on a column in the second table, rather than the first. And, it is NULL if there is no match. So, a better query is:
SELECT c.*, GROUP_CONCAT(ca.attribute_id) as climb_attributes
FROM climbs c LEFT JOIN
climb_attributes ca
ON c.id = ca.climb_id
GROUP BY c.id
ORDER BY c.id;
EDIT:
If you want to list the strings, then something like this should work:
SELECT c.*, GROUP_CONCAT(a.name) as climb_attributes
FROM climbs c LEFT JOIN
climb_attributes ca
ON c.id = ca.climb_id LEFT JOIN
attributes a
ON ca.attribute_id = c.id
GROUP BY c.id
ORDER BY c.id

Possible to make MySQL ignore joins under certain conditions?

The following code successfully gives me details of logged in peoples' requested books, and the books themselves, from a specific library.
SELECT bookRequest.version, bookRequest.status, users.user_id, users.firstname, users.lastname, books.Author, books.Title
FROM users
INNER JOIN bookRequest
ON users.user_id=bookRequest.user_id
INNER JOIN books
ON bookRequest.bookID = books.ID
INNER JOIN libraryTokens
ON bookRequest.libraryID = libraryTokens.libraryID
WHERE libraryTokens.libraryID='". $libraryID ."'
The bookRequest table also has columns for directly storing manually added requests for books not in the system, requested by people not logged in. In this case, bookRequest.user_id is always -1, and the extra columns bookRequest.firstname, bookRequest.lastname, bookRequest.Author, and bookRequest.Title hold the guest's name and their non-listed book request's details.
I'm trying to figure out a way (I have no idea if it's even possible) of, when retrieving a library's book requests, ignoring the
INNER JOIN bookRequest
ON users.user_id=bookRequest.user_id
INNER JOIN books
ON bookRequest.bookID = books.ID
part of the query if bookRequest.user_id is -1, and instead retrieving bookRequest.firstname, bookRequest.lastname, bookRequest.Author, and bookRequest.Title, but not ignoring the joins if bookRequest.user_id>-1?
Is it possible, or wayyyyy too messy, and instead I should do two separate queries, and combine the resulting arrays using php?
Thanks for taking a look.
You'll need to change your JOIN to an OUTER JOIN and add a CASE statement to your query. Something like this:
SELECT bookRequest.version,
bookRequest.status,
CASE WHEN bookRequest.user_id = -1 THEN bookRequest.user_id ELSE users.user_id END userid,
CASE WHEN bookRequest.user_id = -1 THEN bookRequest.firstname ELSE users.firstname END firstname,
...
FROM bookRequest
JOIN libraryTokens
ON bookRequest.libraryID = libraryTokens.libraryID
LEFT JOIN books
ON bookRequest.bookID = books.ID
LEFT JOIN users
ON users.user_id=bookRequest.user_id
WHERE libraryTokens.libraryID='". $libraryID ."'

Join mysql with multiple queries

I have three different SQL tables I need to join:
table "internet" with columns id|type|status
table "type_list" with columns id|type_name
table "status_list" with columns id|status_name
I want to output text from the two other tables (type_list, status_list) but not values as numbers which currently I have in table "internet".
I also don't want to make lazy programming - PHP array to make ID's equal to something like
$type_list = array("1"=>"VDSL2","2"=>"ADSL");
$status_list = array("1"=>"Pending","2"=>"Active");
because the text is already in the tables, i just dont know how to join them and output the text as query combined together in one query.
Use JOIN
SELECT i.id, type_name, status_name
FROM internet i
LEFT OUTER JOIN type_list t ON t.id = i.type
LEFT OUTER JOIN status_list s ON s.id= i.status
Read the MySQL doc for more informations.
Just write the select with the fields you want.
select internet.id,type_name,status_name from internet
inner join type_list
on type_list.id=internet.id
inner join status_list
on status_list.id=internet.id
For this you need a LEFT JOIN, like so:
SELECT i.id, t.type_name, s.status_name
FROM internet AS i
LEFT JOIN type_list AS t ON t.id = i.id
LEFT JOIN status_list AS s ON s.id= i.id
From your question, it is unclear what field you would like to join the queries on. In the above example, the queries are joined on the id field.
Please also note that the AS is not actually necessary, I have just put it in there to make it clear what is going on

mysql/php: show posts and for each post all comments

I know this question has been asked multiple times (however, I could still not find a solution):
PHP MYSQL showing posts with comments
mysql query - blog posts and comments with limit
mysql structure for posts and comments
...
Basic question: having tables posts, comments, user... can you with one single select statement select and show all posts and all comments (with comment.user, comment.text, comment.timestamp)? How would such a select statement look like? If not, what is the easiest solution?
I also tried to JOIN the comments table with the posts table and use GROUP BY, but I got either only one comment in each row or each comment but also those posts multiple times!?
I tried the solution of the first link (nested mysql_query and then fetch) as well as the second link (with arrays). However, the first caused a bunch of errors (the syntax in that post seems to be not correct and I could not figure out how to solve it) and in the second I had problems with the arrays.
My query looks like this till now:
SELECT p.id, p.title, p.text, u.username, c.country_name, (SELECT SUM(vote_type) FROM votes v WHERE v.post_id = p.id) AS sum_vote_type FROM posts p LEFT JOIN user u ON ( p.user_id = u.id ) LEFT JOIN countries c ON ( c.country_id = u.country_id ) ORDER BY $orderby DESC
I was wondering if this issue was not very common, having posts and comments to show...?
Thank you for every help in advance!
Not knowing your database structure, it should look something like this. Note that you should replace the * characters with more explicit lists of columns you actually need.
SELECT p.*, c.*, u.* FROM posts p
LEFT JOIN comments c ON c.post_id = p.id
LEFT JOIN users u ON u.id = p.author_id
Note that if you're just trying to get counts, sums and things like that it's a good idea to cache some of that information. For instance, you may want to cache the comment count in the post table instead of counting them every query. Only count and update the comment count when adding/removing a comment.
EDIT:
Realized that you also wanted to attach user data to each comment. You can JOIN the same table more than once but it gets ugly. This could turn into a really expensive query. I also am including an example of how to alias columns so it's less confusing:
SELECT p.*, c.*, u.name as post_author, u2.name as comment_author FROM posts p
LEFT JOIN comments c ON c.post_id = p.id
LEFT JOIN users u ON u.id = p.author_id
LEFT JOIN users u2 ON u2.id = c.author_id

How can I join 3 tables with mysql & php?

I have a page that pulls the users Post,username,xbc/xlk tags etc which is perfect... BUT since I am pulling information from a MyBB bulletin board system, its quite different. When replying, people are are allowed to change the "Thread Subject" by simplying replying and changing it.
I dont want it to SHOW the changed subject title, just the original title of all posts in that thread.
By default it repies with "RE:thread title". They can easily edit this and it will show up in the "Subject" cell & people wont know which thread it was posted in because they changed their thread to when replying to the post.
So I just want to keep the orginial thread title when they are replying.
Make sense~??
Tables:mybb_users
Fields:uid,username
Tables:mybb_userfields
Fields:ufid
Tables:mybb_posts
Fields:pid,tid,replyto,subject,ufid,username,uid,message
Tables:mybb_threads
Fields:tid,fid,subject,uid,username,lastpost,lastposter,lastposteruid
I haev tried multiple queries with no success:
$result = mysql_query("
SELECT * FROM mybb_users
LEFT JOIN (mybb_posts, mybb_userfields, mybb_threads)
ON (
mybb_userfields.ufid=mybb_posts.uid
AND mybb_threads.tid=mybb_posts.tid
AND mybb_users.uid=mybb_userfields.ufid
)
WHERE mybb_posts.fid=42");
$result = mysql_query("
SELECT * FROM mybb_users
LEFT JOIN (mybb_posts, mybb_userfields, mybb_threads)
ON (
mybb_userfields.ufid=mybb_posts.uid
AND mybb_threads.tid=mybb_posts.tid
AND mybb_users.uid=mybb_posts.uid
)
WHERE mybb_threads.fid=42");
$result = mysql_query("
SELECT * FROM mybb_posts
LEFT JOIN (mybb_userfields, mybb_threads)
ON (
mybb_userfields.ufid=mybb_posts.uid
AND mybb_threads.tid=mybb_posts.tid
)
WHERE mybb_posts.fid=42");
Your syntax isn't appropriate for carrying out multiple LEFT JOINs. Each join needs its own ON clause.
SELECT
*
FROM
mybb_users
LEFT JOIN mybb_userfields ON mybb_users.uid = mybb_userfields.ufid
LEFT JOIN mybb_posts ON mybb_userfields.ufid = mybb_posts.uid
LEFT JOIN mybb_threads ON mybb_posts.tid = mybb_threads.tid
WHERE
mybb_posts.fid = 42
This query should give the results you want. But it may not be the most efficient query for getting those results. Check the output of EXPLAIN as part of testing, to make sure it is not using table scans or anything like that.
Do all of these joins need to be LEFT JOINs? LEFT JOIN forces MySQL to join the tables in the indicated order, rather than allowing the query optimiser to determine the best order in which to join them. That's why you might need to be careful about the query execution plan. The main difference between JOIN and LEFT JOIN as far as query output is concerned is that LEFT JOIN resultsets will contain at least one row for each row of the table on the left-hand side of the join, whereas a regular JOIN will not contain a row if there aren't matches on the right-hand side of the join.
Edit: Also, you say that "I don't want it to SHOW the changed subject title, just the original title of all posts in that thread." This suggests that you only want a subset of the columns from these tables, in which case SELECT * is inappropriate.

Categories