joining two sql queries into one which content is not repeated - php

I've been trying to fetch a list of chats from a table in mysql
This is how the table looks
|id|sender |receiver |date |
| 1|u1 |u2 |2014-06-12|
| 2|u2 |u1 |2014-06-13|
| 3|u3 |u2 |2014-06-14|
| 4|u1 |u2 |2014-06-15|
| 5|u1 |u3 |2014-06-16|
I want the query to fetch all id's where u1 is in receiver or sender but showing just the most updated id and ordering the query using date column
The expected result is something like this
5 4
In this way it show that u1 is chatting with u3 and u1 is chatting with u2 ( as u2 is also a sender in the second id but date is past it is not shown)
I tried to create the query using group and joining but it has been impossible
Thanks

You can find all rows using an inner query that finds the max id per receiver where the sender is u1, and do an outer query to get the rows and sort them;
SELECT id, date FROM mytable WHERE id IN (
SELECT MAX(id) id
FROM mytable
WHERE sender='u1' OR receiver='u1'
GROUP BY CASE WHEN sender='u1' THEN receiver ELSE sender END
)
ORDER BY date DESC;
An SQLfiddle to test with.

You can run a subquery to get the max date and unique records, then join it back to your table to get the IDs of those rows like this:
select
ba.id,
ba.sender,
ba.receiver
from
yourTable ba
join (
select
sender,
receiver
max(`date`) as mdate
from
yourTable
where
sender='u1'
or receiver='u1'
group by
sender,
receiver
) sub
on ba.sender=sub.sender
and ba.receiver=sub.receiver
and ba.`date`=sub.mdate
Your table does use a few column names I would suggest to change - namely date which is a reserved word. That's going to make it a huge pain in the ass to do anything with. Now, you could keep it but then use backticks around it each and every single time, but I would strongly suggest changing it to a name you don't have to give special treatment to.

select x.*
from mytable as x
join (
SELECT MAX(date) as d
FROM mytable
WHERE 'u1' in (sender, receiver)
) as t
on x.date = t.d
and 'u1' in (x.sender, x.receiver)
Alternative
select x.*
from mytable as x
WHERE 'u1' in (sender, receiver)
order by date desc
limit 1;

SELECT DISTINCT id FROM TableName
WHERE
Sender = u1
OR
Receiver = u1
Order BY date DESC

Related

Mysql group by fetch last row

SELECT * FROM conversation_1
LEFT JOIN conversation_2
ON conversation_1.c_id = conversation_2.c_id
LEFT JOIN user
ON conversation_2.user_id = user.user_id
LEFT JOIN message
ON conversation_1.c_id = message.c_id
WHERE conversation_1.user_id=1
GROUP BY message.c_id
conversation_1 conversation_2
c_id user_id c_id user_id
1 1 1 2
2 1 2 3
3 2
I have a message DB build in Mysql
I make 4 tables user, conversation_1, conversation_2, message
when user try to open his message box, it will fetch out all conversations(conversation_1)
than join to user conversation_2 and use conversation_2 to find out which user
than join to the message.
c_id user_id user_name message
1 2 Alex Hi user_1, this is user_2
2 3 John hi user_3, user_2 don't talk to me
it works fine, however I want to display the message from last row GROUP BY
currently it display the 1st row in this group.
ps.conversation_1.c_id is auto increment and the c_id will insert to conversation_2 who has join this conversation
select * from (SELECT * FROM conversation_1
LEFT JOIN conversation_2
ON conversation_1.c_id = conversation_2.c_id
LEFT JOIN user
ON conversation_2.user_id = user.user_id
LEFT JOIN message
ON conversation_1.c_id = message.c_id
WHERE conversation_1.user_id=1
order by conversation_1.c_id desc) finalData
GROUP BY message.c_id
Beware that, as documented under MySQL Extensions to GROUP BY:
MySQL extends the use of GROUP BY so that the select list can refer to nonaggregated columns not named in the GROUP BY clause. This means that the preceding query is legal in MySQL. You can use this feature to get better performance by avoiding unnecessary column sorting and grouping. However, this is useful primarily when all values in each nonaggregated column not named in the GROUP BY are the same for each group. The server is free to choose any value from each group, so unless they are the same, the values chosen are indeterminate. Furthermore, the selection of values from each group cannot be influenced by adding an ORDER BY clause. Sorting of the result set occurs after values have been chosen, and ORDER BY does not affect which values within each group the server chooses.
This is what is happening to select the message (and potentially other columns) in your existing query.
Instead, you want the groupwise maximum:
SELECT messages.* FROM messages NATURAL JOIN (
SELECT c_id, MAX(m_id) m_id FROM messages GROUP BY c_id
) t

Find non-existing ID's using SELECT NOT IN?

I have a list of ID's, comma separated stored as string.
123,456,789
I was previously using something like this to pull data for each row in the database where each ID was present:
SELECT id, name FROM line_item WHERE id IN (123,456,789)
However, I want to only determine the ID's where they do not exist in the database. E.g. check the table to return all ID's provided that do not exist in the DB.
I tried NOT IN assuming it would work, but of course, all I got was every ID that did not match my sample, which was massive (thousands of rows, small number of ID's that we typically need to check for).
SELECT id FROM line_item WHERE id NOT IN (123,456,789)
For example:
Table: ID
111
222
333
444
555
777
999
String:
111,222,666,888,999
Query should return:
666,888
Using MySQL, can I find those values not present without using PHP.
You can create additional table populated with desired id values, and use query with LEFT JOIN and WHERE clauses -
CREATE TABLE temp_table(id INT);
INSERT INTO temp_table VALUES(123),(456),(789);
SELECT t1.id FROM temp_table t1
LEFT JOIN line_item t2
ON t1.id = t2.id
WHERE t2.id IS NULL;
Another variant:
SELECT t1.id FROM (SELECT 123 id UNION SELECT 456 UNION SELECT 789) t1
LEFT JOIN line_item t2
ON t1.id = t2.id
WHERE t2.id IS NULL;
NOT IN will take the values in the parentheses and cause the query to, as you said, give you all other ids.
I do not know if there is a way to do this with several numbers simultaneously - but this is how you could do it with one:
SELECT id, name, description
FROM line_item
WHERE 1 NOT IN (SELECT id FROM line_item) // Replace 1 with your numbers in a loop
On Postgresql you can use the generate_series(i,j) function. For example to find the non-existent integers in the id column you can write:
select i from generate_series(
(select min(id) from line_item),(select max(id) from line_item)
) as i
where i not in (select id from line_item);
If you want to use this query to find the first available integer to use as an id, you can use this query:
select min(i) from generate_series(
(select min(id) from line_item),(select max(id) from line_item)+1
) as i
where i not in (select id from line_item);

PHP MySQL Top 5 Referers Function

I have a table called users which looks like:
-id
-email
-login
-admin
-coins
-cash
-premium
-IP
-pass
-ref
-signup
-online
-promote
-activate
-banned
-rec_hash
-country
-c_changes
-sex
-daily_bonus
If say user with id 81 referred 10 people then those 10 people would have "81" in their ref column.
I would like to create a top 5 referral table but I'm having trouble with the query and displaying that in PHP, would anybody be able to help?
I FORGOT TO MENTION IF THEY HAVE NO REFERRAL IT SHOWS AS 0 HOW WOULD I EXCLUDE 0 FROM BEING SHOWN AS A REFERRAL?
You can do it in a single SQL statement like this:
SELECT ref, COUNT(*) AS num FROM users
GROUP BY ref ORDER BY num DESC LIMIT 5
But that will just get you the 5 IDs, rather than their user rows. You can then perform a further query to get the actual rows. Alternatively, use the above query with a join to do it all in one.
IF THEY HAVE NO REFERRAL IT SHOWS AS 0
messy design - this should be null. Regardless...
SELECT u.login, ilv.referred
FROM
(SELECT ref, COUNT(*) AS referred
FROM users
WHERE ref IS NOT NULL
AND ref>0
GROUP BY ref
ORDER BY COUNT(*) DESC
LIMIT 0,5) ilv
INNER JOIN users u
ON ilv.ref=users.id
ORDER BY ilv.referred DESC;
Or and SQL like this:
SELECT u.*, COUNT(*) as referrers FROM users r JOIN users u ON r.ref = u.id
GROUP BY u.id ORDER BY referrers DESC LIMIT 5
It is faster to use just one statement even with a join on the same table.

Complicated MySQL Database Query

I have the following database structure:
Sites table
id | name | other_fields
Backups table
id | site_id | initiated_on(unix timestamp) | size(float) | status
So Backups table have a Many to One relationship with Sites table connected via site_id
And I would like to output the data in the following format
name | Latest initiated_on | status of the latest initiated_on row
And I have the following SQL query
SELECT *, `sites`.`id` as sid, SUM(`backups`.`size`) AS size
FROM (`sites`)
LEFT JOIN `backups` ON `sites`.`id` = `backups`.`site_id`
WHERE `sites`.`id` = '1'
GROUP BY `sites`.`id`
ORDER BY `backups`.`initiated_on` desc
The thing is, with the above query I can achieve what I am looking for, but the only problem is I don't get the latest initiated_on values.
So if I had 3 rows in backups with site_id=1, the query does not pick out the row with the highest value in initiated_on. It just picks out any row.
Please help, and
thanks in advance.
You should try:
SELECT sites.name, FROM_UNIXTIME(b.latest) as latest, b.size, b.status
FROM sites
LEFT JOIN
( SELECT bg.site_id, bg.latest, bg.sizesum AS size, bu.status
FROM
( SELECT site_id, MAX(initiated_on) as latest, SUM(size) as sizesum
FROM backups
GROUP BY site_id ) bg
JOIN backups bu
ON bu.initiated_on = bg.latest AND bu.site_id = bg.site_id
) b
ON sites.id = b.site_id
In the GROUP BY subquery - bg here, the only columns you can use for SELECT are columns that are either aggregated by a function or listed in the GROUP BY part.
http://dev.mysql.com/doc/refman/5.5/en/group-by-hidden-columns.html
Once you have all the aggregate values you need to join the result again to backups to find other values for the row with latest timestamp - b.
Finally join the result to the sites table to get names - or left join if you want to list all sites, even without a backup.
Try with this:
select S.name, B.initiated_on, B.status
from sites as S left join backups as B on S.id = B.site_id
where B.initiated_on =
(select max(initiated_on)
from backups
where site_id = S.id)
To get the latest time, you need to make a subquery like this:
SELECT sites.id as sid,
SUM(backups.size) AS size
latest.time AS latesttime
FROM sites AS sites
LEFT JOIN (SELECT site_id,
MAX(initiated_on) AS time
FROM backups
GROUP BY site_id) AS latest
ON latest.site_id = sites.id
LEFT JOIN backups
ON sites.id = backups.site_id
WHERE sites.id = 1
GROUP BY sites.id
ORDER BY backups.initiated_on desc
I have removed the SELECT * as this will only work using MySQL and is generally bad practice anyway. Non-MySQL RDBSs will throw an error if you include the other fields, even individually and you will need to make this query itself into a subquery and then do an INNER JOIN to the sites table to get the rest of the fields. This is because they will be trying to add all of them into the GROUP BY statement and this fails (or is at least very slow) if you have long text fields.

sorting by value in another table php mysql

I have two tables, one called episodes, and one called score. The episode table has the following columns:
id | number | title | description | type
The score table has the following columns:
id | userId | showId | score
The idea is that users will rate a show. Each time a user rates a show, a new row is created in the score table (or updated if it exists already). When I list the shows, I average all the scores for that show ID and display it next to the show name.
What I need to be able to do is sort the shows based on their average rating. I've looked at joining the tables, but haven't really figured it out.
Thanks
To order the results, use and ORDER BY clause. You can order by generated columns, such as the result of an aggregate function like AVG.
SELECT e.title, AVG(s.score) AS avg_score
FROM episodes AS e
LEFT JOIN scores AS s ON e.id=s.showId
GROUP BY e.id
ORDER BY avg_score DESC;
You're right. You have to JOIN these tables, then use GROUP BY on the 'episodes' table's 'id' column. Then you'll be able to use AVG() function on 'the scores' tables's 'score' column.
SELECT AVG(scores.score) FROM episodes LEFT JOIN scores ON scores.showId = episodes.id GROUP BY episodes.id
SELECT episodes.*, AVG(score.score) as AverageRating FROM episodes
INNER JOIN score ON (episodes.id = score.showId)
GROUP BY episodes.id
ORDER BY AVG(score.score) DESC

Categories