Select and count query from two tables - php

I must retrive all those who have sent messages to a given user and if the messages have not been read, return the number.
I have two tables
users
| id_user| username | status |
| 1 | user1 | yes |
| 2 | user2 | yes |
| 3 | user3 | yes |
messages
| id_message |id_sender|id_dest| message | read |
| 1 | 1 | 2 | some text | yes |
| 2 | 3 | 2 | some text | no |
| 3 | 2 | 1 | some text | no |
and I have this query
select id_sender,username, count(distinct id_message) as nr_messages
FROM messages
INNER JOIN users
ON id_sender = id_user
WHERE id_dest=$dest and status='yes'
GROUP by id_sender
I can count the number of messages for each user but I can not know how many are unread. I must have a number if the messages are unread and 0 i the user do not have unread messages.
for example for user2
sender: user3 (1 unread message)
sender: user1 (0 unred message)
messages table is ~2000000 rows

If you need to count both read and unread at the same time, you can use conditional sum as
select
m.id_sender,
u.username,
sum(m.`read` = 'yes') as read_messages,
sum(m.`read` <> 'yes') as unread_messages
from messages m
join users u on u.id_user = m.id_sender
where m.id_dest = 2
group by m.id_sender ;
For the above sample data you will get
+-----------+----------+---------------+-----------------+
| id_sender | username | read_messages | unread_messages |
+-----------+----------+---------------+-----------------+
| 1 | user1 | 1 | 0 |
| 3 | user3 | 0 | 1 |
+-----------+----------+---------------+-----------------+

Related

is it safe to run a another query in side a mysql loop

I have a left join query to get posts liked by a users. if 2nd logged in user visit the 1st user profile will show the likes by 1st user and also will show a text on the post if 2nd user (logged in user) like the same post
user table
user_id | username
likes table
like_id | post_id | uid
MySQL
$SQL = "SELECT * FROM likes LEFT JOIN users ON users.user_id = likes.uid WHERE likes.uid = 'user1'"
If i run another query inside the while loop of above query it will work
$check_id = row['post_id']; //get post id from 1st loop
if(isset($_SESSION['userid'])){
$check = "SELECT * FROM likes WHERE post_id='$check_id' AND uid='LOGED-IN-USER-ID'"
}
Then i can get the num_rows and add text. This work perfectly fine but i like to know is there a better way to do this without running so many queries inside the while loop. Is there a way to combine the queries or do the 2nd query outside of the loop.
That's "safe" from "data consistency point of view", but querying in a while after a query is called a "1+N" and is typically a performance killer, you may easily find documentation about SQL 1+N problem.
The solution is to let the SQL server do the job for you in a single query, avoiding playing ping pong with it (read: TCP packets back-and-forth, query parsing, ...).
Given:
> SELECT * FROM user;
+---------+----------+
| user_id | username |
+---------+----------+
| 1 | root |
| 2 | user2 |
| 3 | user3 |
+---------+----------+
> SELECT * FROM `like`;
+---------+---------+---------+
| like_id | post_id | user_id |
+---------+---------+---------+
| 1 | 1 | 1 |
| 2 | 2 | 1 |
| 3 | 3 | 1 |
| 4 | 4 | 1 |
| 5 | 2 | 2 |
+---------+---------+---------+
> SELECT * FROM `post`;
+---------+--------+
| post_id | text |
+---------+--------+
| 1 | post 1 |
| 2 | post 2 |
| 3 | post 3 |
| 4 | post 4 |
+---------+--------+
There's multiple way to request what you want, but one way may be:
> SELECT like_id, like.post_id, text,
(SELECT 1 FROM `like`
WHERE post_id = post.post_id AND
user_id = 2 /* logged in user */) AS I_like_it_too
FROM `like`
JOIN post USING (post_id)
WHERE user_id = 1 /* user id of seen profile */;
+---------+---------+--------+---------------+
| like_id | post_id | text | I_like_it_too |
+---------+---------+--------+---------------+
| 1 | 1 | post 1 | NULL |
| 2 | 2 | post 2 | 1 |
| 3 | 3 | post 3 | NULL |
| 4 | 4 | post 4 | NULL |
+---------+---------+--------+---------------+
The use the I_like_it_too alias to display post differently as needed.
From a performance point of view you'll need an index on like.user_id to restrict the selected rows on a little subset, the dependent subquery will only be ran for this subset, so that's OK.
Another possibility may be:
> SELECT displayed.like_id, displayed.post_id, text, my_likes.like_id is not null AS i_also_like
FROM `like` AS displayed
JOIN post USING (post_id)
LEFT JOIN `like` AS my_likes ON
displayed.post_id = my_likes.post_id AND
my_likes.user_id = 2 /* logged user */
WHERE displayed.user_id = 1 /* user id of seen profile */;
+---------+---------+--------+-------------+
| like_id | post_id | text | i_also_like |
+---------+---------+--------+-------------+
| 1 | 1 | post 1 | 0 |
| 2 | 2 | post 2 | 1 |
| 3 | 3 | post 3 | 0 |
| 4 | 4 | post 4 | 0 |
+---------+---------+--------+-------------+
Do u mean like this ?
Table SO_LIKES ( ur "Like" Table )
like_id | post_id | uid
1 | 1 | 1
2 | 2 | 1
3 | 1 | 2
Table SO_USERS ( ur "Users" Table )
user_id | username
1 | User1
2 | User2
SQL
SELECT * FROM SO_LIKES as t1 LEFT JOIN SO_USERS as t2 ON t1.uid = t2.user_id INNER JOIN SO_LIKES as t3 ON t1.post_id = t3.post_id WHERE t2.user_id = 1 AND t3.uid = 2
SO Simply call the Same Table in ur query again and use the ID of user 2 there
WHERE t2.user_id = 1 AND t3.uid = 2
Output Looks then like this
like_id | post_id | uid | user_id | username | like_id | post_id | uid
1 | 1 | 1 | 1 | User1 | 3 | 1 | 2
SO u get the POST_id 1 That both Users has Liked

Query mysql when reach row then stop? show the queue number

I am doing a script want to calculate how many row record before an user record when t1.status is 1.
My table is t1, and the data as below:
+------+---------+------------+----------+----------+
| ID | name | desc | status | time |
+------+---------+------------+----------+----------+
| 1 | ABB | | 1 | 0325 |
| 2 | CCD | | 1 | 0236 |
| 3 | EEF | | 1 | 0325 |
| 4 | GGG | | 1 | 0000 |
| 5 | HIJ | | 2 | 1234 |
| 6 | KKK | | 1 | 5151 |
+---------------------------------------------------+
I was thinking about the query is something like (query row where status = 1 AND stop when reach $userid)
I would like to output to show user (Let's say username is GGG) as:
$userid = 'GGG';
then my output will be
<table><tr><td>Queue: GGG You came in 4 place, in front of you still got 3 person in queue, please be patient</td></tr></table>
How to I do the right query to get the number 4 and 3 ?
Thank you.
You can try something like this hope it helps :-
SELECT count(*) as COUNT FROM t1 WHERE id < (SELECT id FROM t1 WHERE userid = $userid)

SQL: get data spread over 3 tables

I am trying to get some statistics for an online game I maintain. I am searching for an SQL statement to get the result on the bottom.
There are three tables:
A table with teams, each having a unique identifier.
table teams
---------------------
| teamid | teamname |
|--------|----------|
| 1 | team_a |
| 2 | team_x |
---------------------
A table with players, each having a unique identifier and optionally an affiliation to one team by it's unique teamid.
table players
--------------------------------
| playerid | teamid | username |
|----------|--------|----------|
| 1 | 1 | user_a |
| 2 | | user_b |
| 3 | 2 | user_c |
| 4 | 2 | user_d |
| 5 | 1 | user_e |
--------------------------------
Finally a table with events. The event (duration in seconds) is related to one of the players through their playerid.
table events.
-----------------------
| playerid | duration |
|----------|----------|
| 1 | 2 |
| 2 | 5 |
| 3 | 3 |
| 4 | 8 |
| 5 | 12 |
| 3 | 4 |
-----------------------
I am trying to get a result where the durations of all team members is summed up.
result
--------------------------
| teamid | SUM(duration) |
|--------|---------------|
| 1 | 14 | (2+12)
| 2 | 15 | (3+8+4)
--------------------------
I tried several combinations of UNION, WHERE IN, JOIN and GROUP but could not get it right. I am using PostgreSQL and PHP. Can anyone help me?
Just use sum with group by:
select t.teamid, sum(e.duration)
from team t
join players p on t.teamid = p.teamid
join events e on p.playerid = e.playerid
group by t.teamid
If you need all teams to be returned even if they don't have events, then use an outer join instead.
Try this
SELECT teamid, Sum(duration),
AS LineItemAmount, AccountDescription
FROM teams
JOIN teams ON teams.teamid = players.teamid
JOIN events ON players.playersid = events.playersid
JOIN GLAccounts ON InvoiceLineItems.AccountNo = GLAccounts.AccountNo
GROUP BY teamid
http://www.w3computing.com/sqlserver/inner-joins-join-two-tables/

mysql group by select with conditional value

In my messages table I have following rows for example,
|----|---------|--------------|------|
| id | user_id | message |status|
|====|=========|==============|======|
| 1 | 2 | msgs 11 | r |
|----|---------|--------------|------|
| 2 | 3 | msgs 12 | r |
|----|---------|--------------|------|
| 3 | 2 | msgs 13 | r |
|----|---------|--------------|------|
| 4 | 3 | msgs 14 | u |
|----|---------|--------------|------|
Now, I need to know two things for each user_id
Whether it has any status u or not.
How many messages are there
For example, a query like below
select user_id, status, count(*) as totalMsg from messages group by user_id
Would brought me following rows
| user_id | status| totalMsg |
|=========|=======|==========|
| 2 | r | 2 |
|---------|-------|----------|
| 3 | r | 2 |
^
|------> I need this value to be 'u' because user 3 has a message u
My current query doesnt really gurantee that it will look for a u in the status column.
Is that possible to do? If so how?
MAX() will work on this since r is the least value based on the lexicographical order.
SELECT user_ID,
MAX(status) status,
COUNT(*) totalMsg
FROM messages
GROUP BY user_ID

Getting the last row in each group by?

Hello basically im trying to copy the messaging system that facebook has onto my site.
This is the logic...
"When a user1 CREATES A NEW MESSAGE to send to user7, A new thread is created with thread_id of 1(table: messages_thread) and a new entry is inserted into table:messages which is message_id 1(table:messages). When user7 REPLYS to user1's message, message2 is created, and it has a thread_id of 1.
Now when user 1 CREATES A NEW MESSAGE to sent to user7 thread 2 is created, and message 3 is created. When user7 replies to thread2, message 4 is created (hopefully you get the logic.)
Everything is fine. the only problem is i need to select the newest message in the thread but im having trouble with the sql,
This sql that I have as of right now...
SELECT max(message_id) message_id, m.thread_id, m.body, m.user_id,m.to_id, m.message_status, m.new, m.date, u.id, u.displayname, u.username, u.profile_img
FROM messages m INNER JOIN users u ON u.id = m.user_id
WHERE to_id = 7 AND (message_status = 'unread' or message_status='read' or message_status='saved')
group by thread_id Order by message_id Desc LIMIT 10
Produces this...
+------------+-----------+----------------------+---------+-------+----------------+-----+------------+----+--------------+----------+-------------+
| message_id | thread_id | body | user_id | to_id | message_status | new | date | id | displayname | username | profile_img |
+------------+-----------+----------------------+---------+-------+----------------+-----+------------+----+--------------+----------+-------------+
| 6 | 2 | Really nice | 1 | 7 | read | 0 | 1298617367 | 1 | Kenny Blake | imkenee | 28_1 |
| 4 | 1 | Whats good with you? | 1 | 7 | read | 0 | 1298607438 | 1 | Kenny Blake | imkenee | 28_1 |
+------------+-----------+----------------------+---------+-------+----------------+-----+------------+----+--------------+----------+-------------+
This is good but one small problem, it selects the first row in each group and im trying to select the newest (the last row) in each group
how can i do this? here is the tables. Thanks!
Table: Messages_thread
+----+---------+----------------+-------------+-----------+---------------+-------------+------------+
| id | user_id | subject | from_status | to_status | from_s_delete | to_s_delete | date |
+----+---------+----------------+-------------+-----------+---------------+-------------+------------+
| 1 | 1 | Hey Kenny | unread | unread | 0 | 0 | 1298607438 |
| 2 | 7 | Check out this | unread | unread | 0 | 0 | 1298617344 |
+----+---------+----------------+-------------+-----------+---------------+-------------+------------+
Table Messages
+------------+-----------+---------+-------+-----------------------------------------------------------+----------------+-----------------+-----+------------+
| message_id | thread_id | user_id | to_id | body | message_status | is_sent_deleted | new | date |
+------------+-----------+---------+-------+-----------------------------------------------------------+----------------+-----------------+-----+------------+
| 1 | 1 | 1 | 7 | Whats good with you? | read | 0 | 0 | 1298607438 |
| 2 | 1 | 7 | 1 | Nothing Kenny just chilling. Whats up with you though???? | read | 0 | 0 | 1298607473 |
| 4 | 1 | 1 | 7 | Just posted victor how are you man? | read | 0 | 0 | 1298607956 |
| 5 | 2 | 7 | 1 | Look at this poem.... | read | 0 | 0 | 1298617344 |
| 6 | 2 | 1 | 7 | Really nice | read | 0 | 0 | 1298617367 |
| 7 | 2 | 7 | 1 | Yea i know right :) | unread | 0 | 0 | 1298617383 |
+------------+-----------+---------+-------+-----------------------------------------------------------+----------------+-----------------+-----+------------+
Group the threads in a subquery, which will return the last message for each thread:
SELECT m.message_id, m.thread_id, m.body, m.user_id,
m.to_id, m.message_status, m.new, m.date, u.id, u.displayname, u.username, u.profile_img
FROM messages m
INNER JOIN users u ON u.id = m.user_id
INNER JOIN (
SELECT MAX(message_id) MaxMsgIDForThread
FROM messages
WHERE to_id = 7
AND (message_status = 'unread'
or message_status='read'
or message_status='saved')
GROUP BY thread_id
) g ON m.message_id = g.MaxMsgIDForThread
Order by m.message_id Desc
LIMIT 10
The WHERE may need to be moved to the outer query, right now it will pick the last message the meets the criteria, move it to the outer query if you want to skip the thread entirely if the conditions are not met.
You should also consider storing the message status as a ENUM which will help the comparisons.
There is no way to add order to group by
But maybe this works:
SELECT max(message_id) message_id, MAX(m.thread_id), m.body, m.user_id,m.to_id, m.message_status, m.new, m.date, u.id, u.displayname, u.username, u.profile_img
FROM messages m INNER JOIN users u ON u.id = m.user_id
WHERE to_id = 7 AND (message_status = 'unread' or message_status='read' or message_status='saved')
group by thread_id Order by message_id Desc LIMIT 10

Categories