How can I make a reward system in PHP that has a timer and sets a timer for a certain amount of time after they click on it and mysql inserts a random value into the table?
I am making a project and I want users to accumulate the in-game currency as if they were using a Bitcoin faucet.
A simple way to do this would be to keep track of the users "currency" using a table that has both amounts and datetimes. The user's balance would only show the entries that have datetimes before the current time. That way, you can insert the entry right away and then it only goes live when you need it.
rowid | user_id | amount | activetime |
-------------------------------------------------------
90000 | 1 | 0.01 | 12:13:12 01-16-15 |
90001 | 1 | 0.01 | 12:13:12 02-16-15 |
On inserts you can set the active time to something like DATE_ADD(NOW(), INTERVAL 2 HOUR) so it will show up 2 hours later.
Example of adding a row:
INSERT INTO transactions
(user_id, amount, activetime)
VALUES
(1, 0.01, DATE_ADD(NOW(), INTERVAL 2 HOUR))
Example of getting current balance for a user:
SELECT SUM(amount) AS balance FROM transactions WHERE activetime <= NOW()
Also, if you want to rate limit the user, it is easy now to check if they have already clicked the button because there will be an entry greater than the current time. You could query quickly like this:
SELECT 1 FROM transactions WHERE activetime > NOW() LIMIT 1
Then in php, if num_rows() is 1, they have already clicked the button and are waiting for payment. Otherwise it will return 0.
Hopefully this helps. Let me know if you have any other questions on this topic, or if you need some clarification.
Related
So I have a quiz with a count down, Each question takes 15 seconds to answer.
When the user starts a question, I save the question_id + the end_time which would be after 15 seconds from now.
The table looks like that:
quiz_attempts
________________________________________
| id | user_id | question_id | end_time |
|____|_________|_____________|__________|
| 1 | 1 | 1 | end_time |
|____|_________|_____________|__________|
Then when the page is fully loaded, I show a counter for the user which starts from 15 seconds and ends with 0. And the user has to answer the question within 15 seconds.
<span class="counter">15</span>
The issue is that the end_time would be sooner than the 15 seconds counter shown to the user.
So for example the end_time is 12:06:22 and the counter is shown to the user ends 12:06:25. And it would depend on the internet speed of the user.
So how to time both times so that the end_time ends with the front-end counter like 12:06:22 for both?
I don't want to start with the end_time from the database, As I want the page loaded 1st so that the user can start reading and answering the question within 15 seconds not less
I am trying to make a "top purchaser" module on my store and I am a bit confused about the MySQL query.
I have a table with all transactions and I need to select the person (which could have one or many transactions) with the highest amount of money spent in the past month.
What I have:
name | money spent
------------------
john | 50
mike | 12
john | 10
jane | 504
carl | 99
jane | 12
jane | 1
What I want to see:
With a query, I need to see:
name | money spent last month
-----------------------------
jane | 517
carl | 99
john | 60
mike | 12
How do I do that?
I do not really seem to find many good solutions since my MySQL query skills are quite basic. I thought of making a table in which money is added to the user when he buys something.
That's a simple aggregated query :
SELECT t.name, SUM(t.moneyspent) money_spent_last_month
FROM mytable t
GROUP BY t.name
ORDER BY t.money_spent_last_month DESC
LIMIT 1
The query sums the total money sped by customer name. The results are ordered by descending total money spent, and only the first row is retained.
If you are looking to filter data over last month, you need a column in the table that keeps track of the transaction date, say transaction_date, and then you can just add a WHERE clause to the query, like :
SELECT t.name, SUM(t.moneyspent) money_spent_last_month
FROM mytable t
WHERE
t.transaction_date >=
DATE_ADD(LAST_DAY(DATE_SUB(NOW(), INTERVAL 2 MONTH)), INTERVAL 1 DAY)
AND t.transaction_date <=
DATE_SUB(NOW(), INTERVAL 1 MONTH)
GROUP BY t.name
ORDER BY t.money_spent_last_month DESC
LIMIT 1
This method is usually more efficient than using DATE_FORMAT to format dates as string and compare the results.
I am trying to calculate user's login time in hours and mins for current day.
I have my session table as
id| user_id | logintime |logouttime | isactive
1| 100 | 2017-06-12 22:53:53 |2017-06-13 02:53:53 | 0
2| 100 | 2017-06-13 08:53:53 |2017-06-13 09:13:53 | 0
3| 100 | 2017-06-13 10:53:53 |2017-06-13 11:33:53 | 0
4| 100 | 2017-06-13 11:53:53 |2017-06-13 12:13:53 | 0
5| 100 | 2017-06-13 12:53:53 |NULL (As user is currently logged in)| 1
I want a query which can calculate total login day of current day let say the date is 13 today. One more thing i want to mention is that in Record id 1 it states that user did login at 12-06-2017 but as i need record time of 13 so it will start from 2017-06-13 00:00:00 (as login time of that day).
Thanks in advance.
EDIT:
So far query i tried which is giving wrong calculation for the id 1 and 5 .
for id 1 it is calculating the minutes of yesterday also and for 5th it is giving null as user is currently logged in
SELECT TIMESTAMPDIFF(minute,logintime,logouttime) FROM `table` WHERE user_id= 17 and DATE(logouttime) = DATE(UTC_TIMESTAMP)
For active sessions you could use CURRENT_TIMESTAMP instead of logouttime value. Create view with corrected timestamps to avoid duplicate same select subquery in your query:
CREATE VIEW sessions_view AS
SELECT
user_id,
logintime,
IF(isactive, CURRENT_TIMESTAMP, logouttime) AS logouttime
FROM sessions_table;
To split sessions that passes from one day to another use UNION syntax with conditions. Use TIMESTAMPDIFF function to get difference between logintime and logouttime values. To get aggregated duration that is time data type, use SUM aggregate function and SEC_TO_TIME to convert seconds to DATETIME value:
SELECT
user_id,
DATE(logintime) AS `day`,
SEC_TO_TIME(SUM(TIMESTAMPDIFF(SECOND, logintime, logouttime)))
FROM
(SELECT
user_id,
logintime,
IF(DATE(logouttime)>DATE(logintime), TIMESTAMP(DATE(logouttime)), logouttime) AS logouttime
FROM sessions_view
UNION ALL
SELECT
user_id,
TIMESTAMP(DATE(logouttime)) AS logintime,
logouttime
FROM sessions_view
WHERE DATE(logouttime)>DATE(logintime)) splited_sessions
GROUP BY user_id, `day`;
If you want to get data for specific date only, then append following WHERE clause to the query:
WHERE DATE(logintime) = '2017-06-13'
Suppose I have a MySQL table that looks like the following, where I keep track of when (Date) a user (User.id) read an article on my website (Article.id):
------------------------------------------
Article_Impressions
------------------------------------------
date | user_id | article_id
--------------------+---------+-----------
2013-04-02 15:33:23 | 815 | 2342
2013-04-02 15:38:21 | 815 | 108
2013-04-02 15:39:33 | 161 | 4815
...
I'm trying to determine how many session I had, as well as average session duration per user on a given day. A session ends when an article was not read within 30 minutes after another article.
Question
How can I efficiently determine how many session I had on a given day? I'm using PHP and MySQL.
My first idea is to query all that data for a given day, sorted by user. Then I iterate through each user, check if an impression was within 30 minutes of the last impression, and tally up a total count of session each user had that day.
Since we have around 2 million impressions a day on our site, I'm trying to optimize this report generator.
Try this query
Query 1:
select
#sessionId:=if(#prevUser=user_id AND diff <= 1800 , #sessionId, #sessionId+1) as sessionId,
#prevUser:=user_id AS user_id,
article_id,
date,
diff
from
(select #sessionId:=0, #prevUser:=0) b
join
(select
TIME_TO_SEC(if(#prevU=user_id, TIMEDIFF(date, #prevD), '00:00')) as diff,
#prevU:=user_id as user_id,
#prevD:=date as date,
article_id
from
tbl
join
(select #prev:=0, #prevU=0)a
order by
user_id,
date) a
[Results]:
| SESSIONID | USER_ID | ARTICLE_ID | DATE | DIFF |
-----------------------------------------------------------------
| 1 | 161 | 4815 | 2013-04-02 15:39:33 | 0 |
| 2 | 815 | 2342 | 2013-04-02 15:33:23 | 0 |
| 2 | 815 | 108 | 2013-04-02 15:38:21 | 298 |
| 3 | 815 | 108 | 2013-04-02 16:38:21 | 3600 |
This query will return a unique session for every new user and also for same user if the next article read is after 30 mins as per your requirement mentioned in your question. The diff column returns the seconds difference between the 2 articles by the same user which helps us count the sessionId. Now using this result it will be easy for you to count the average time per user and also total time per session.
Hope this helps you...
SQL Fiddle
If the concept of the user "session" is important to your analytics, then I would start logging data in your table to make querying of session-related data not such a painful process. A simple approach would be to log your PHP session ID. If your PHP session id is set to have the same 30 minute expiry, and you log the PHP session ID to this table then you would basically have exactly what you are looking for.
Of course that won't help you with your existing records. I would probably go ahead and create the session field and then back-populate it with randomly generated "session" id's. I wouldn't look for a fully SQL solution for this, as it may not do what you want in terms of handling edge cases (sessions spanning across days, etc.). I would write a script to perform this backfill, which would contain all the logic you need.
My general approach would be to SELECT all the records like this:
SELECT user_id, date /* plus any other fields like unique id that you would need for insert */
FROM Article_Impressions
WHERE session_id IS NULL
ORDER BY user_id ASC, date ASC
Note: make sure you have index on both user_id and date fields.
I would then loop through the result set, building a temp array of each user_id, and loop through that array for all date values assigning a randomly generated session id which would change each time the date change was greater than 30 minutes. Once the user value increments, I would make inserts for that previous user to update the session_id values and then reset the temp array to empty and continue that process with the next user.
Note that it is probably important to take the approach of keeping a relatively small temp/working array like this, as with the number of records you are talking about, you are likely not going to be able to read the entire result set into an array in memory.
Once your data is populated, the query becomes trivial:
Unique sessions for each day:
SELECT DATE(date) as `day`, COUNT(DISTINCT session_id) AS `unique_sessions`
FROM Article_Impressions
GROUP BY `day`
ORDER BY `day` DESC /* or ASC depending on how you want to view it */
Average sessions per day:
SELECT AVG(sessions_per_day.`unique_sessions`) AS `average_sessions_per_day`
FROM
(
SELECT DATE(date) as `day`, COUNT(DISTINCT session_id) AS `unique_sessions`
FROM Article_Impressions
GROUP BY `day`
) AS sessions_per_day
GROUP BY sessions_per_day.`day`
Note: you need an index on the new session_id field.
I am trying to write ONE SQL query, which gives always gives three rows of results. Database is as follows:
uid | program_date | program_time | program_name
------------------------------------------------
1 | 2012-04-16 | 21:00 | Some movie
2 | 2012-04-16 | 23:00 | Program end
3 | 2012-04-17 | 10:00 | Animation
4 | 2012-04-17 | 11:00 | Some other movie
5 | 2012-04-17 | 12:00 | Some show
All I need - always have three rows - what is on air now, next and upcomming. So if today is 2012-04-16 21:00 it should output Some movie, Program end, Animation.
At 2012-04-17 00:00 it should output Program end, Animation, Some other movie.
Problem is that I need to "navigate" back in one day if there is no records WHERE program_date = date("Y-m-d") AND program_time <= date("H:i:s");
There is another problem - database does not have Unix timestamp field, only Uid, program_date (date field) and program_time (time field) and program_name.
Also, there might be, that Uid's are not inserted into table in sequence, as some program entry might be inserted in between into existing program schedule.
I am trying various approaches, but want to do everything in one SQL query, without looping in PHP.
Can anyone help me here?
As TV-people count and show time in rather strange manner, MySQL function may be created to handle their non-human ;-) logic easier:
CREATE FUNCTION TV_DATE(d CHAR(10), t CHAR(5))
RETURNS CHAR(16) DETERMINISTIC
RETURN CONCAT(d, IF (t < "06:00", "N", " "), t);
User-defined functions are declared per-database and this may be done just once. DETERMINISTIC tells that function always return the same result for the same input and internal MySQL optimizer may rely on that. N is just a letter which is larger (in string comparison) than whitespace. Consider it as mnemonics for next or night.
note: Hours should be always formatted with 2 digits!
Then using this function we may select what we need even simpler:
-- what is on air now
(SELECT `program_name`, TV_DATE(`program_date`, `program_time`) AS `tv_time`
FROM `table`
WHERE (`tv_time` <= TV_DATE(date("Y-m-d"), date("H:i"))
ORDER BY `tv_time` DESC
LIMIT 1)
UNION
-- next and upcomming
(SELECT `program_name`, TV_DATE(`program_date`, `program_time`) AS `tv_time`
FROM `table`
WHERE (`tv_time` > TV_DATE(date("Y-m-d"), date("H:i"))
ORDER BY `tv_time` ASC
LIMIT 0, 2)
Keep in mind, that if all records in DB are in future you'll get only 2 of them.
The same for situation, when the next program is the last one in DB.
You may add different constant values into queries in order to distinguish those 2 situations.