Select values from another table with most recent entry - php

I have 2 MySQL Tables:
Table with all Events
+----+---------------------+
| id | Eventtitle |
+----+---------------------+
| 1 | Event 1 |
| 2 | Event 2 |
| 3 | Event 3 |
| 4 | Event 4 |
+----+---------------------+
Table with user attend statuses - Users can change their status multiple times. Every time they change it, a new entry is inserted into the table
+----+------------+----------+---------+---------------------+
| id | event_id | user_id | status | attend_time |
+----+------------+----------+---------+---------------------+
| 1 | 1 | 2 | 1 |2013-07-03 15:34:02 |
| 2 | 1 | 2 | 2 |2013-08-03 19:01:02 | <--
| 3 | 3 | 1 | 1 |2013-07-03 15:34:02 |
| 4 | 4 | 4 | 3 |2013-07-03 15:34:02 |
| 5 | 4 | 6 | 2 |2013-07-03 15:34:02 |
| 6 | 4 | 6 | 1 |2013-07-03 18:55:02 | <--
+----+-----------------------+---------+---------------------+
Now I want all events listed and additionally the most recent attend state and attend_time of the currently logged in user (for example user_id 2 oder 54) - IF he is not in the attend table i still need an entry of the event. (in that case a NULL for the state and attend_time would be good)
Here I rebuild the Data Structure: http://sqlfiddle.com/#!2/c1b3f
IF there is no way on how to get the result with MySQL - I will try to get it with PHP

Try something like this:
SELECT s.id, eventtitle, status, attend_time
FROM events e
JOIN status s ON e.id = s.event_id
WHERE user_id = 2 AND attend_time = (SELECT MAX(attend_time) FROM status
WHERE user_id = 2)
SQLFiddle
Or:
SELECT s.id, eventtitle, s.status, s.attend_time
FROM events e
JOIN status s ON e.id = s.event_id
LEFT JOIN status s2 ON s.attend_time < s2.attend_time
WHERE s.user_id = 2 AND s2.attend_time IS NULL
SQLFiddle

(not tested)
SELECT a.id, eventtitle, status, attend_time FROM events b JOIN status a ON b.id = a.event_id WHERE user_id = 2 order by attend_time desc

Related

How to get a value from MySQL n days before from CURDATE()

I want to join 4 tables to list all the values from a table those have the duration from last updated to current date is more that the duration in other table, table are given below (my English not good to understand so am explaining with examble)
first table daily_tasks
+---------+---------+
| task_id | type_id |
+---------+---------+
| 1 | 1 |
| 2 | 1 |
| 3 | 2 |
+---------+---------+
Second Table daily_task_report
+-----------+---------+------------+
| report_id | task_id | task_date |
+-----------+---------+------------+
| 1 | 1 | 2015-09-10 |
| 2 | 3 | 2015-09-10 |
| 3 | 1 | 2015-09-11 |
| 4 | 3 | 2015-09-16 |
+-----------+---------+------------+
Third Table duration_types
+---------+---------------+------------------------+
| type_id | duration_type | duration_time(in days) |
+---------+---------------+------------------------+
| 1 | Daily Task | 1 |
| 2 | Weekly Task | 6 |
| 3 | Monthly Task | 26 |
| 4 | Yearly Task | 313 |
+---------+---------------+------------------------+
Fourth Table calendar
+--------+------------+---------+
| cal_id | cal_date | holiday |
+--------+------------+---------+
| 1 | 2015-09-10 | 0 |
| 2 | 2015-09-11 | 0 |
| 3 | 2015-09-12 | 0 |
| 4 | 2015-09-13 | 1 |
+--------+------------+---------+
Here daily_tasks.type_id is from duration_types.type_id and daily_task_report.task_id is from daily_tasks.task_id. I want to select all the task_id those task_date and current_date difference will greater than duration_time, also while calculating the duration i have to avoid the dates those have holiday=1 from calendar.
I tried queries but not proper, i got the values without including the calendar table, but that not a good way, query is taking more time to execute.
"SELECT dailyTasks.task_id FROM
(SELECT tab.* FROM (SELECT
tasks.task_type,report.*
FROM daily_tasks AS tasks
LEFT JOIN daily_task_reports AS report ON tasks.task_id=report.task_id
WHERE 1 ORDER BY report.task_date DESC) as tab GROUP BY tab.d_task_id) AS dailyTasks
LEFT JOIN duration_types AS type ON dailyTasks.task_type=type.type_id
WHERE DATEDIFF(CURDATE(),dailyTasks.task_date)>=type.duration_time"
Please someone help, I stuck in this section
According to given table You have some unexpected text or unknown columns in your query
Try this query
"SELECT dailyTasks.d_task_id FROM
(SELECT tab.* FROM
(SELECT tasks.type_id,report.* FROM daily_tasks AS tasks
LEFT JOIN daily_task_reports AS report ON tasks.task_id=report.task_id
ORDER BY report.task_date DESC)
as tab GROUP BY tab.task_id) AS dailyTasks
LEFT JOIN duration_types AS type ON dailyTasks.type_id=type.type_id
WHERE DATEDIFF(CURDATE(),dailyTasks.task_date)>=type.duration_time"
It can be also works in single query
SELECT tasks.task_id FROM daily_tasks AS tasks
LEFT JOIN daily_task_reports AS report ON tasks.task_id=report.task_id
LEFT JOIN duration_types AS type ON tasks.type_id = type.type_id and DATEDIFF(CURDATE(),report.task_date) >= type.duration_time
*I tried but not exclude that id which have holiday in calendar
You can create it on your coding side I gave you new query included with calendar
*
SELECT tasks.task_id,report.task_date,calendar.holiday FROM daily_tasks AS tasks
LEFT JOIN daily_task_reports AS report ON tasks.task_id=report.task_id
LEFT JOIN duration_types AS type ON tasks.type_id = type.type_id and DATEDIFF(CURDATE(),report.task_date) >= type.duration_time
LEFT JOIN calendar ON report.task_date=calendar.cal_date
where calendar.holiday = '0'
order By report.task_date desc

How can I get total user in a group from database by PHP

I have 2 tables in database:
How can I get total user for each group. i.e: group 1: total are 2 users;
group2: total are 2 users;
group3: total is 1 user
You need normalization and never store comma-separated data.
Consider the following
mysql> select * from user_table ;
+---------+---------------+
| user_id | user_group_id |
+---------+---------------+
| 1 | 1,2 |
| 2 | 2 |
| 3 | 1,3 |
+---------+---------------+
3 rows in set (0.00 sec)
mysql> select * from group_table ;
+----------+------------+
| group_id | group_name |
+----------+------------+
| 1 | a |
| 2 | b |
| 3 | c |
+----------+------------+
3 rows in set (0.00 sec)
The above data is not normalized and to get the desired result out of these you need to use some in-efficient query as
select
g.group_id,
count(*) as total
from group_table g
left join user_table u on find_in_set(g.group_id,u.user_group_id) > 0
group by g.group_id ;
+----------+-------+
| group_id | total |
+----------+-------+
| 1 | 2 |
| 2 | 2 |
| 3 | 1 |
+----------+-------+
Now lets do normalization and store user-group data in a different table as
mysql> select * from user_to_group ;
+---------+----------+
| user_id | group_id |
+---------+----------+
| 1 | 1 |
| 1 | 2 |
| 2 | 2 |
| 3 | 1 |
| 3 | 3 |
+---------+----------+
You can easily write different queries from these tables now and here are some examples
select group_id,count(*) as tot from user_to_group group by group_id ;
+----------+-----+
| group_id | tot |
+----------+-----+
| 1 | 2 |
| 2 | 2 |
| 3 | 1 |
+----------+-----+
Joining the tables would even more easy
select
g.group_id,
g.group_name,
count(*) as tot
from user_to_group ug
join group_table g on g.group_id = ug.group_id
join user_table u on u.user_id = ug.user_id
group by g.group_id
+----------+------------+-----+
| group_id | group_name | tot |
+----------+------------+-----+
| 1 | a | 2 |
| 2 | b | 2 |
| 3 | c | 1 |
+----------+------------+-----+
SELECT group_name, COUNT(*) FROM user_table u, group_table g WHERE u.user_group_id LIKE %g.group_id% GROUP BY g.group_name;
this should work and give you a list of all groups and how many users are in them.
I will recommend you to create a third table which holds the information about which users are in which groups.
CREATE TABLE users_in_groups
(
user_id INT
, group_id INT
);
Then you can join like this:
SELECT
gt.group_id
, count(ut.user_id)
FROM
user_table AS ut
, INNER JOIN users_in_groups AS uig ON uig.user_id = ut.user_id
, INNER JOIN group_table AS gt ON gt.group_id = uig.group_id
GROUP BY
gt.group_id
;
To use the table you have now will you have to do something like this (in mysql):
SELECT
gt.group_id
, count(ut.user_id)
FROM
user_table AS ut
, INNER JOIN group_table AS gt ON LOCATE(gt.group_id, ut.user_group_id) > 0
GROUP BY
gt.group_id
Remember, when using group by, always locate what makes your group unique!
This is not an answer to your specific question but rather an alternative data structure proposal that might be better.
Introduce a new table members that looks like
# members
user_id | group_id
1 | 1
1 | 2
2 | 2
3 | 1
3 | 3
Then you could SELECT group_id, count(*) FROM members GROUP BY group_id
+----------+----------+
| group_id | count(*) |
+----------+----------+
| 1 | 2 |
| 2 | 2 |
| 3 | 1 |
+----------+----------+
This structure might also make it easier for you to manage your memberships. user_id + group_id should be unique. And if supported let them be foreign keys.

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 Sum multiple column values with conditions

I have the following schema (two tables):
**APPS**
| ID (bigint) | USERID (Bigint) | USAGE_START_TIME (datetime) |
------------------------------------------------------------------
| 1 | 12 | 2013-05-03 04:42:55 |
| 2 | 12 | 2013-05-12 06:22:45 |
| 3 | 12 | 2013-06-12 08:44:24 |
| 4 | 12 | 2013-06-24 04:20:56 |
| 5 | 13 | 2013-06-26 08:20:26 |
| 6 | 13 | 2013-09-12 05:48:27 |
**USAGE**
| ID (bigint) | APPID (bigint) | DEVICEID (bigint) | HIGH_COUNT (bigint) | MEDIUM_COUNT (bigint) |
--------------------------------------------------------------------------------------------------------
| 1 | 1 | 2 | 400 | 200 |
| 2 | 1 | 3 | 200 | 100 |
| 3 | 2 | 3 | 350 | 40 |
| 4 | 3 | 4 | 2 | 400 |
| 5 | 4 | 2 | 4 | 30 |
| 6 | 5 | 3 | 50 | 300 |
Explanation:
So, there are two tables.
Now I want to find the following:
Given a USERID, Get sum of HIGH_COUNT & MEDIUM_COUNT. While counting
the SUM it should be taken care that: If in USAGE, same device is used
more than once, then the record which has the latest info (based on
APPS.USAGE_START_TIME), should be considered while calculating the
sum.
For ex:
For above schema, result should be (for userid=12) :
| HIGH_COUNT (bigint) | MEDIUM_COUNT (Bigint) |
-----------------------------------------------
| 356 | 470 |
SQL Fiddle: http://sqlfiddle.com/#!2/74ae0f
If a user uses multiple APPS on one device, this query will use the APPS row with the highest usage_start_time:
select a.userid
, sum(u.high_count)
, sum(u.medium_count)
from apps a
join `usage` u
on u.appid = a.id
join (
select u.device_id
, a.userid
, max(a.usage_start_time) as max_start_time
from apps a
join `usage` u
on u.appid = a.id
group by
u.device_id
, a.userid
) filter
on filter.device_id = u.device_id
and filter.userid = a.userid
and filter.max_start_time = a.usage_start_time
group by
a.userid
In your dataset, it will select usage rows 5, 3, 4 for user 12.
See it working at SQL Fiddle.
I can't quite get your numbers, but something like this should work...
SELECT a.userid
, SUM(u.high_count)
, SUM(u.medium_count)
FROM apps a
JOIN `usage` u
ON u.appid = a.id
JOIN
( SELECT userid
, deviceid
, MAX(usage_start_time) max_usage_start_time
FROM apps a
JOIN `usage` u
ON u.appid = a.id
GROUP
BY userid
, deviceid
) x
ON x.userid = a.userid
AND x.deviceid = u.deviceid
AND x.max_usage_start_time = a.usage_start_time
GROUP
BY userid;
Note that usage is a reserved word. Therefore, this is a bad name for a column (or a table). Also, note inconsistencies between your question and your fiddle.
I think not had chance to test it but
SELECT SUM(HIGH_COUNT), SUM(MEDIUM_COUNT) FROM `USAGE` INNER JOIN `APPS` ON USAGE.APPID=APPS.ID WHERE APPS.USERID=$input_user_id_to_lookup
will give you your counts.
For yoru other question (homework?) you didn't give us the full schema so we can't guess what you need doing.
Also whoever designed that db should be shot its horrible

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