mysql - select count multiple tables, group by week, custom date range - php

For example, I have 2 tables and a date range (1 dec 2015 - 10 jan 2016).
First table: USERS
id (int) date (datetime)
1 3-dec-2015
2 4-dec-2015
3 19-dec-2015
4 20-dec-2015
5 21-dec-2015
6 29-dec-2015
7 30-dec-2015
Second table: BIRTHDAYS
id (int) date (datetime)
1 6-dec-2015
2 8-dec-2015
3 9-dec-2015
4 17-dec-2015
5 28-dec-2015
The result after the query should be the following:
[0] 1st week => 2 users, 1 birthday
[1] 2nd week => 0 users, 2 birthday
[2] 3ed week => 1 users, 1 birthday
[3] 4th week => 1 users, 0 birthday
[4] 5th week => 2 users, 1 birthday
[5] 6th week => 0 users, 0 birthday
Any ideas how to achive this result or something close? I can use and PHP if needed.

I would start off with something like this:
select ((week(dateb) - week('2015-12-01')) + 1) as week_number, count(a.dateb) as userdates
from users as a
where dateb between '2015-12-01' and '2016-01-01'
group by week(dateb)
order by week(dateb);
and
select ((week(dateb2) - week('2015-12-01')) + 1) as week_number, count(dateb2) as birthdays
from birthdays
where dateb2 between '2015-12-01' and '2016-01-01'
group by week(dateb2)
order by week(dateb2);
Demo, http://sqlfiddle.com/#!9/c83cb/21
from there you can fiddle with the outputting with PHP.
Also note with this approach only rows with populated data are returned. So you should check on the iteration that each row is incremented by 1.
e.g. so for users when you got from week 1 to week 3 you should output week 2 = 0; or however you want to display it.

Related

MYSQL query that contains 2 things I need in 1 query

I have built a live scoring system for a game. When the scores are entered into the MySQL database, they are entered like this...
ID userid sessionid sprintid pointvalue1 pointvalue2 pointvalue3
1 1 1 1 10 5 2
2 2 1 1 10 5 3
3 3 1 1 12 6 3
4 1 1 2 10 6 4
5 2 1 2 12 5 3
6 3 1 2 9 4 3
As you can see from the above, there is 1 session, 2 different sprints (iterations) and 3 different users. I want to display a leader board. The leader board would show (by iteration), user name (from another table), sprint, point value 1, point value 2, point value 3, sum of point value 1 cumulative for all iterations in that session. So on iteration 1, point value 1 and sum of point value 1 cumulative would be the same. But, in iteration 2, I should see a sum of point value 1 of 20 for user 1, 22 for user 2, and 21 for user 3, in the proper order (descending). I tried a subquery using a select sum, but couldn't quite get it right, and I tried a SUM(IF()), which seemed like it would be right, but it's not. I guess I need some guidance on which direction to go.
I think this query will get you the raw data you want. You will probably want to adjust the query with some form of ordering on total points, and possibly WHERE and LIMIT clauses to meet your exact needs.
SELECT s1.userid,
s1.sessionid,
s1.sprintid,
s1.pointvalue1,
s1.pointvalue2,
s1.pointvalue3,
SUM(s2.pointvalue1) AS sum_pointvalue1,
SUM(s2.pointvalue2) AS sum_pointvalue2,
SUM(s2.pointvalue3) AS sum_pointvalue3
FROM scores s1
JOIN (SELECT userid,
sessionid,
sprintid,
SUM(pointvalue1) AS pointvalue1,
SUM(pointvalue2) AS pointvalue2,
SUM(pointvalue3) AS pointvalue3
FROM scores
GROUP BY userid, sessionid, sprintid
) s2
ON s2.userid = s1.userid AND s2.sessionid = s1.sessionid AND s2.sprintid <= s1.sprintid
GROUP BY userid, sessionid, sprintid
ORDER BY sprintid
Output:
userid sessionid sprintid pointvalue1 pointvalue2 pointvalue3 sum_pointvalue1 sum_pointvalue2 sum_pointvalue3
2 1 1 10 5 3 10 5 3
3 1 1 12 6 3 12 6 3
1 1 1 10 5 2 10 5 2
2 1 2 12 5 3 22 10 6
3 1 2 9 4 3 21 10 6
1 1 2 10 6 4 20 11 6
SQLFiddle Demo

Get data if time greater than 30

I want to get data from database if time of each data greater than 30, actually I try to delete each item after 30 days, but for now I trying to select data, I will change query to delete later. I set my code in cronjob, this is my query:
$query = $connection->prepare("SELECT id FROM ads WHERE time >= CURRENT_DATE - INTERVAL 30 DAY");
It return data as an array but the problem is some data are less than 30 days. What I have missed?
Array
(
[0] => 151 //2018-01-18 19:35:49
[1] => 164 //2018-01-25 22:56:16
[2] => 198 //2018-02-01 11:05:09
[3] => 203 //2018-02-04 20:36:34
)
Update: time is timestamp.
You are getting all results lesser than 30 days because your operator is the inverse of that you want :
SELECT id FROM ads WHERE time <= CURRENT_DATE - INTERVAL 30 DAY
Will give you the records before than 30 days from now.

Merge data on multiple rows in one

I have a table(MySQL) which stores the utilization of users. Now, what I want to do is, get the total utilization per day for each user. I can get data for each user, however, I am finding it really difficult to merge data from multiple rows for a single day.
Right now, the data I get is as :
id date download upload
1 2015-10-28 08:05:10 1 5
2 2015-10-28 10:25:15 2 5
3 2015-10-28 11:25:10 3 4
4 2015-10-29 11:25:10 8 5
5 2015-10-29 11:25:10 2 7
6 2015-10-29 11:25:10 1 3
7 2015-10-30 11:25:10 11 10
8 2015-10-30 11:25:10 4 5
9 2015-10-30 11:25:10 5 1
10 2015-10-30 11:25:10 10 1
But what I want it to appear like is :
id date download upload
1 2015-10-28 6 14
4 2015-10-29 11 15
7 2015-10-30 30 17
You can use the following query:
SELECT MIN(id) AS id, DATE(`date`) AS 'date',
SUM(download) AS download, SUM(upload) AS upload
FROM mytable
GROUP BY DATE(`date`)
The query uses DATE function in order to extract the date value from the date field.
Demo here
You can try this -
select id, date(`date`) `date`, sum(download) `download`, sum(upload) `upload`
from your_table
group by date(`date`)
order by `date`
It will sum all the downloads & uploads grouping by the date.
you need to a simple sql query lik this:
select max(user_id),op_date,sum(download_count) from test group by op_date;
data:
1 1 09-SEP-16 2
2 1 09-SEP-16 1
5 1 09-SEP-16 3
4 1 10-SEP-16 2
3 1 10-SEP-16 4
filtered data:
1 09-SEP-16 6
1 10-SEP-16 6
Use group by command
select id,`date`,sum(download) as 'download',sum(upload) as 'upload'
from ur_table
group by date(`date`)

Decrement cells when records are deleted

So I have a simple MySQL table (block) as such:
SELECT * FROM `block` WHERE 1 ORDER BY `year`, `month`, `day`, `block`;
id year month day block te status
20000 2015 12 28 1 100000 1
20001 2015 12 28 2 100000 1
20002 2015 12 28 3 100001 1
20003 2015 12 28 4 100001 1
20004 2015 12 29 1 100001 1
20005 2015 12 29 2 100001 1
20006 2015 12 29 3 100002 1
20066 2015 12 30 1 100003 1
20078 2015 12 30 1 100007 1
20070 2015 12 30 1 100004 1
20067 2015 12 30 2 100003 1
20071 2015 12 30 2 100004 1
20079 2015 12 30 2 100007 1
20072 2015 12 30 3 100004 1
20080 2015 12 30 3 100007 1
20068 2015 12 30 3 100003 1
20069 2015 12 30 4 100003 1
20073 2015 12 30 4 100004 1
20074 2015 12 31 1 100004 1
20075 2015 12 31 1 100000 1
20076 2015 12 31 2 100000 1
20077 2015 12 31 3 100000 1
20007 2016 1 1 1 100017 1
20008 2016 1 1 2 100017 1
20009 2016 1 1 3 100017 1
My question is, how can I shift all the remaining rows up if I delete a contiguous section of rows?
For example, if I delete all blocks for 2015-12-28, I want all the remaining blocks to decrement to occupy the unused blocks on the schedule.
Let me know if this is confusing or unclear. Thanks.
I think a bit more background is required. What is your eventual goal?
Two options...
First Option
Check out the link below on numbering rows in MySQL. You can use something like this to make your blocks dynamic.
http://www.xaprb.com/blog/2006/12/02/how-to-number-rows-in-mysql/
Second Option
If you are pulling data by day, just pull the data in order of the id and you can just assume that the blocks are in order in whatever application you use the data in.
For example, if I delete all blocks for 2015-12-28, I want all the remaining blocks to decrement to occupy the unused blocks on the schedule.
You do not actually specify what decrementing means. I think you want to shift the dates in all the blocks. But look here:
20000 2015 12 28 1 100000 1
20001 2015 12 28 2 100000 1
20002 2015 12 28 3 100001 1
20003 2015 12 28 4 100001 1
20004 2015 12 29 1 100001 1
20005 2015 12 29 2 100001 1
20006 2015 12 29 3 100002 1
20066 2015 12 30 1 100003 1
We have four blocks for 2015-12-28 and three for 2015-12-29. Say we delete the four 28's. What happens? Do three of the 29's shift to 28's, and the 30 become 29? Or do the three 29's and the 30 shift to fill the four places left by the 28's?
Depending on the strategy you need to choose different strategies. Perhaps you should define what exactly a block is, and what data is outside a "block", and supply a couple examples of a shift (with different numbers of days - e.g., 28 28 29 30 and you delete the two 28. BTW, can you delete only one? If so, what would happen then?).
Say each date can hold a fixed number of records
For example you can first count how many rows you're going to delete
SELECT COUNT(*) FROM ... WHERE day=28 AND month=12 AND year=2015
and then select the next items that have a later date. You then shift them, but you need to do this iteratively (once the 29's are gone, the 30's will shift, and so on). This may become quite expensive to calculate.
But in this case you might separate the date from the data:
TableA
2015 12 28
2015 12 28
2015 12 28
TableB
20000 100000 1
20001 100000 1
Now you need to join the tables in such a way that you can... well, join them. Whatever their cardinality. You might have a longer TableA than TableB, or vice versa.
This would be pretty easy in some RDBMS. In MySQL it's pretty awful (you can't even use CREATE VIEW to simplify the syntax because you'd need either a variable or a subquery in the view), and for small data sets you'd be better off by selecting TableA, then selecting TableB, and using a cycle in PHP:
// Run the query from tableA to get the dates
$TableADataSet = array();
while ($tuple = SQLFetch($rs)) {
$TableADataSet = $tuple;
}
// Run the query from tableB to get the rest
$TableBDataSet = array();
while ($tuple = SQLFetch($rs)) {
$TableBDataSet = $tuple;
}
// Now put them together.
for (;;) {
if (empty($TableADataSet)) {
if (empty($TableBDataSet)) {
break;
}
$TupleA = $EmptyRowFormattedAsTableA;
$TupleB = array_pop($TableBDataSet);
} else {
$TupleA = array_pop($TableADataSet);
if (empty($TableBDataSet)) {
$TupleB = $EmptyRowFormattedAsTableB;
} else {
$TupleB = array_pop($TableBDataSet);
}
}
// Now you have a row with dates and data "aligned".
// Missing data are taken by the sample rows EmptyRowFormattedAs... .
}
The result could be (simplifying)
2015 12 28 RowA
2015 12 28 RowB
2015 12 29 RowC
NULL NULL NULL RowD
and if you delete RowB, the rightmost part of the rows shifts upwards:
2015 12 28 RowA
2015 12 28 RowC
2015 12 29 RowD
In MySQL you first need to number the rows, and you have no RECNO() or ROW_NUMBER() function, so you do it like
SELECT #a:=#a+1 AS ranka,data1.* FROM ( SELECT * FROM TableA ORDER BY year, month, day ) AS data1, (SELECT #a:=0) AS init1;
and get your dates with a row number.
1 2015 12 28
2 2015 12 28
3 2015 12 29
You do the same for TableB. But now you have to do the "horizontal join" trick. We can be in one of these three cases:
B is longer A is longer Same length
A B A B A B
A B A B A B
- B A - A B
The "A B" part we can get with a JOIN. The "A -" part we get with a LEFT JOIN with a WHERE TableB.primarykey IS NULL. The "- B" part we get with a RIGHT JOIN WHERE TableA.primarykey IS NULL. Finally we put it all together with a UNION. And these three queries will need to be made with the awkward syntax above.
The (quite convoluted) query coming out of all this is
SELECT * FROM
( SELECT #a:=#a+1 AS rank1a, data1.*
FROM ( SELECT * FROM TableA ORDER BY year, month, day ) AS data1,
(SELECT #a:=0) AS init1
) AS ta
JOIN
( SELECT #b:=#b+1 AS rank1b, data2.*
FROM ( SELECT * FROM TableB ORDER BY something ) AS data2,
(SELECT #b:=0) AS init2 )
) AS tb
ON (rank1a = rank1b)
UNION
SELECT * FROM
( SELECT #c:=#c+1 AS rank2a, data3.*
FROM ( SELECT * FROM TableA ORDER BY year, month, day ) AS data3,
(SELECT #c:=0) AS init3
) AS tc
LEFT JOIN
( SELECT #d:=#d+1 AS rank2b, data4.*
FROM ( SELECT * FROM TableB ORDER BY something ) AS data4,
(SELECT #d:=0) AS init4 )
) AS td
ON (rank2a = rank2b) WHERE ( rank2b IS NULL )
UNION
SELECT * FROM
( SELECT #e:=#e+1 AS rank3a, data5.*
FROM ( SELECT * FROM TableA ORDER BY year, month, day ) AS data5,
(SELECT #e:=0) AS init5
) AS te
LEFT JOIN
( SELECT #f:=#f+1 AS rank3b, data6.*
FROM ( SELECT * FROM TableB ORDER BY something ) AS data6,
(SELECT #f:=0) AS init6 )
) AS tf
ON (rank3a = rank3b) WHERE ( rank3a IS NULL )
which, from a performance point of view, is probably a hog.

MySql - count from beginning until each day

Let's say I have the following table (keep in mind that this table will have 10000+ rows):
id total date
1 5 2015-05-16
2 8 2015-05-17
3 4 2015-05-18
4 9 2015-05-19
5 3 2015-05-20
I want the query to give the following result:
1
date => 2015-05-16
total => 5
2
date => 2015-05-17
total => 13
3
date => 2015-05-18
total => 17
4
date => 2015-05-19
total => 26
5
date => 2015-05-20
total -> 29
I can't think of any query that would do this right now, that's why I am not providing any code that I have tried.
Any thoughts? I am not sure if this is possible only with mysql, maybe I have to use and php.
This could be done using user defined variable in mysql and then get the running total as
select
id,
total,
date
date from
(
select
id,
#tot:= #tot+total as total,
date from my_table,(select #tot:=0)x
order by date
)x
You can do this -
SELECT
a.id,
a.date,
(SELECT SUM(b.total) FROM your_table WHERE b.date <= a.date) as new_total
FROM your_table a, your_table b
ORDER BY a.date ASC
This should do it:
select id, (select sum(total) from table a where a.date <= b.date) from table b

Categories