Efficient way to track returning users? - php

I'm in the process of finding an efficient way to track returning users.
List of options I went over so far:
save (update) login count per user, per day/week/month
save (update) which users have logged in, in a Text field (bad choice?), per day/week/month
deduce returning users via other database resources (user-added records from several tables)
I think this last option is most efficient, since I won't need to create a separate logging table.
However, a logging table seems more accurate, period-wise, right?
Update:
Accuracy still has the priority for this, so I went with the first option, together with some logic in PHP.
I've opted to store the login counts per week (of the year), like: 201433.
Question: regarding the PHP code, is there a way to combine the two queries, and leave out the nested loop (efficiency)?
Database Table:
+----+--------+--------+-----------+
| id | userId | logins | year_week |
+----+--------+--------+-----------+
| 1 | 1 | 4 | 201432 |
| 2 | 1 | 3 | 201433 |
| 3 | 2 | 2 | 201433 |
+----+--------+--------+-----------+
Queries:
SELECT
userId,
SUM(logins) as total
FROM
User_Logins
GROUP BY
userId
ORDER BY
userId
-----------
// get first year_week
SELECT
userId,
year_week
FROM
User_Logins
GROUP BY
userId
ORDER BY
userId
Query results:
+--------+-------+
| userId | total |
+--------+-------+
| 1 | 7 |
| 2 | 2 |
+--------+-------+
+--------+-----------+
| userId | year_week |
+--------+-----------+
| 1 | 201432 |
| 2 | 201433 |
+--------+-----------+
PHP code:
$returningUsers = 0;
$userLogins = $this->model->firstQuery();
$userLoginWeeks = $this->model->secondQuery();
$today = new DateTime();
function weeksPassed($today, $year, $week)
{
$today->setISODate($today->format('Y'), $today->format('W'));
// year and week from user first login
$yearWeek = new DateTime();
$yearWeek->setISODate($year, $week);
$daysPassed = $today->diff($yearWeek)->days;
$weeksPassed = $daysPassed / 7;
return $weeksPassed;
}
if ($userLogins && $userLoginWeeks)
{
foreach ($userLoginWeeks as $userLoginWeek)
{
$userId = $userLoginWeek->userId;
$year_week = $userLoginWeek->year_week;
$year = (int) substr($year_week, 0, 4);
$week = (int) substr($year_week, 4);
$weeksPassed = weeksPassed($today, $year, $week);
$totalLogins = 0;
// Look up user logins from other query
foreach ($userLogins as $logins)
{
if ($logins->userId == $userId)
{
$totalLogins = $logins->total;
break;
}
}
$avgLogins = $totalLogins;
// Average calculated over weeks that have passed since first login
if ($weeksPassed > 0)
{
$avgLogins = $totalLogins / $weeksPassed;
}
// if average >= 1 per week => returning user
if ($avgLogins >= 1)
{
$returningUsers++;
}
}
}
return $returningUsers;

Related

Displays WHERE for two conditions in the same column

-----------------------------------------------------
| id | posts_id | users_id | ratings |
-----------------------------------------------------
| 1 | 7 | 20 | 5 |
| 2 | 8 | 20 | 3 |
| 3 | 7 | 21 | 4 |
-----------------------------------------------------
Table name: mytable
\I want to make sure that ratings between posts_id and users_id are matched on the same column.
$query = $conn->query("SELECT ratings FROM mytable WHERE posts_id=7 and users_id=20");
$row = $query->fetch_array();
echo $row['ratings'];
This query does not work. I know there must be something wrong.
I want to get results: 5
What is the best query to show ratings?
----------------UPDATE-----------------------------
Sorry, my first problem lies with the connection, and now it is resolved.
But now there is a new problem.
I want to display the total sum of the rating results.
My new code
$Rate = $conn->query("SELECT * FROM mytable WHERE posts_id=7");
while ($Rated = $Rate->fetch_array()) {
echo $Rated['ratings'] + $Rated['ratings'];
}
For example on posts_id=7
Here I expect 5 + 4 = 9
But my code results exactly 54 + 54
How to fix this code?
For the updated question, this code should be work. We can use sum() function. Check here sum() function
$Rate = $conn->query("SELECT sum(ratings) as ratings FROM mytable WHERE posts_id=7");
while ($Rated = $Rate->fetch_array()) {
echo $Rated['ratings'];
}

Yii2 - Query count per day to ActiveRecord

I have the following table structure and using Yii2 ActiveRecord methods I'd like to extract the number of bookings (OrderLine) a supplier has for each day for the next week (0 entries also required). So some way of getting a row per day per supplier, with num_bookings or potentially 0 depending on the supplier.
/--------------------\ /------------\
| OrderLine |------------------|Availability|
|--------------------| 0..n 1 |------------|
|ID {PK} | |ID {PK} |
|availabilityID {FK} | |start |
|line_status | \------------/
|supplierID {FK} |
\--------------------/
| 1
|
|
| 1
/----------\
| Supplier |
|----------|
|ID {PK} |
\----------/
Querying the database directly, using DAO, with the following SQL gives me (almost) the desired result,
select count(ol.ID) as num_bookings,
day(from_unixtime(a.start)) as order_day,
ol.supplierID
from order_line ol left join
availability a on ol.availabilityID = a.ID
where ol.line_status = "booked"
and a.start >= 1451952000 //magic number for midnight today
and a.start <= 1452556800 //magic number for seven days from now
group by order_day, ol.supplierID;
something along the lines of
------------------------------------
| num_bookings|order_day|supplierID|
------------------------------------
| 1 | 5 | 3 |
| 2 | 5 | 7 |
| 1 | 6 | 7 |
| 1 | 7 | 7 |
------------------------------------
So there should be entries of 0 for the days the given Supplier has no bookings, like so
------------------------------------
| num_bookings|order_day|supplierID|
------------------------------------
| 1 | 5 | 3 |
| 0 | 6 | 3 |
| 0 | 7 | 3 |
| 2 | 5 | 7 |
| 1 | 6 | 7 |
| 1 | 7 | 7 |
------------------------------------
[days 8+ omitted for brevity...]
I've got some php/Yii code which will [eventually] give me something similar but involves multiple queries and database connections as follows,
$suppliers = Supplier::find()->all(); // get all suppliers
$start = strtotime('tomorrow');
$end = strtotime('+7 days', $start); // init times
// create empty assoc array with key for each of next 7 days
$booking_counts[date('D j', $start)] = 0;
for ($i=1; $i<7; ++$i) {
$next = strtotime('+'.$i." days", $start);
$booking_counts[date('D j', $next)] = 0;
}
foreach ($suppliers as $supplier) {
$bookings = OrderLine::find()
->joinWith('availability')
->where(['order_line.supplierID' => $supplier->ID])
->andWhere(['>=', 'availability.start', $start])
->andWhere(['<=', 'availability.start', $end])
->andWhere(['order_line.line_status' => 'booked'])
->orderBy(['availability.start' => SORT_ASC])
->all();
$booking_count = $booking_counts;
foreach ($bookings as $booking) {
$booking_count[date('D j', $booking->availability->start)] += 1;
}
}
This gives me an array for each supplier with the count stored under the appropriate day's index but that feels quite inefficient.
Can I refactor this code to return the desired data with fewer database calls and less 'scaffold' code?
This could be is the trasposition of your firt select
$results = OrderLine::find()
->select('count(order_line.ID) as num_bookings, day(from_unixtime(availability.start)) as order_day', order_line.supplierID )
->from('order_line')
->leftjoin('availability', 'order_line.availabilityID = availability.ID')
->where( 'order_line.line_status = "booked"
and a.start >= 1451952000
and a.start <= 1452556800')
->groupBy(order_day, order_line.supplierID)
->orderBy(['availability.start' => SORT_ASC])
->all();
in this way you should obtain a row for supplierID (and order_day) avoinding the foreach on supplier
For getting the data in $results->num_bookings and order_day you need add
public $num_bookings;
public $order_day;
in your OrderLine model
I hope this is what you are looking for.

MySQL: How can I count and limit the number of post submitted by the user every hour?

I'm currently building a website where user can post(like a tweet on twitter), but I want to limit the number of post a user can submit on the website every hour.
This is what I have coded so far and it outputs the total number of post a user have.
$counter = mysql_query("SELECT COUNT(*) AS post_userID FROM post");
$num = mysql_fetch_array($counter);
$count = $num["post_userID"];
echo("$count");
if($count > 2) {
echo("You have exceeded the posting limits, please try again in 24 hours");
}
MY POST TABLE
+------------+------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+------------+------+-----+---------+----------------+
| postID |int(11) | NO | PRI | NULL | auto_increment |
| post_userID|int(11) | NO | | NULL | |
| message |VARCHAR(140)| NO | | NULL | |
| time |datetime | NO | | NULL | |
+------------+------------+------+-----+---------+----------------+
As you can see, I have a time(datetime) stored on my database and it holds the time when the post was submitted.
EXAMPLE:
+------------+------------+------------+----------+
| postID |post_userID | message | time |
+------------+------------+------------+----------+
| 1 | 25 | Hello Mike |1413620228|
| 2 | 26 | Hi John! |1413620332|
+------------+------------+------------+----------+
Oh btw, my server type is MySQL.
One way to do this would be:
select count(*) from post
where post_userID = 25
and time >= date_add(now(), INTERVAL -1 HOUR);
This will give you the number of posts for the user in the past hour.
Link to SQL Fiddle
I would assume that you have a users table. Create a column in the users table called last_post_submit_time and then whenever a user submits the post save that time in it. Finally whenever a user submits a post check if the current time - it's last_post_submit_time >= one hour. I hope you get it...
You can simply divide the timestamp by 3600 and round it to integer, something like this:
SELECT
ROUND(time/3600) AS hours_since_epoch
FROM
posts
WHERE
port_userID = <user> AND hours_since_epoch = ROUND(UNIX_TIMESTAMP(NOW())/3600)
This will return you the list posts user posted this hour (roughly).
Another way is to see how much the user has posted for the last hour:
SELECT
COUNT(*)
FROM
posts
WHERE
port_userID = <user> AND time > UNIX_TIMESTAMP(NOW()) - 3600

Is it possible to sort by the duration of time when its computed based on the joined table?

I have a query to get the total number of duration of when the user become internal,hm,rec status. I wanted to ask, is it possible to sort it by total number of duration when it's re calculated from another table? Please see my query structure to understand further.
The user can be assigned into many status. It can be that he was in internal status for 4th times,rc for 2 times and etc.
$sql = "SELECT * FROM user WHERE creation_date < NOW()";
$qry = mysql_query($sql);
$res = array();
while($row=mysql_fetch_assoc($qry))
{
$res[] = $row;
$res['duration'] = getTimeDuration($row['userId'],$row['startDate']);
}
function getTimeDuration($userId,$startDate);
{
$sql = "SELECT statusDate FROM userStatus WHERE userId=$userId AND status IN('internal','hm','rec') ORDER BY statusDate DESC
$qry = mysql_query($sql);
While($row=mysql_fetch_assoc($qry))
{
$diff = $row['statusDate']-$startDate;
$duration = $duration + $diff;
}
return $duration;
}
This is the table structure
user Table
+----------+--------------+
| userId | startDate |
+----------+--------------+
| 1 | 2011-09-18 |
+----------+--------------+
| 2 | 2012-05-25 |
+----------+--------------+
userStatus Table
+----------+--------------+--------------+
| userId | statusDate | status |
+----------+--------------+--------------+
| 1 | 2012-09-18 | internal |
+----------+--------------+--------------+
| 1 | 2012-10-18 | hm |
+----------+--------------+--------------+
| 1 | 2012-10-25 | rec |
+----------+--------------+--------------+
| 1 | 2012-11-05 | manager |
+----------+--------------+--------------+
| 1 | 2012-11-15 | assistant |
+----------+--------------+--------------+
| 1 | 2012-12-05 | internal |
+----------+--------------+--------------+
| 2 | 2012-10-05 | rec |
+----------+--------------+--------------+
| 2 | 2012-11-05 | internal |
+----------+--------------+--------------+
Output:
http://screencast.com/t/BsgouWSc5H3m
In my screenshot, USER ID: 1 had a total time duration all in all for 1589. USER ID: 2 has a duration of 297. What I want is to add the ability to sort base on the total time of duration. In this case, if DESC USER ID 1 will show first. Then, if ASC USER ID: 2 will show first.

How do I perform an iterative calculation using a value from another table selected based on a YYYY-MM date and write it back to the first table?

Started learning PHP and MySQL yesterday and have managed to create two tables, insert rows and then display that data on a web page using various different groupings. Now I need to do a calculation based on data in the two tables and write the result back to one of the tables.
I'm trying to figure out how to perform an equation for a row with a date in table A using a range of values associated with a range of dates in table B. The two dates are in the format YYYY-MM-DD, but the days mostly do not match, so I need match on the month.
Here's the two tables I have:
Table A (user)
+----+----------+------------+-------------+
| id | username | start-date | bench-value |
+----+----------+------------+-------------+
| 1 | tim | 2010-03-04 | |
+----+----------+------------+-------------+
| 2 | jim | 2010-05-30 | |
+----+----------+------------+-------------+
| 3 | fred | 2010-06-12 | |
+----+----------+------------+-------------+
| 4 | sam | 2010-08-16 | |
+----+----------+------------+-------------+
| 5 | jane | 2010-10-21 | |
+----+----------+------------+-------------+
| 6 | ella | 2010-10-21 | |
+----+----------+------------+-------------+
| 7 | bob | 2011-01-24 | |
+----+----------+------------+-------------+
Table B (benchmark)
+----+------------+---------+
| id | start-date | value |
+----+------------+---------+
| 1 | 2010-01-31 | 1173.19 |
+----+------------+---------+
| 2 | 2010-02-28 | 1199.85 |
+----+------------+---------+
| 3 | 2010-03-31 | 1264.91 |
+----+------------+---------+
| 4 | 2010-04-30 | 1263.43 |
+----+------------+---------+
| 5 | 2010-05-31 | 1211.36 |
+----+------------+---------+
| 6 | 2010-06-30 | 1187.32 |
+----+------------+---------+
| 7 | 2010-07-31 | 1218.30 |
+----+------------+---------+
| 8 | 2010-08-31 | 1207.96 |
+----+------------+---------+
| 9 | 2010-09-30 | 1272.12 |
+----+------------+---------+
| 10 | 2010-10-31 | 1280.27 |
+----+------------+---------+
| 11 | 2010-11-30 | 1275.60 |
+----+------------+---------+
| 12 | 2010-12-31 | 1346.45 |
+----+------------+---------+
| 13 | 2011-01-31 | 1337.07 |
+----+------------+---------+
| 14 | 2011-02-28 | 1338.37 |
+----+------------+---------+
| 15 | 2011-03-31 | 1349.14 |
+----+------------+---------+
And here's an example of what I'm trying to achieve:
tim's current bench value today = the sum of: (first(benchmark.value)/latest(benchmark.value))for every month from the first to the latest month inclusive
First date = 2010-03 which is id 3 = 1264.91
Latest date = 2011-03 which is id 15 = 1349.14 (this is always the last row as I am trying to calculate on "today" and nothing in the future)
1/(first/latest) =1/(1264.91/1349.14) = 1.0666 [this is bench.id=3]
...now iterate...
1/(next/latest) =1/(1263.43/1349.14) = 1.0678 [bench.id=4]
1/(next/latest) =1/(1211.36/1349.14) = 1.1137 [bench.id=5]
1/(next/latest) =1/(1187.32/1349.14) = 1.1363 [bench.id=6]
1/(next/latest) =1/(1218.30/1349.14) = 1.1074 [bench.id=7]
1/(next/latest) =1/(1207.96/1349.14) = 1.1169 [bench.id=8]
1/(next/latest) =1/(1272.12/1349.14) = 1.0605 [bench.id=9]
1/(next/latest) =1/(1280.27/1349.14) = 1.0538 [bench.id=10]
1/(next/latest) =1/(1275.60/1349.14) = 1.0577 [bench.id=11]
1/(next/latest) =1/(1346.45/1349.14) = 1.0020 [bench.id=12]
1/(next/latest) =1/(1337.07/1349.14) = 1.0090 [bench.id=13]
1/(next/latest) =1/(1338.37/1349.14) = 1.0080 [bench.id=14]
...and finish up...
1/(current/latest) =1/(1349.14/1349.14) = 1.0000 [bench.id=15]
Total = 13.7997 = 1.0666 + 1.0678 + 1.1137 + 1.1363 + 1.1074 + 1.1169 + 1.0605 + 1.0538 + 1.0577 + 1.002 + 1.009 + 1.008 + 1
So I would then want to write that result back to Table A, giving me:
Table A (user)
+----+----------+------------+-------------+
| id | username | start-date | bench-value |
+----+----------+------------+-------------+
| 1 | tim | 2010-03-04 | 13.7997 |
+----+----------+------------+-------------+
As this is an iterative process it would be a much shorter calculation for a user like 'bob' who started in 2011-01.
I would also like to be able to do this every 4 months to produce termly stats so that someone like user tim would be calculated like this (the initial search to find the first date would need to take into account over a 4 month period):
1/(first/latest) = 2010-03 = 1/(1264.91/1349.14) = 1.0666
1/(next/latest) = 2010-07 = 1/(1218.30/1349.14) = 1.1074
1/(next/latest) = 2010-11 = 1/(1275.60/1349.14) = 1.0577
1/(current/latest) = 2011-03 = 1/(1349.14/1349.14) = 1.0000
Total = 1.0666 + 1.1074 + 1.0577 + 1 = 4.2317
That major issues I'm having are two fold:
1. how to use the user.start-date value for each user to pick the first(benchmark.value) based ont he year and the month (day is unimportant).
2. how to iteratively calculate the formula up to and including the latest value in the bench table - at the end of april, a new row with id=16 would be added and if this were run then the April value would become the last value used in the calculation.
As I'm learning SQL And PHP right now I'm not sure which parts of this process should be done in SQL and which in PHP.
Any and all help would be greatly appreciated as I'm determined to figure this out.
That major issues I'm having are two fold:
how to use the user.start-date value
for each user to pick the
first(benchmark.value) based on the
year and the month (day is
unimportant).
how to iteratively calculate the
formula up to and including the
latest value in the bench table - at
the end of april, a new row with
id=16 would be added and if this
were run then the April value would
become the last value used in the
calculation.
As I'm learning SQL And PHP right now I'm not sure which parts of this process should be done in SQL and which in PHP.
Any and all help would be greatly appreciated as I'm determined to figure this out.
Just for reference, I've been reading:
http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html
http://www.databasejournal.com/features/mssql/article.php/10894_2191631_3/Working-with-SQL-Server-DateTime-Variables.htm
There were more but it won't let me post the other links yet...
There's almost too much info out there, so some guided advice would be realy appreciated. Thanks again.
I wouldn't include the column bench-value in table A. This value will be constantly changing, so it would be better to create a View that would calculate the latest User bench-value or create a stored procedure that takes a User as a parameter and then returns the bench-value
There also needs to be a link/key between the two tables, right now there is no way to tell which user is related to which benchmark
Wow, what a well asked question. Sadly my reply maybe a lot shorter.
What I think you're looking for is:
bob's current bench value today = the
sum of:
first(benchmark.value)/latest(benchmark.value)
First date = 2011-01 which is id 13 =
1337.07 Latest date = 2011-03 which is id 15 = 1349.14
select username, start_date, tmp.value/tmp2.value as new_mark from tablea
join (select id,value from tableb having id=min(id) group by id) as tmp
on tablea.id=tmp.id
join (select id,value from tableb having id=max(id) group by id) as tmp2
on tablea.id=tmp2.id
That seems ugly but should work.

Categories