I got an database (phpmyadmin) in which I put users,
grid looks like this
+-----+---------+-----------------+----------------+
| id | user | categorie_bit | calender_week |
+-----+---------+-----------------+----------------+
| 1 | Kevin | 01 | 39 |
+-----+---------+-----------------+----------------+
the id and user is self explained. Categorie_bit stands for the current job he / she has to do like having a walk with the dog.
0 - no job
1 - job 1
10 - job 2
11 - both jobs
and the calender week shows the current calender week.
like this in php:
$date = new DateTime();
echo $date->format('W');
I got various users, each user has to do a job for this calenderweek for this instance 39.
The user is picked randomly from the database. (I used a PDO Wrapper)
$table = "users";
$columns = array("id", "firstname");
$orderBy = array('RAND()');
$result_set = $db->select($table,$columns, Null, Null, $orderBy, 1);
In the next step I give the user a "job".
Categorie_bit = 1.
The problem is now that I want to pick a user who did not have a "job" last calender week, so that all users are treated equally.
Edit:
How do I accomplish this for further usage, lets say user 1 had to work week 39 and user 2 for week 44 and so on...
My first idea is to change the random picker somehow.
Anyone knows how to accomplish this?
Just add a WHERE clause to your query checking if there's an entry last week for your user. I'm not familiar with your PDO wrapper but the resultant query should be:
SELECT u.id, u.firstname
FROM users u
WHERE (
SELECT COUNT(*)
FROM users u2
WHERE u.id = u2.id
AND u2.calendar_week = <?= $date->format('W') - 1 ?>
AND u2.categorie_bit > 0
) = 0
ORDER BY RAND()
LIMIT 1
UPDATE
To make sure each user wasn't assigned another job until every user had done a job at least once I would record the information differently. I would have two tables, one for users and one for the job they completed and the week they completed it on.
Users
+----+-------+
| id | user |
+----+-------+
| 1 | Kevin |
+----+-------+
Jobs
+----+--------+-----+------+
| id | userId | job | week |
+----+--------+-----+------+
| 1 | 1 | 01 | 39 |
| 2 | 2 | 10 | 39 |
| 3 | 6 | 01 | 40 |
| 4 | 8 | 10 | 40 |
+----+--------+-----+------+
Then you can use the following query:
SELECT u.id, u.user
FROM users u
LEFT JOIN jobs j ON u.id = j.userId
WHERE j.id IS NULL
ORDER BY RAND()
LIMIT 1
So now you only get users who haven't completed a job. Once everyone has completed a job and the query returns 0 results you just clear the jobs table and start afresh.
Related
In a blog-like website, all the users can "star" a news (= bookmark it, mark it as "favourite").
I have a mysql table for stats.
table_news_stats
id_news
total_stars (int) //Total number of users who starred this news
placement (int)
The placement field is intuitive: if you order all the news by the total_stars field you get each news placement. So, the news with most stars will be number 1, and so on.
So, suppose I have 700 records in my table_news_stats, and for each one I have the id and the total_stars count, how can I update the placement field automatically for each record? Which query is faster/better?
Example of the table_news_stats content:
First record (A):
1-3654-?
Second record (B):
2-2456-?
Third record (C):
3-8654-?
If you order the record by stars count:
the sequence of records is C - A - B
So... the result will be:
First record (A):
1-3654-2
Second record (B):
2-2456-3
Third record (C):
3-8654-1
Clarification:
why would I ever need the placement field at all?
It's pretty simple... the placement field will be populated by a cronjob the first day of every month. Basically it will provide a 'snapshot' of the rank of each news in terms of popularity (as it was at the beginning of the current month). As a consequence, thanks to the placement field, I will have the following information:
"The 1st day of this month the 'top starred' news list was like this:
1- News C
2- NewsA
3- News B "
Then, with a query "SELECT * FROM table_news_stats ORDER BY total_stars DESC" I can obtain the new ranking (in real-time).
As a consequence, I will have the following information:
"At the time the page is loaded, the 'top starred' news list is like this:
1- News A
2- News C
3- News B "
Finally, by comparing the two rankings, I obtain the last piece of information:
"News A has gained a position" +1
"News C has lost a position" -1
"News B has no change in position" +0
If there is a better way of doing this, let me know.
I guess you don't need to update the table just:
SELECT *
FROM table_news_stats
ORDER BY total_stars DESC
But if you want to know the place of each one you can:
SELECT *, IF(#idx IS NULL,#idx:= 1,#idx:= #idx+1)
FROM table_news_stats
ORDER BY total_stars DESC
And if you still need to update something like:
UPDATE table_news_stats
SET placement = FIND_IN_SET(id_news,(SELECT GROUP_CONCAT(t.id_news) FROM (SELECT id_news
FROM table_news_stats
ORDER BY total_stars DESC) t ))
SQLFiddle
Consider the following
mysql> select * from test ;
+------+-------------+-----------+
| id | total_stars | placement |
+------+-------------+-----------+
| 1 | 3 | 0 |
| 2 | 6 | 0 |
| 3 | 7 | 0 |
| 4 | 2 | 0 |
| 5 | 9 | 0 |
| 6 | 2 | 0 |
| 7 | 1 | 0 |
+------+-------------+-----------+
Now using the following you can update the placement as
update test t1 join
(
select *,
#rn:= if(#prev = total_stars,#rn,#rn+1) as rank ,
#prev:= total_stars
from test,(select #rn:=0,#prev:=0)r
order by total_stars desc
)t2
on t2.id = t1.id
set t1.placement = t2.rank ;
mysql> select * from test order by placement ;
+------+-------------+-----------+
| id | total_stars | placement |
+------+-------------+-----------+
| 5 | 9 | 1 |
| 3 | 7 | 2 |
| 2 | 6 | 3 |
| 1 | 3 | 4 |
| 4 | 2 | 5 |
| 6 | 2 | 5 |
| 7 | 1 | 6 |
+------+-------------+-----------+
Note that in case of tie will have the same placement.
The following is a simplified version of the problem I'm trying to solve. I just can't figure it out.
I have two tables:
Table 1: messages
id | timestamp | message
------------------------
1 | 2014-01-20 09:00:00 | Hello I'm a message
2 | 2014-01-20 09:00:00 | Second message for you
3 | 2014-01-27 11:00:00 | This message has been updated
4 | 2014-01-28 13:45:00 | Last message for now
and
Table 2: reads
id | message_id | user_id | last_read_timestamp
------------------------
1 | 1 | 1 | 2014-01-20 09:10:00
1 | 2 | 1 | 2014-01-20 09:15:00
1 | 3 | 1 | 2014-01-24 19:25:00
1 | 1 | 2 | 2014-01-28 13:45:00
id in Table a relates to message_id in Table 2.
The messages in Table A are updated frequently and the timestamp is updated. So I need to run a query that will return the Table A id of any messages that have been updated SINCE they were read for a particular user_id, for that the user hasn't read (and therefore no corresponding entry appears in Table 2.
So for user_id = 1, message_id 3 has been updated since being read, and message_id 4 is a new message. The result for user_id = 1 would be:
Unread
------
3
4
I've tried a LEFT JOIN between the tables but just can't seem to get the correct result.
If you just need to retrieve messages unread for a single user, you could use something like this:
SELECT messages.id AS unread
FROM
messages LEFT JOIN (SELECT message_id, MAX(last_read_timestamp) as last_rd
FROM reads
WHERE user_id = 1
GROUP BY message_id) lr
ON messages.id = lr.message_id
AND messages.timestamp < lr.last_rd
WHERE
lr.message_id IS NULL
The Table A id of any messages that have been updated SINCE they were read for a particular user_id; will give below query.(not tested)
select * from table1
left join table2 on (table1.id=table2.message_id && table1.timestamp>table2.last_read_timestamp)
where table2.user_id=1;
I can code in PHP but I'm not good with SQL at all. I need to run an update on a table in order to pass in a given user_id and set the "access_end" date for all products the user owns to one year from today's date.
Any help much appreciated
Database is MySQL
Table name is dap_users_products_jn
Relevant Fields in database are:
user_id | access_end_date | product_id
1 | 2012-10-26 | 34
1 | 2012-11-21 | 30
1 | 2012-12-22 | 3
2 | 2012-10-20 | 34
2 | 2012-07-18 | 30
2 | 2012-08-15 | 3
...etc
update dap_users_products_jn
set access_end_date = date_add(now(), interval 1 year)
where user_id = 1
I'm using MySQL and trying to sum the number of hours that user has participated in events, but only for a specific organization.
EDIT: I have another table thrown into the mix (misEvents). I think the GROUP BY statement is causing some problems, but I'm not quite sure what to do about it.
table events: contains the "event" information. orgID is the ID of the organization hosting it
-each listing is a separate event
ID | orgID | hours
1 | 1 | 1
2 | 1 | 2
3 | 2 | 1
table eventUserInfo: shows which users attended the events
-refID references the ID column from the events table
ID | refID | userID
1 | 1 | 1
2 | 1 | 3
3 | 2 | 2
4 | 2 | 1
5 | 3 | 2
table miscEvents
-these are entered in manually if an event by an organization wasn't posted on the site, but
the users still wants to keep track of the hours
ID | userID | orgID | Hours
1 | 1 | 1 | 2
So, when I view the member activity for organization 1, the following table should display
userid | total hours
1 | 5 //participated in ID #1 and 2 and a misc event, total of 4 hours
2 | 2 //participated in ID #2 only for org 1
3 | 1 //participated only in ID #1
assume the given input is $orgID which is set to 1 in this case
SELECT eventUserInfo.userID, sum(events.hours) as hours,sum(miscEvents.hours) AS mhours FROM events
JOIN eventUserInfo ON events.ID = eventUserInfo.refID
JOIN miscEvents ON events.orgID = miscEvents.orgID
WHERE events.orgID = '$orgID'
GROUP BY eventUserInfo.userID
I think it should be:
SELECT eventInfo.userID --- there is no "events.userID" column
, SUM(events.hours) AS hours --- alias added
FROM events
JOIN eventInfo --- no need for LEFT JOIN
ON events.ID = eventInfo.refID
WHERE events.orgID = '$orgID'
GROUP BY eventInfo.userID
The problem lies probably in that you are trying to print the "hours" with: $event['hours'] but there is no hours column returned (only userID and SUM(event.hours). Use an alias in the SELECT list, as above.
Or since eventINFO seems to be you primary table in the query:
SELECT eventINFO.userID, SUM(events.hours)
FROM eventINFO
JOIN events ON eventINFO.refID = events.ID
WHERE events.orgID = '$orgID'
GROUP BY eventINFO.userID
Should result in the same as ypercube but to me seems a little more clear calling your primary table in the FROM call
To your new problem, you need to get rid of this:
sum(events.hours + miscEvents.hours)
If I'm not mistaken, you can't have multiple columns in a SUM you can only add a single column per call.
I would try something like:
SUM(events.hours) AS hours, SUM(miscEvents.hours) AS mhours
Then, in your function, add together those values $total = hours + mhours
I have two tables one called meeting and one called attendance, attendance is a many to many relational database in the following format:
Attendance:
user_id | meeting_id | invited
--------+------------+--------
1 | 5 | 1
2 | 5 | 0
3 | 4 | 0
3 | 5 | 1
3 | 6 | 0
Meetings are in the following format:
Meetings:
meeting_id | meeting_name | owner_id
-----------+--------------+----------
3 | Awesome | 2
4 | Boring | 2
5 | Cool | 5
9 | Sexy | 3
There can only be one meeting row per meeting, but unlimited attendance rows per meeting (limited to for every possible user for every meeting).
How in SQL and/or Propel do I create something that would list all meetings where the (provided) user_id is either the owner_id in meetings OR were the user_id and invited in the attendance database.
I am looking for a result (based on the above data) when searching for userid 3 of:
Result for userid3:
meeting_id | meeting_name | owner_id
-----------+--------------+----------
5 | Cool | 5 - Because userid 3 is attending meeting 5
9 | Sexy | 3 - Because userid 3 owns meeting 9
I currently have the following which doesn't work really, and produces multiple rows per meeting (because the meeting exists more than once in the attendance DB).
$criteria->addJoin(MeetingMeetingsPeer::ID, MeetingAttendancePeer::MEETING_ID, Criteria::LEFT_JOIN);
$criterion = $criteria->getNewCriterion(MeetingMeetingsPeer::OWNER_ID, Meeting::getUserId());
$criterion->addOr($criteria->getNewCriterion(MeetingAttendancePeer::USER_ID, Meeting::getUserId()));
$criteria->add($criterion);
return $criteria;
Which is something like the below in SQL:
SELECT meeting_meetings.ID, meeting_meetings.OWNER_ID, meeting_meetings.GROUP_ID, meeting_meetings.NAME, meeting_meetings.COMPLETED, meeting_meetings.LOCATION, meeting_meetings.START, meeting_meetings.LENGTH, meeting_meetings.CREATED_AT, meeting_meetings.UPDATED_AT FROM `meeting_meetings` LEFT JOIN meeting_attendance ON (meeting_meetings.ID=meeting_attendance.MEETING_ID) WHERE (meeting_meetings.OWNER_ID=1 OR meeting_attendance.USER_ID=1)
Thanks for your time,
This should get you all meetings that are owned by user_id 1, as well as all meetings that are attended by user_id 1.
SELECT meeting_meetings.ID, meeting_meetings.OWNER_ID, meeting_meetings.GROUP_ID, meeting_meetings.NAME,
meeting_meetings.COMPLETED, meeting_meetings.LOCATION, meeting_meetings.START, meeting_meetings.LENGTH,
meeting_meetings.CREATED_AT, meeting_meetings.UPDATED_AT
FROM `meeting_meetings`
WHERE `meeting_meetings`.`owner_id` = 1
UNION DISTINCT
SELECT meeting_meetings.ID, meeting_meetings.OWNER_ID, meeting_meetings.GROUP_ID, meeting_meetings.NAME,
meeting_meetings.COMPLETED, meeting_meetings.LOCATION, meeting_meetings.START, meeting_meetings.LENGTH,
meeting_meetings.CREATED_AT, meeting_meetings.UPDATED_AT
FROM `meeting_meetings`
JOIN `meeting_attendance` ON `meeting_meetings`.`meeting_id` = `meeting_attendance`.`meeting_id` AND `meeting_attendance`.`invited`
WHERE `meeting_attendance`.`user_id` = 1
It's a bit clunky. Personally, I would consider adding an owner flag to the meetings_attendance table.