Get data if insert are in the same second - php

I have this table :
id idm date_play
1 5 2017-08-23 12:12:12
2 5 2017-08-23 12:12:12
3 6 2017-08-23 12:14:13
I want to identify if user has more then one insert in the same second. In the case describe I want to get the user id that is 5.
I tried like this :
SELECT `idm`, MAX(`s`) `conseq` FROM
(
SELECT
#s := IF(#u = `idm` AND (UNIX_TIMESTAMP(`date_play`) - #pt) BETWEEN 1 AND 100000, #s + 1, 0) s,
#u := `idm` `idm`,
#pt := UNIX_TIMESTAMP(`date_play`) pt
FROM table
WHERE date_play >= '2017-08-23 00:00:00'
AND date_play <= '2017-08-23 23:59:59'
ORDER BY `date_play`
) AS t
GROUP BY `idm`
Can you help me please ? Thx in advance and sorry for my english.

Assuming your dates are accurate down to the second level, you can do this with a single aggregation:
select idm
from t
group by idm
having count(*) > count(distinct date_play);
If date_play has fractional seconds, then you would need to remove those (say by converting to a string).
If you want the play dates where there are duplicates:
select idm, date_play
from t
group by idm, date_play
having count(*) >= 2;
Or, for just the idms, you could use select distinct with group by:
select distinct idm
from t
group by idm, date_play
having count(*) >= 2;
(I only mention this because this is the only type of problem that I know of where using select distinct with group by makes sense.)
If you want all the rows that are duplicated, I would go for exists instead:
select t.*
from t
where exists (select 1
from t t2
where t2.idm = t.idm and t2.date_play = t.date_play and
t2.id <> t.id
);
This should have reasonable performance with an index on (idm, date_play, id).

If your table is called mytable, the following should work:
SELECT t.`idm`
FROM mytable t INNER JOIN mytable t2
ON t.`idm`=t2.`idm` AND t.`date_play`=t2.`date_play` AND t.`id`!=t2.`id`
GROUP BY t.`idm`
Basically we join the table with itself, pairing records that have the same idm and date_play, but not the same id. This will have the effect of matching up any two records with the same user and datetime. We then group results by user so you don't get the same user id listed multiple times.
Edit:
Gordon Linoff and tadman's suggestions led me to this probably much more efficient query (credit to them)
SELECT t.`idm`
FROM mytable t
GROUP BY t.`date_play`
HAVING COUNT(t.`id`)>1

Related

How to make ranking system in php mysql?

I have a php application which has workout table where I am storing daily users workout data based on activity(like: run, walk, cycling). At the end of the month I have to create a leaderboard to generate rank based on highest workout points.
SELECT total_distance, user_id,date_created
FROM workouts
WHERE date_created LIKE '%2019-11%' AND activity_type='Run'.
with the above query I am getting output as mention below
I have optimized the query by using GROUP BY user_id in query.
SELECT SUM(total_distance) as points, user_id,date_created
FROM workouts
WHERE date_created LIKE '%2019-11%' AND activity_type='Run'
GROUP BY user_id
I want my final output as shown in 2nd image along with rank as new column.
How to write a query for that. I am not able to do it..
SELECT
CASE WHEN #l=points THEN #rank ELSE #rank:=#rank+1 END as rank,#l:=points ,points,user_id from (select
SUM(total_distance) as points, user_id,date_created
FROM workouts
WHERE date_created LIKE '%2019-07%'
GROUP BY user_id order by points desc) a, (SELECT#rank := 0) vars;
SQL Demo here http://sqlfiddle.com/#!9/0e8634/2
I would change this condition
date_created LIKE '%2019-11%'
into
month(date_created) = '11' and year(date_created) = '2019'
Final query, order rows by points desc and use row_numbers() or danse_rank() for creating ranking.
Function row_numbers()and danse_rank() are introduced since MySQL version >= 8 and since MariaDB >= 10.2.0.
For newer versions (MySQL version >= 8 OR MariaDB >= 10.2.0.)
SELECT SUM(total_distance) as points, user_id, date_created,
ROW_NUMBER() OVER (ORDER BY points desc) as rank
FROM workouts
WHERE month(date_created) = '11' AND year(date_created) = '2019' AND activity_type='Run'
GROUP BY user_id
ORDER BY points desc
For older versions (MySQL version < 8 OR MariaDB < 10.2.0.)
This is also working for (MySQL version >= 8 OR MariaDB >= 10.2.0.)
SET #rank=0;
SELECT SUM(total_distance) as points, user_id, date_created,
#rank:=#rank+1 as rank
FROM workouts
WHERE month(date_created) = '11' AND year(date_created) = '2019' AND activity_type='Run'
GROUP BY user_id
ORDER BY points desc

combine two tables and sum mysql

i want to join two tables but i can't do it as i want to sum column and get the result between two dates
first table named : vip_allotment_details
allotment_id qty
2 3
2 5
1 2
1 4
the second table name : vip_allotment
id date_from date_to
1 2017-10-1 2017-10-5
2 2017-10-6 2017-10-10
what i want from the query to get me this result
id qty date_from date_to
1 6 2017-10-1 2017-10-5
2 8 2017-10-6 2017-10-10
i will explain the result :
first allotment_id field is linked with id field in second table , the result i want that we can make sum of qty by the two fields (id , allotment_id ) between the date_from and date_to
and here is my try :
$query1 = "
SELECT SUM(qyt) as total
FROM vip_allotment_details
where allotment_id IN ( SELECT id from vip_allotment where date_from >= '$date_1' AND date_to <= '$date_2')
";
In my query the result gets all the sum of qty field with no filter ..
I hope I have explained my problem well .
thanks/.
I'm not try yet, but maybe you can try like this:
SELECT a.id AS id, SUM(qyt) AS qty, date_from, date_to
FROM vip_allotment AS a
LEFT JOIN vip_allotment_details AS b on b.allotment_id = a.id
WHERE a.date_from >= '{thedatestart}' AND a.date_to <= '{thedateend}'
GROUP BY a.id
ORDER BY a.id ASC;
You need to use JOIN. I see you are using IN keyword, which won't work. There can be many ways to solve your problem. One of them is,
select allotment_id, qty, date_from, date_to
from
(select allotment_id, SUM(qty) as qty
from vip_allotment_details group by allotment_id
) at
INNER JOIN
vip_allotment va
ON va.id= at.allotment_id;
I think the following should do what you ask.
SELECT
va.id,
SUM(vad.qyt) AS total,
va.date_from,
va.date_to
FROM vip_allotment_details AS vad
LEFT JOIN vip_allotment AS va ON va.id = vad.allotment_id
GROUP BY vad.allotment_id
Try below.i think you will get your desired result.
select va.id, temp.qty , va.date_from,va.date_to from vip_allotment as va
inner join (select sum(qty) as qty , allotment_id from vip_allotment_details group by `allotment_id`) as temp
ON temp.allotment_id=va.id
where va.date_from >= '$date_1' AND va.date_to <= '$date_2';
If you want more then one result form an aggregate function (SUM, COUNT, AVG, ...) you'll need to use a GROUP BY. Your query isn't that hard, this should do the trick:
SELECT va.id, va.date_from, va.date_to, SUM(vad.qyt) AS qyt
FROM vip_allotment AS va
LEFT JOIN vip_allotment_details AS vad ON vad.allotment_id = va.id
GROUP BY va.id
And as you can see here, this produces the expected result: http://sqlfiddle.com/#!9/707a8/2
If you now want to start adding extra filters (like filter by date), you can just do so by adding a WHERE to the query. Something like this:
...
LEFT JOIN ...
WHERE va.date_from >= "2017-10-06" and va.date_to <= "2018-10-06"
GROUP BY ...
http://sqlfiddle.com/#!9/707a8/6
On a side note, I noticed you are not binding your params in the php part of your code . Do note that this can pose serious security issues, especially if these dates come directly from the user input. I would suggest looking in to PDO to do the actual querying in PHP.
Try this..change your table name and run the query..hopefully it should give the result as your requirement..if not let me know...
select a.id
, sum(b.qty)
, a.date_from
, a.date_to
from table1 a
, table2 b
where a.id = b.allotment_id
group
by b.allotment_id

Select only one latest message from every distinct sender in php [duplicate]

I have a table ("lms_attendance") of users' check-in and out times that looks like this:
id user time io (enum)
1 9 1370931202 out
2 9 1370931664 out
3 6 1370932128 out
4 12 1370932128 out
5 12 1370933037 in
I'm trying to create a view of this table that would output only the most recent record per user id, while giving me the "in" or "out" value, so something like:
id user time io
2 9 1370931664 out
3 6 1370932128 out
5 12 1370933037 in
I'm pretty close so far, but I realized that views won't accept subquerys, which is making it a lot harder. The closest query I got was :
select
`lms_attendance`.`id` AS `id`,
`lms_attendance`.`user` AS `user`,
max(`lms_attendance`.`time`) AS `time`,
`lms_attendance`.`io` AS `io`
from `lms_attendance`
group by
`lms_attendance`.`user`,
`lms_attendance`.`io`
But what I get is :
id user time io
3 6 1370932128 out
1 9 1370931664 out
5 12 1370933037 in
4 12 1370932128 out
Which is close, but not perfect. I know that last group by shouldn't be there, but without it, it returns the most recent time, but not with it's relative IO value.
Any ideas?
Thanks!
Query:
SQLFIDDLEExample
SELECT t1.*
FROM lms_attendance t1
WHERE t1.time = (SELECT MAX(t2.time)
FROM lms_attendance t2
WHERE t2.user = t1.user)
Result:
| ID | USER | TIME | IO |
--------------------------------
| 2 | 9 | 1370931664 | out |
| 3 | 6 | 1370932128 | out |
| 5 | 12 | 1370933037 | in |
Note that if a user has multiple records with the same "maximum" time, the query above will return more than one record. If you only want 1 record per user, use the query below:
SQLFIDDLEExample
SELECT t1.*
FROM lms_attendance t1
WHERE t1.id = (SELECT t2.id
FROM lms_attendance t2
WHERE t2.user = t1.user
ORDER BY t2.id DESC
LIMIT 1)
No need to trying reinvent the wheel, as this is common greatest-n-per-group problem. Very nice solution is presented.
I prefer the most simplistic solution (see SQLFiddle, updated Justin's) without subqueries (thus easy to use in views):
SELECT t1.*
FROM lms_attendance AS t1
LEFT OUTER JOIN lms_attendance AS t2
ON t1.user = t2.user
AND (t1.time < t2.time
OR (t1.time = t2.time AND t1.Id < t2.Id))
WHERE t2.user IS NULL
This also works in a case where there are two different records with the same greatest value within the same group - thanks to the trick with (t1.time = t2.time AND t1.Id < t2.Id). All I am doing here is to assure that in case when two records of the same user have same time only one is chosen. Doesn't actually matter if the criteria is Id or something else - basically any criteria that is guaranteed to be unique would make the job here.
Based in #TMS answer, I like it because there's no need for subqueries but I think ommiting the 'OR' part will be sufficient and much simpler to understand and read.
SELECT t1.*
FROM lms_attendance AS t1
LEFT JOIN lms_attendance AS t2
ON t1.user = t2.user
AND t1.time < t2.time
WHERE t2.user IS NULL
if you are not interested in rows with null times you can filter them in the WHERE clause:
SELECT t1.*
FROM lms_attendance AS t1
LEFT JOIN lms_attendance AS t2
ON t1.user = t2.user
AND t1.time < t2.time
WHERE t2.user IS NULL and t1.time IS NOT NULL
Already solved, but just for the record, another approach would be to create two views...
CREATE TABLE lms_attendance
(id int, user int, time int, io varchar(3));
CREATE VIEW latest_all AS
SELECT la.user, max(la.time) time
FROM lms_attendance la
GROUP BY la.user;
CREATE VIEW latest_io AS
SELECT la.*
FROM lms_attendance la
JOIN latest_all lall
ON lall.user = la.user
AND lall.time = la.time;
INSERT INTO lms_attendance
VALUES
(1, 9, 1370931202, 'out'),
(2, 9, 1370931664, 'out'),
(3, 6, 1370932128, 'out'),
(4, 12, 1370932128, 'out'),
(5, 12, 1370933037, 'in');
SELECT * FROM latest_io;
Click here to see it in action at SQL Fiddle
If your on MySQL 8.0 or higher you can use Window functions:
Query:
DBFiddleExample
SELECT DISTINCT
FIRST_VALUE(ID) OVER (PARTITION BY lms_attendance.USER ORDER BY lms_attendance.TIME DESC) AS ID,
FIRST_VALUE(USER) OVER (PARTITION BY lms_attendance.USER ORDER BY lms_attendance.TIME DESC) AS USER,
FIRST_VALUE(TIME) OVER (PARTITION BY lms_attendance.USER ORDER BY lms_attendance.TIME DESC) AS TIME,
FIRST_VALUE(IO) OVER (PARTITION BY lms_attendance.USER ORDER BY lms_attendance.TIME DESC) AS IO
FROM lms_attendance;
Result:
| ID | USER | TIME | IO |
--------------------------------
| 2 | 9 | 1370931664 | out |
| 3 | 6 | 1370932128 | out |
| 5 | 12 | 1370933037 | in |
The advantage I see over using the solution proposed by Justin is that it enables you to select the row with the most recent data per user (or per id, or per whatever) even from subqueries without the need for an intermediate view or table.
And in case your running a HANA it is also ~7 times faster :D
Ok, this might be either a hack or error-prone, but somehow this is working as well-
SELECT id, MAX(user) as user, MAX(time) as time, MAX(io) as io FROM lms_attendance GROUP BY id;
select b.* from
(select
`lms_attendance`.`user` AS `user`,
max(`lms_attendance`.`time`) AS `time`
from `lms_attendance`
group by
`lms_attendance`.`user`) a
join
(select *
from `lms_attendance` ) b
on a.user = b.user
and a.time = b.time
I have tried one solution which works for me
SELECT user, MAX(TIME) as time
FROM lms_attendance
GROUP by user
HAVING MAX(time)
I have a very large table and all of the other suggestions here were taking a very long time to execute. I came up with this hacky method that was much faster. The downside is, if the max(date) row has a duplicate date for that user, it will return both of them.
SELECT * FROM mb_web.devices_log WHERE CONCAT(dtime, '-', user_id) in (
SELECT concat(max(dtime), '-', user_id) FROM mb_web.devices_log GROUP BY user_id
)
select result from (
select vorsteuerid as result, count(*) as anzahl from kreditorenrechnung where kundeid = 7148
group by vorsteuerid
) a order by anzahl desc limit 0,1
I have done same thing like below
SELECT t1.*
FROM lms_attendance t1
WHERE t1.id in (SELECT max(t2.id) as id
FROM lms_attendance t2
group BY t2.user)
This will also reduce memory utilization.
Thanks.
Possibly you can do group by user and then order by time desc. Something like as below
SELECT * FROM lms_attendance group by user order by time desc;
Try this query:
select id,user, max(time), io
FROM lms_attendance group by user;
This worked for me:
SELECT user, time FROM
(
SELECT user, time FROM lms_attendance --where clause
) AS T
WHERE (SELECT COUNT(0) FROM table WHERE user = T.user AND time > T.time) = 0
ORDER BY user ASC, time DESC

SQL Left Join Return Latest Row

Two tables, with a left join. For ease table 1 and table 2.
Table 1 contains a list of people and their current status, table 2 is all of their "invites". All im trying to do as part of the join is show in a list all the current "people" and then the LATEST invite status (from table 2) so return a single row from table 2.
I have everything working... but its duplicating for example if a person has had multiple invites it will put them twice on the list. I just want to limit it to
$sql = "SELECT table1.fieldname as table1fielname table2.fieldname [more fields]
FROM xxx
LEFT JOIN xxx on table1.sharedid=table2.sharedid
WHERE XXX LIMIT 1 ";`
Obvioulsy the limit 1 doesnt do what its supposed to. I have tried adding additional select statements in brackets but being honest it just breaks everything and im not an expert at all.
I'm not an expert too but I'll try. Have you tried to use DISTINCT?
For exemple:
SELECT DISTINCT column_name1,column_name2
FROM table_name; [...]
It normally delete double matches.
Here are the links:
http://www.w3schools.com/sql/sql_distinct.asp
https://www.techonthenet.com/oracle/distinct.php
Give example data. And use good table and column names. For example:
(this returns all rows that satisfy the join):
WITH people(ppl_id,ppl_name,status) AS (
SELECT 1,'Arthur','active'
UNION ALL SELECT 2,'Tricia','active'
), invites(ppl_id,inv_id,inv_date) AS (
SELECT 1,1, DATE '2017-01-01'
UNION ALL SELECT 1,2, DATE '2017-01-07'
UNION ALL SELECT 1,3, DATE '2017-01-08'
UNION ALL SELECT 2,1, DATE '2017-01-01'
UNION ALL SELECT 2,2, DATE '2017-01-08'
)
SELECT
*
FROM people
JOIN invites USING(ppl_id)
ORDER BY 1
;
ppl_id|ppl_name|status|inv_id|inv_date
1|Arthur |active| 1|2017-01-01
1|Arthur |active| 3|2017-01-08
1|Arthur |active| 2|2017-01-07
2|Tricia |active| 2|2017-01-08
2|Tricia |active| 1|2017-01-01
But we want only 'Arthur' with '2017-01-08' and 'Tricia' with '2017-01-08'.
With any database that supports ANSI 99, you could try with a temporary table containing the newest invitation date per "people id", and join that temporary table with the invitations table. We call that table newest_invite_date, and, apparently, it does what we expect it to do:
WITH people(ppl_id,ppl_name,status) AS (
SELECT 1,'Arthur','active'
UNION ALL SELECT 2,'Tricia','active'
), invites(ppl_id,inv_id,inv_date) AS (
SELECT 1,1, DATE '2017-01-01'
UNION ALL SELECT 1,2, DATE '2017-01-07'
UNION ALL SELECT 1,3, DATE '2017-01-08'
UNION ALL SELECT 2,1, DATE '2017-01-01'
UNION ALL SELECT 2,2, DATE '2017-01-08'
), newest_invite_date(ppl_id,inv_date) AS (
SELECT ppl_id,MAX(inv_date)
FROM invites
GROUP BY ppl_id
)
SELECT
people.ppl_id
, people.ppl_name
, people.status
, newest_invite_date.inv_date
FROM people
JOIN newest_invite_date USING(ppl_id)
ORDER BY 1
;
ppl_id|ppl_name|status|inv_date
1|Arthur |active|2017-01-08
2|Tricia |active|2017-01-08
Is this what you were looking for?
Happy playing ...
Marco the Sane

Mysql group by where delta between records X

I need help to write MySQL query.
I have table full of logs where one of the column is unix timestamp.
I want to group (GROUP BY) those records so that events that were made in close range time (i.e. 5 sec) between each of them are in one group.
For example:
Table:
timestamp
----------
1429016966
1429016964
1429016963
1429016960
1429016958
1429016957
1429016950
1429016949
1429016943
1429016941
1429016940
1429016938
Become to groups like that:
GROUP_CONCAT(timestamp) | COUNT(*)
-----------------------------------------------------------------------------
1429016966,1429016964,1429016963,1429016960,1429016958,1429016957 | 6
1429016950,1429016949 | 2
1429016943,1429016941,1429016940,1429016938 | 4
Of course I can work with the data array afterwards in php, but I think that mysql would do it faster.
I started by using a variable to get the position of each row, where 1 is the highest time column and ending with the lowest, like this:
SET #a := 0;
SELECT timeCol, #a := #a + 1 AS position
FROM myTable
ORDER BY timeCol DESC;
For simplicity, we will call this positionsTable so that the rest of the query will be more readable. Once I created that table, I used a 'time_group' variable that checked if a previous row was within the last 5 seconds. If it was, we keep the same time_group. It sounds ugly, and looks kind of ugly, but it's like this:
SELECT m.timeCol, m.position,
CASE WHEN (SELECT p.timeCol FROM positionsTable p WHERE p.position = m.position - 1) <= m.timeCol + 5
THEN #time_group
ELSE #time_group := #time_group + 1 END AS timeGroup
FROM positionsTable m;
And then ultimately, using that as a subquery, you can group them:
SELECT GROUP_CONCAT(timeCol), COUNT(*)
FROM(
SELECT m.timeCol, m.position,
CASE WHEN (SELECT p.timeCol FROM positionsTable p WHERE p.position = m.position - 1) <= m.timeCol + 5
THEN #time_group
ELSE #time_group := #time_group + 1 END AS timeGroup
FROM positionsTable m) tmp
GROUP BY timeGroup;
Here is an SQL Fiddle example.
http://sqlfiddle.com/#!9/37d88/20
SELECT GROUP_CONCAT(t1.t) as `time`,
COUNT(*)
FROM (SELECT *
FROM table1
ORDER BY t) as t1
GROUP BY CASE WHEN (#start+5)>=t THEN #start
ELSE #start:=t END

Categories