How to select data from multiple tables based on different time ranges? - php

I have two tables in MySQL:
visits
points
visits table looks like the following:
+-------+-------------------+-----------+
| id | date | user_id |
+-------+-------------------+-----------+
| 1 |2014-08-01 05:23:00| 43 |
| 2 |2014-08-01 14:41:00| 21 |
| 3 |2014-08-02 23:54:00| 43 |
| 4 |2014-08-03 03:21:00| 43 |
| 5 |2014-08-03 04:19:00| 34 |
| 6 |2014-08-03 11:33:00| 43 |
| 7 |2014-08-04 12:21:00| 43 |
| 8 |2014-08-05 01:55:00| 43 |
| 9 |2014-08-06 06:13:00| 43 |
| 10 |2014-08-07 19:47:00| 43 |
+-------+-------------------+-----------+
points table looks like the following:
+-------+-------------------+-----------+-------+----------+
| id | date | user_id | points| status |
+-------+-------------------+-----------+-------+----------+
| 1 |2014-08-01 04:33:00| 43 | 10 | 0 |
| 2 |2014-08-02 05:21:00| 21 | 23 | 0 |
| 3 |2014-08-02 09:01:00| 43 | 32 | 1 |
| 4 |2014-08-02 01:21:00| 43 | 21 | 0 |
| 5 |2014-08-03 23:23:00| 34 | 54 | 0 |
| 6 |2014-08-04 20:34:00| 43 | 11 | 0 |
| 7 |2014-08-04 17:54:00| 43 | 9 | 0 |
| 8 |2014-08-04 03:45:00| 43 | 34 | 0 |
| 9 |2014-08-06 08:23:00| 43 | 76 | 0 |
| 10 |2014-08-07 11:43:00| 43 | 52 | 0 |
+-------+-------------------+-----------+-------+----------+
I want execute only 1 query and achieve the following.
I'd like to count the rows in the visits table where the user_id = 43 and the date is between 2014-08-01 and 2014-08-03.
I also want to count the rows, sum the points in the points table where user_id = 43 and the date is between 2014-08-01 and 2014-08-03 and the status is 0.
After that, in the same query, I'd like to select the same as above, but in a different timeframe, like: 2014-08-04 and 2014-08-07.
Is there any query out there which can solve this problem for me?
(I'm actually doing this because I'd like to get data for one of my jQuery chart which is called: Morris.js. I'd like to get 12 datasets if the visitor selects a time range and divide it based on the days, the visitor selected. For example: if he selects: 2014-08-01 till 2014-08-01, I want to display him 12 datasets of that day. But if he selects for example: 2014-08-01 till 2014-08-06, then I'd want to display him the data for the 6 day divided by 12.)
If you don't understand something here, just let me know and I'll explain it better. The point is that I'd like to collet the datasets and draw the chart based on the time range to the visitor. Is that above MySQL logic a good solution for my issue?
EDIT:
As per request I'm showing the desired result in here:
+---------------------+-----------+-------+-------+-------------+
| date | user_id | points| visits| points_count|
+---------------------+-----------+-------+-------+-------------+
|2014-08-01-2014-08-03| 43 | 31 | 4 | 2 |
|2014-08-04-2014-08-07| 43 | 182 | 4 | 5 |
+---------------------+-----------+-------+-------+-------------+
I hope I've calculated everything correctly.

ok so this took a little work because you have to join the two tables independantly of eachother.. the reason being is because one can't have the status of 1 and the other can.. so with that in mind this query will return exactly what you want.
QUERY:
SELECT
t.join_date as 'Time Frame',
t1.user_id,
t.num_visits,
t1.num_points,
t1.total_points
FROM
( SELECT
CASE
WHEN DATE(date) >= '2014-06-01' AND DATE(date) <= '2014-07-10' THEN 1
WHEN DATE(date) >= '2014-08-05' AND DATE(date) <= '2014-08-07' THEN 2
ELSE 3
END AS grouping_col,
CONCAT(MIN(DATE(date)), ' - ', MAX(DATE(date))) as join_date,
COUNT(id) as num_visits
FROM visits
WHERE user_id = 43
GROUP BY grouping_col
)t
LEFT JOIN
( SELECT
CASE
WHEN DATE(date) >= '2014-06-01' AND DATE(date) <= '2014-07-10' THEN 1
WHEN DATE(date) >= '2014-08-05' AND DATE(date) <= '2014-08-07' THEN 2
ELSE 3
END AS grouping_col,
CONCAT(MIN(DATE(date)), ' - ', MAX(DATE(date))) as join_date,
user_id,
COUNT(id) as num_points,
SUM(points) as total_points
FROM points
WHERE user_id = 43
AND status = 0
GROUP BY grouping_col
)t1 ON t1.grouping_col = t.grouping_col
WHERE t.grouping_col IN(1, 2) OR t1.grouping_col IN(1, 2)
NOTE:
you can add as many timeframes to this by just adding more rows to the CASE statement...
SEE DEMO:
FIDDLE

A better option may be to actually use a stored procedure and set a beginning date param and an end date param so you can pick any dates you want

The only approach which comes to mind is to do two distinct query and use UNION ALL
SELECT *
FROM
(SELECT
count(v.id) AS visit_count,
count(p.id) AS point_count,
sum(p.points) AS points
FROM visits v1
JOIN points p1
ON v1.user_id = p1.user_id
WHERE v1.user_id = 43
AND DATE(v1.date) BETWEEN '2014-08-01' AND '2014-08-03'
AND p1.status = 0
UNION ALL
SELECT
count(v.id) AS visit_count,
count(p.id) AS point_count,
sum(p.points) AS points
FROM visits v2
JOIN points p2
ON v2.user_id = p2.user_id
WHERE v2.user_id = 43
AND DATE(v2.date) BETWEEN '2014-08-04' AND '2014-08-07'
AND p2.status = 0
) temp

You could try:
select count(id)
from visits
join points on points.user_id = visits.user_id
where date between 2014-08-04 00:00:00 and 2014-08-07 00:00:00
and visits.user_id = 43

Related

how to find the max value in the table, if there is the same value, then retrieve data from the smallest ID among the data found (mysql 5.5.36)

I have two tables, with names of identities and results.
I want to retrieve all data, based on the rs_val column which has the maximum value, then group data by column identities.idn_year and identities.idn_tag
if there is the same maximum value, then the data displayed is based on the smallest rs_id among the same data, but different data years and tags are still displayed
**Table identities**
idn_kode | idn_name | idn_year | idn_tag
IDN.001 Andi 2017 3
IDN.002 sarah 2017 3
IDN.003 Jhon 2017 3
IDN.004 Doe 2018 5
IDN.005 Mark 2018 5
IDN.006 Sisca 2018 5
**Table result**
rs_kode | idn_kode | rs_id | rs_val
RS.001 IDN.002 2 73
RS.002 IDN.004 4 90
RS.003 IDN.005 5 90
RS.004 IDN.006 6 85
RS.005 IDN.003 3 100
RS.006 IDN.001 1 65
I tried using this query
SELECT * FROM (SELECT * FROM result ORDER BY rs_val DESC ) x INNER JOIN identities a on x.idn_kode = a.idn_kode GROUP BY a.idn_yer, a.idn_tag
**the results of the query above**
idn_name | idn_year | idn_tag | id | rs_val
Jhon 2017 3 3 100
Mark 2018 5 5 90
Expected results
idn_name | idn_year | idn_tag | rs_id | rs_val
Doe 2018 5 4 90
Jhon 2017 3 3 100
please, is there someone who can help me?
For MySql 8.0+ you can do it with ROW_NUMBER():
with cte as (
select i.idn_name, i.idn_year, i.idn_tag, r.rs_id, r.rs_val,
row_number() over (partition by i.idn_year, i.idn_tag order by r.rs_val desc, r.rs_id) rn
from identities i inner join result r
on r.idn_kode = i.idn_kode
)
select idn_name, idn_year, idn_tag, rs_id, rs_val
from cte
where rn = 1;
See the demo.
Results:
| idn_name | idn_year | idn_tag | rs_id | rs_val |
| -------- | -------- | ------- | ----- | ------ |
| Jhon | 2017 | 3 | 3 | 100 |
| Doe | 2018 | 5 | 4 | 90 |
For MySql 5.5 use this query:
select i.idn_name, i.idn_year, i.idn_tag, min(r.rs_id) rs_id, t.val
from identities i
inner join result r on r.idn_kode = i.idn_kode
inner join (
select i.idn_year, i.idn_tag, max(r.rs_val) val
from identities i inner join result r
on r.idn_kode = i.idn_kode
group by i.idn_year, i.idn_tag
) t on t.idn_year = i.idn_year and t.idn_tag = i.idn_tag and t.val = r.rs_val
group by i.idn_year, i.idn_tag, t.val
For this code to work you must have [ONLY_FULL_GROUP_BY][2] SQL mode disabled.
See the demo.

php mysql select by month between records

I've this MySQL table my_table:
+-------+------------+-----------+
|Student| Date | Classroom |
+-------+------------+-----------+
| 1 | 2018-01-01 | 101 |
| 2 | 2018-01-01 | 102 |
| 3 | 2018-01-01 | 103 |
| 1 | 2018-03-01 | 104 |
| 2 | 2018-06-01 | 103 |
| 3 | 2018-09-01 | 104 |
| 1 | 2018-11-01 | 106 |
| 2 | 2018-12-01 | 101 |
+-------+------------+-----------+
The students stay in the assigned classroom till changed.
I'm trying to get which classroom they were in for a certain month.
For example in October(10), student 1 was in 104, 2 was in 103, and 3 was in 104.
I'm really unsure on how to proceed with this one so any help is appreciated.
Currently using this query based on Strawberry answer
SELECT x.*
FROM my_table x
LEFT OUTER JOIN my_table y
ON y.student = x.student
AND y.date < x.date
WHERE x.date <= LAST_DAY('2018-10-01')
GROUP BY student
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(Student INT NOT NULL, Date DATE NOT NULL, Classroom INT NOT NULL,PRIMARY KEY(student,classroom));
INSERT INTO my_table VALUES
(1,'2018-01-01',101),
(2,'2018-01-01',102),
(3,'2018-01-01',103),
(1,'2018-03-01',104),
(2,'2018-06-01',103),
(3,'2018-09-01',104),
(1,'2018-11-01',106),
(2,'2018-12-01',101);
SELECT x.*
FROM my_table x
JOIN
( SELECT student
, MAX(date) date
FROM my_table
WHERE date <= LAST_DAY('2018-10-01')
GROUP
BY student
) y
ON y.student = x.student
AND y.date = x.date;
+---------+------------+-----------+
| Student | Date | Classroom |
+---------+------------+-----------+
| 1 | 2018-03-01 | 104 |
| 2 | 2018-06-01 | 103 |
| 3 | 2018-09-01 | 104 |
+---------+------------+-----------+
Here's a go at it (snippet to go in a stored procedure; assumes table called example & output to table months). It produces a row per student for each month of the range.
drop table months;
create table months (month date, student integer, classroom integer);
set #month = (select min(date) from example);
start_loop: LOOP
insert into months select #month, s1.student, classroom from
(select student, max(date) as maxdate from example where date <= #month group by student) s1
join example s2 on s1.student = s2.student and maxdate = date;
if #month = (select max(date) from example) then
leave start_loop;
end if;
set #month = #month + interval 1 month;
END LOOP start_loop;
Let's break the problem into two parts. Firstly, find all the rooms which have been allocated to student A so far and sort them using the date. Next, find the record which is just before or equal to the required month.
For example:
Consider student 1. We get
+-------+------------+-----------+
|Student| Date | Classroom |
+-------+------------+-----------+
| 1 | 2018-01-01 | 101 |
| 1 | 2018-03-01 | 104 |
| 1 | 2018-11-01 | 106 |
+-------+------------+-----------+
Now, let's say for month June we try to find month just less than or equal to 2018-06-01 to get the required room number. I hope this will help.

Get the position of a row after sorting mysqli result

I am looking for a way to get the place of a specific item after sorting my mysqliresult. I have a table like this:
id_______|_Name_____________|_ParentID
58 | Carl | 15
55 | Clark | 15
12 | David | 4
23 | Sophie | 15
45 | Amanda | 15
I am only interested in the rows with ParentID 15 and I want to sort them by their Name. Then I want to look at a specific item, say id 55 (Clark) and know which row number it is placed on. I want to generate a table like this:
id_______|_Name_____________|_ParentID
45 | Amanda | 15
58 | Carl | 15
55 | Clark | 15
23 | Sophie | 15
I then want to get the number 3 when I am interested in Clark, since his row is the third row in this new table.
Is this at all possible to do with mysqli? If it can't be done in a single statement, I have to loop through all selected items and have a count that increases for every item until I find my correct one but I would really like to avoid this since the number of items to loop through quickly increases.
Try this:
SELECT t.*,
(SELECT COUNT(*) FROM YourTable s
WHERE t.parent_id = s.parent_id
and t.name <= s.name) as Cnt
FROM YourTable t
Output :
id_______|_Name_____________|_ParentID__|_Cnt
58 | Carl | 15 | 2
55 | Clark | 15 | 3
12 | David | 4 | 1
23 | Sophie | 15 | 4
45 | Amanda | 15 | 1
Now Cnt column contain the row position, so to get any one of their positions:
SELECT tt.cnt
FROM (...above query here...) tt
WHERE tt.name = ? --optional
AND tt.parent_id = ? --optional
You can get the row for Clark by doing:
select count(*)
from t
where t.ParentId = 15 and
t.Name <= 'Clark';
Indexes t(Name, ParentId) will speed performance.
In mysql you can use User-Defined Variables
SELECT T.*, if(#a, #a:=#a+1, #a:=1) as rownum
FROM T
WHERE ParentID=15
ORDER BY Name
SQLFiddle demo
Try
SELECT * FROM table_name WHERE parent_id = 15 ORDER BY name ASC;

Mysql Query SUM adding each to eachother result?

Sorry if my question makes no sense. Not sure if we can do this with mysql only. Lets say I have this query:
SELECT SUM(win) * 100 as win_profit, date, uid FROM `tips` WHERE uid = 60 AND placed = 1 GROUP by date
This would obviously get the sum of the win column each day that is in the database.
Lets say the database had:
|___win___|____date____|
| 10 | 2014-04-16 |
| 10 | 2014-04-16 |
| 10 | 2014-04-17 |
| 10 | 2014-04-18 |
| 10 | 2014-04-18 |
| 10 | 2014-04-18 |
| 10 | 2014-04-19 |
| 10 | 2014-04-19 |
| 10 | 2014-04-19 |
This would result:
20
10
30
30
How can I get it to result so each adds up, mysql query only. So the result would be:
20
30
60
90
You could get all distinct dates, and LEFT JOIN to find the sum of all values up to that date; I kept the 100 multiplier from your sample query, but you need to remove it to get a result matching your desired result.
SELECT 100 * SUM(b.win), a.date
FROM (SELECT DISTINCT date FROM `tips`) a
LEFT JOIN tips b ON a.date >= b.date
GROUP BY a.date
ORDER BY a.date
An SQLfiddle to test with.
This could be another way to do it...
SET #full_sum=0;
SELECT #full_sum+SUM(win) as win_profit, date as this_date, uid,
#full_sum:=(SELECT #full_sum+SUM(win)
FROM `testing` WHERE uid = 60
GROUP by date HAVING date=this_date)
FROM `testing` WHERE uid = 60 GROUP by date;

Select highest percent first php sql

I have the following sql which selects the most recurring row first based on the column "reported"
$datan = mysql_query("
SELECT *, COUNT(reported) AS ct
FROM profile_reports
WHERE open = '1'
GROUP BY reported
ORDER BY ct DESC
LIMIT 1
") or die(mysql_error());
I want my sql to also check which 'reporter' (each is a number associated with a user) has the best percentage of useful reports, which is determined this way:
((raction > 0 AND raction < 99 AND open = '0' AND reporter = 'reporter') / (reporter = 'reporter' AND open = '0')) * 100
...and show the rows with highest percentage first. It's a little tricky because no initial reporter is set.
Here's a sample table:
+----+----------+----------+-------+----------+
| id | reporter | reported | open | raction |
+----+----------+----------+-------+----------+
| 1 | 24 | 26 | 0 | 3 |
| 2 | 24 | 23 | 0 | 0 |
| 3 | 24 | 29 | 1 | |
| 4 | 12 | 29 | 0 | 4 |
| 5 | 12 | 29 | 1 | |
| 6 | 24 | 21 | 1 | 0 |
+----+----------+----------+-------+----------+
I want it to see that there are more reports about user 29(column: reported), then check which reporting user(column: reporter) has the best percentage (based on the line of code above), in this case user 12, and display their report
Its actually pretty easy in just take the sums of your conditions and divide. In order to get the "Reported" correctly you'll need to use an inline view to find the highest report.
SELECT pr.*,
( Sum(pr.raction > 0
AND pr.raction < 99
AND pr.open = '0'
AND pr.reported = t.reported) / Sum(pr.reported = t.reported
AND pr.open = '0') ) * 100 AS
usefull
FROM profile_reports pr,
(SELECT reported
FROM profile_reports
WHERE open = '1'
GROUP BY reported
ORDER BY Count(reported) DESC
LIMIT 1) t
GROUP BY reporter
ORDER BY usefull DESC
LIMIT 1
demo
Output
| ID | REPORTER | REPORTED | OPEN | RACTION | USEFULL |
-------------------------------------------------------
| 4 | 12 | 29 | 0 | 4 | 100 |
I haven't done everything for you. You will have to decide what to do if the divisor is zero
Note in just about everything but MySQL you would need to use CASE
SUM ( CASE WHEN raction > 0 AND .... THEN 1 ELSE 0 END) / ....

Categories