MySql - Get latest rows from table without duplicate subject id - php

I am attempting to get the latest threads that have been posted on in a Forum with vars from Users, Threads and the last Post. Problem is that the current method i am attempting brings back duplicate threads because the newer posts have been posted in those threads, whereas i just want one post to return per thread, not all the latest posts.
SELECT t.thread_id, u.user_name, p.post_entry
FROM forum_thread as t
LEFT JOIN forum_post AS p ON p.post_thread = t.thread_id
LEFT JOIN user AS u ON u.user_id = p.post_user
ORDER BY t.thread_lastpost DESC LIMIT 0,8
Currently that is returning:
/-----------------------------------------/
| 7049 | USERNAME | Post Entry |
|----------------------------------------|
| 7049 | USERNAME | Post Entry |
|----------------------------------------|
| 7049 | USERNAME | Post Entry |
|----------------------------------------|
| 7049 | USERNAME | Post Entry |
|----------------------------------------|
| 7650 | USERNAME | Post Entry |
|----------------------------------------|
| 7068 | USERNAME | Post Entry |
|----------------------------------------|
| 7056 | USERNAME | Post Entry |
|----------------------------------------|
| 7136 | USERNAME | Post Entry |
I want to remove those first duplicate IDs and only leave one with the latest post entry from that thread.
I hope i have explained it well enough for people to understand.
Thanks.
----------------- EDIT --------------------
Got it to work with GROUP BY:
SELECT t.thread_id, u.user_id, p.post_entry
FROM forum_post AS p
LEFT JOIN forum_thread AS t ON t.thread_id = p.post_thread
LEFT JOIN user AS u ON u.user_id = p.post_user
GROUP BY t.thread_id
ORDER BY t.thread_lastpost DESC LIMIT 0,8

this query may not be best performing, but I can't do much more without knowing structure of your database. You'll need some post_id or post_timestamp for including the second left join as well. Neither DISTINCT nor GROUP BY would solve your issue, as the username and post_entry will usually be different in all cases, i.e. the lines won't actually be distinct.
SELECT t.thread_id, u.user_name, p.post_entry
FROM forum_thread as t
LEFT JOIN forum_post AS p ON p.post_thread = t.thread_id
LEFT JOIN forum_post AS p2 ON p.post_id > p2.post_id
LEFT JOIN user AS u ON u.user_id = p.post_user
where p2.post_id is null
ORDER BY t.thread_lastpost DESC LIMIT 0,8

Related

I need to calculate percentage of counted items

I have two tables:
1st: reasons
id | title
---------------------------------
1 | Customer didn't like it
2 | Needs improving
3 | Wrong format
2nd: projects
id | title | rejected
------------------------------------
1 | Priject 1 | Null
2 | Priject 2 | 1
3 | Priject 3 | 1
4 | Priject 4 | Null
5 | Priject 5 | 2
I need to display Reasons.Title and number of project rejected for that reason. I've managed to join those tables together, with this code
SELECT reasons.title as title, count(*) as num
FROM reasons
LEFT JOIN reasons on projects.rejected = reasons.id
WHERE projects.rejectedIS NOT NULL
GROUP BY projects.rejected
Now I need to add percentage, so my final table looks like this
title | num | percentage
--------------------------------------------------
Customer didn't like it | 2 | 66,6
Needs improving | 1 | 33,3
The format of percentage is of course not important.
I would like to get this done with MySql, so I do not need to use two queries and extra PHP, but if there is another solution, other from MySql, I'm open to suggestions
You can do this by getting the total in the FROM clause:
SELECT r.title as title, count(*) as num,
COUNT(*) / pp.cnt as ratio
FROM reasons r JOIN
projects p
ON p.rejected = r.id CROSS JOIN
(SELECT COUNT(*) as cnt FROM projects p WHERE rejects IS NOT NULL) pp
GROUP BY r.title, pp.cnt;
Notes:
This fixes the table names, so the query has a projects table.
This removes the WHERE because it is not needed.
This changes the LEFT JOIN to an inner join.

mysql sub query to get latest comment from two tables

I have been working on a query to get summarised list of communications like an inbox that shows conversations
below are my tables
users
company | contact_person | pic_small
alerts
comment_id | user_id | poster_id | timestamp
activity
comment_id | user_id | comment | timestamp
comments
comment_id | user_id | comment | timestamp
This is what I have so far which works ok although I need help with one aspect.
SELECT alerts.comment_id,
alerts.user_id,
alerts.poster_id,
alerts.active,
MAX(alerts.timestamp) AS maxTime,
users.contact_person,
users.company,
users.pic_small
FROM alerts
LEFT JOIN users ON users.user_id = alerts.poster_id
WHERE alerts.user_id = %s
GROUP BY alerts.comment_id
ORDER BY maxTime DESC
Comments are held in the activity and comments tables and I need to somehow join the last (newest) comment from either the activity or comments table (depending which is newer)
How do I add this to my above query, below is what I am trying to achieve

PHP MySQL Unknown column in 'on clause'

Keep getting this error whenever I try to run my mysql query: Unknown column 'tt.uid' in 'on clause'
Which is weird since the column exists in my table.
Here's my PHP:
$query = mysqli_query($con, "SELECT * FROM test_text tt, users u JOIN test_text ON tt.uid = u.id WHERE u.username IN ('$friends')") or die(mysqli_error($con));
$friends looks like this: 'jcarter','pcoulson' And is created dynamically
I want to select everything from both tables where the username is equal to a username in my friends string.
I don't really understand how the above query is supposed to work, I got it from a php forum. The way I understand it is that the query selects everything in the users table where the username is a username in my friends string. Then it selects everything from test_text table where the user id from the users table is equal to the user id in the test_text table. Please correct me if I'm wrong.
I added a foreign key to test_text.uid that links the the user.id column.
uid is a column that exists in my test_text table. So I don't know why I'm getting that error. Maybe it's my syntax?
My tables look like this:
users table:
------------------------------------------
| id | username | first_name | last_name |
-----+----------+------------+------------
| 1 | jcarter | Jake | Carter |
| 2 | pcoulson | Phil | Coulson |
------------------------------------------
test_text table:
----------------------------
| pid | post_body | uid |
-------+-------------+------
| 1 | hello world | 1 |
| 2 | foo bar | 2 |
| 3 | whats up | 1 |
----------------------------
The problem seems to be you are joining 3 tables where you probably mean to join 2:
SELECT * FROM test_text tt, users u JOIN test_text
Should probably be:
SELECT * FROM users u JOIN test_text tt
I suppose you mean to use
SELECT * FROM test_text tt JOIN users u ON tt.uid = u.id WHERE u.username IN ('$friends')

Fairly complex SQL statement using inner join (I presume)

I'm having trouble figuring out how to write an SQL query to return results from the following table structure.
The first thing I do is get a list of clients that have a status equal to 1 by:
SELECT * FROM clients WHERE status=1
Then I need to get all user email addresses that belong to a client. My plan was to loop through the results of the query above and running multiple queries for each client. As you can see from the table 'client_user_list' a single user can belong to multiple clients.
I tried doing something like this:
SELECT emailaddress
FROM users
INNER JOIN client_user_list ON users.user_id = client_user_list.user_id
WHERE users.client_id = 1
But it failed. As you can see I'm a total novice when it comes to this stuff. Any help would be appreciated, or feel free to point me to an appropriate resource to learn more. I've looked, but I haven't found anything that covers something complex like this.
Additional info: Using foreign keys there are relationships between clients <-> client_user_list and client_user_list <-> users
clients:
|---------------------------------------|
| client_id | client_name | status |
|---------------------------------------|
| 1 | John Doe | 1 |
| 2 | James Doe | 0 |
|---------------------------------------|
client_user_list:
|----------------------|
| client_id | user_id |
|----------------------|
| 1 | 5 |
| 2 | 6 |
| 1 | 6 |
|----------------------|
users:
|---------------------------------------|
| user_id | emailaddress |
|---------------------------------------|
| 5 | notan#email.com |
| 6 | afake#email.com |
|---------------------------------------|
Thanks so much in advance.
I'm not sure if this is your only problem, since you didn't specify what the exact problem is, but the WHERE-clause of your query contains an error. You query should be changed into this:
SELECT DISTINCT emailaddress
FROM users
INNER JOIN client_user_list ON users.user_id = client_user_list.user_id
WHERE client_user_list.client_id = 1
The users table does not have a field called client_id, the client_user_list table does.
You can get the clients with status = 1 and their users with only one query, by joining all three tables:
select clients.client_id, clients.client_name, users.user_id, users.emailaddress
from clients
inner join client_user_list on client_user_list.client_id = clients.client_id
inner join users on client_user_list.user_id = users.user_id
where clients.status = 1
order by clients.client_id, users.user_id
The following command should resolve this issue
I hope it is userful.
select distinct use.emailaddress
from clients cli
inner join client_user_list cul on (cli.client_id=cul.client_id)
inner join users use on (cul.user_id = use.user_id)
where cli.status = 1

Mysql Query from Query results

I am making something like an announcement board that requires readers to acknowledge that they read it, and was wondering if there is a more efficient way of doing this.
I have 3 Tables on MySQL side:
+-----------------+ +-----------------+ +-----------------+
| Announcements | | Acknowledgement | | User |
+-----------------+ +-----------------+ +-----------------+
| announce_id | | ack_id | | user_id |
| announce_msg | | announce_id | | user_name |
| ... | | user_id | | ... |
+-----------------+ +-----------------+ +-----------------+
When a user "reads" the announcement (by clicking a button), Acknowledgment table will be inserted with the Announcement ID and User ID. When a second user "reads" the same announcement, Acknowledgement table will be inserted again with same Announcement ID and the second User ID and so on...
+--------------------------------+
| Acknowledgement |
+--------+-------------+---------+
| ack_id | announce_id | user_id |
+--------+-------------+---------+
| 1 | 1 | 1 |
| 2 | 1 | 4 |
| 3 | 1 | 3 |
| 4 | 3 | 1 |
| 5 | 3 | 6 |
| 6 | 3 | 2 |
+--------+-------------+---------+
Now to the problem. On the front end, when I list all the announcements on a page, I would have to first query for all the announcements. Then, for each announcement, I would have to do another query for all the users that have read this announcement.
$sql = "select * from Announcements";
$result = $pdo->query($sql);
while ($row = $result->fetch())
{
$announce_id = $row['announce_id'];
$announce_msg = $row['annouce_msg'];
$readers = "";
$sql2 = "select u.user_name from Acknowledgement as a INNER JOIN User as u where announcement_id =".$annouce_id;
$result2 = $pdo->query($sql);
while ($row2 = $result2->fetch())
{
$readers .= $row2['user_name'].", ";
}
echo "id:".$annouce_id.", message:".$announce_msg.", Readers:".$readers;
}
So if there 10 announcements on the page, there will be 10 sub-queries for each announcement. What I have now does the job right now... but what if there is 1000 announcements? Then there will be 1000 sub-queries? Sounds like the database will be really hammered. So I'm hoping there is a better way of doing this.
Also, if 1000 people in the user table reads all 1000 announcements, the acknowledgement table will have 1000x1000 entries. seems like the acknowledgement table will become really really long. Will that be a problem as time goes by?
This is a really rough example of what I'm trying to do but it did take me a long time to write all this. If more details is needed let me know.
There is a better way. You can use a single query with group_concat:
select a.*, group_concat(u.user_name separator ', ') as AllUsers
from Announcements a join
Acknowledgement ak
on a.Announce_Id = ak.Announce_Id join
User u
on u.user_ID = ak.User_ID
group by a.announce_id
This uses the MySQL feature of hidden columns to group by only one column (announce_id) but still pull in a bunch of other columns with no aggregations (everything else pulled in by the "*").
If your purpose here is to filter out the announcements that your current user has read, you can do this an entirely different way. Instead of querying for every announcement, and then finding out all the users that have read those announcements and examining those results to find ones that your use has read and trimming them from the displayed list, you can just query in one go for everything a particular user (or list of users) have not yet read.
Change your query to this:
SELECT * FROM Announcements WHERE Announce_id NOT IN (SELECT ANNOUNCE_ID FROM Acknowledgement WHERE User_ID = <INSERT USER ID HERE>)
That should return all Announcement rows that this particular user has not yet acknowledged. If you change that final WHERE clause to be WHERE User_ID IN () then you can specify a list of user IDs.
EDIT: Given the comment you posted above, you could use this query to get all announcements that have been read by no one:
SELECT * FROM Announcements WHERE Announce_id NOT IN (SELECT ANNOUNCE_ID FROM Acknowledgement WHERE User_ID IN (SELECT User_ID FROM User))
The logic for putting together a query to find announcements that haven't been read by someone (if not everyone) is escaping me right this second.
EDIT THE SECOND: Every announcement, and everyone who has and has not read it, requires use of a different kind of join that you've used above, a FULL OUTER JOIN. Unfortunately MySQL doesn't have that feature IIRC, but it can be simulated with a union query
SELECT A.*, ACK.*, U.* FROM Announcements AS A
INNER JOIN Acknowledgement AS ACK ON A.Announce_ID = ACK.Announce_ID
LEFT OUTER JOIN User AS U ON ACK.User_ID = U.User_ID
WHERE U.User_ID IS NOT NULL
UNION ALL
SELECT A.*, ACK.*, U.* FROM Announcements AS A
LEFT OUTER JOIN Acknowledgement AS ACK ON A.Announce_ID = ACK.Announce_ID
RIGHT OUTER JOIN User AS U ON ACK.User_ID = U.User_ID
I think that should do it. No facilities to test at the moment, of course.

Categories