Recovery the missing hour, minute interval MySQL database - mysql

This is part of my table on MySQL database
+----------+---------------------+--------+
| sID | sDatetime | sETX |
+----------+---------------------+--------+
| 16213404 | 2020-04-24 16:00:00 | 497681 |
| 16213398 | 2020-04-20 14:58:56 | 281011 |
+----------+---------------------+--------+
This table count with 14.121.398 records
I realized that in this case more than one hour has passed between the previous and the next row
mysql> SELECT
TIMEDIFF(
'2020-04-20 16:00:00',
'2020-04-20 14:58:56'
);
+------------------------------------------------------------------+
| TIMEDIFF(
'2020-04-20 16:00:00',
'2020-04-20 14:58:56'
) |
+------------------------------------------------------------------+
| 01:01:04 |
+------------------------------------------------------------------+
1 row in set
this is not possible because the data is downloaded maximum from the source every five minutes
in this case is missing the time slot between 3pm and 4pm
I have tried this query without success because the return is all zero
I think because the sID is not consecutive
The code I've tried below
SELECT A.`sID`, A.`sDatetime`, (B.`sDatetime` - A.`sDatetime`) AS timedifference
FROM tbl_2020 A INNER JOIN tbl_2020 B ON B.sID = (A.sID + 1)
ORDER BY A.sID ASC;
how can i find this anomaly in mysql table?
my version of MySQL is 5.5.62-log
the name of column is sDatetime the type is Datetime.
any suggestion, please?
thanks in advance for any help
edit #01
+----------+-----------+---------------------+
| sID | time_diff | sDatetime |
+----------+-----------+---------------------+
| 18389322 | 301 | 2020-05-16 23:53:29 |
| 18390472 | 308 | 2020-05-16 23:48:21 |
| 18389544 | 301 | 2020-05-16 23:43:20 |
| 18388687 | 303 | 2020-05-16 23:38:17 |
| 18388398 | 301 | 2020-05-16 23:33:16 |
| 18390451 | 308 | 2020-05-16 23:28:08 |
| 18388915 | 302 | 2020-05-16 23:23:06 |
| 18388208 | 301 | 2020-05-16 23:18:05 |
| 18390516 | 301 | 2020-05-16 23:13:04 |
| 18389904 | 301 | 2020-05-16 23:08:03 |
+----------+-----------+---------------------+
mysql> SELECT
TIMEDIFF(
'2020-05-16 23:53:29',
'2020-05-16 23:48:21'
) AS td;
+----------+
| td |
+----------+
| 00:05:08 |
+----------+
1 row in set

You should try something like this
SELECT
sID
,TIME_TO_SEC(TIMEDIFF(#date,sDatetime)) time_diff
,#date := sDatetime
,sETX
FROM(
SELECT * FROM table1
ORDER BY sDatetime DESC) s1,(SELECT #date :=(SELECT MAX(sDatetime) FROM table1)) s2
HAVING time_diff > 300
First you order the table by time, then you get the time difference between two consecutive rows and check if they are bigger than 5 minutes
see example here https://www.db-fiddle.com/f/2yKt6d5RWngXVYJKPGZL6m/8

Comparing current row to previous works
drop table if exists t;
create table t
(sID int, sDatetime datetime, sETX int);
insert into t values
( 16213404 , '2020-04-24 16:00:00' , 497681),
( 16213398 , '2020-04-20 14:58:56' , 281011);
select sid,sdatetime,(select sdatetime from t t1 where t1.sid < t.sid order by t1.sid desc limit 1) prevdt,
time_to_sec(sdatetime) - time_to_sec((select sdatetime from t t1 where t1.sid < t.sid order by t1.sid desc limit 1)) diff
from t
where time_to_sec(sdatetime) - time_to_sec((select sdatetime from t t1 where t1.sid < t.sid order by t1.sid desc limit 1)) > 300;
+----------+---------------------+---------------------+------+
| sid | sdatetime | prevdt | diff |
+----------+---------------------+---------------------+------+
| 16213404 | 2020-04-24 16:00:00 | 2020-04-20 14:58:56 | 3664 |
+----------+---------------------+---------------------+------+
1 row in set (0.002 sec)
If this is too slow add your table definition so that we can see the indexes you have.

Related

GROUP BY same record but different TIMESTAMP/DATETIME

I'm trying to GROUP BY same records but different timestamp or datetime.
The difference of time is only about 3 minutes from the first entry.
example:
This is what the database table looks like.
*-------------------------------------------*
| id | name | time |
| 1 | Lei | 2018-02-21 12:00:10 |
| 2 | Lei | 2018-02-21 12:01:11 |
| 3 | Lei | 2018-02-21 12:01:15 |
| 4 | Lei | 2018-02-21 12:01:16 |
| 5 | Anna | 2018-02-21 12:03:11 |
| 6 | Anna | 2018-02-21 12:03:13 |
| 7 | Bell | 2018-02-21 12:05:01 |
| 8 | Lei | 2018-02-21 12:10:00 |
*-------------------------------------------*
I want to get Lei's entry from 12:00:10 up to 3 minutes from her first timestamp or datetime record.
so the output would be like this.
*-------------------------------------------*
| id | name | time |
| 1 | Lei | 2018-02-21 12:00:10 |
| 5 | Anna | 2018-02-21 12:03:11 |
| 7 | Bell | 2018-02-21 12:05:01 |
| 8 | Lei | 2018-02-21 12:10:00 |
*-------------------------------------------*
I'll be gladly appreciate your help, mysql or php it is.
SQL Fiddle
MySQL 5.6 Schema Setup:
CREATE TABLE Table1
(`id` int, `name` varchar(4), `time` datetime)
;
INSERT INTO Table1
(`id`, `name`, `time`)
VALUES
(1, 'Lei', '2018-02-21 12:00:10'),
(2, 'Lei', '2018-02-21 12:01:11'),
(3, 'Lei', '2018-02-21 12:01:15'),
(4, 'Lei', '2018-02-21 12:01:16'),
(5, 'Anna', '2018-02-21 12:03:11'),
(6, 'Anna', '2018-02-21 12:03:13'),
(7, 'Bell', '2018-02-21 12:05:01')
;
Query 1:
select id, name, min(time) as time
from Table1
group by name
order by time
Results:
| id | name | time |
|----|------|----------------------|
| 1 | Lei | 2018-02-21T12:00:10Z |
| 5 | Anna | 2018-02-21T12:03:11Z |
| 7 | Bell | 2018-02-21T12:05:01Z |
OR if you want to group by interval 3 minute you can do it like this
select id, name, min(time) as time
from Table1
group by name, UNIX_TIMESTAMP(time) DIV 180
order by time
;
With your sample data, you don't need to consider the timestamp at all:
select (#rn := #rn + 1) as id, name, min(time) as time
from t cross join
(select #rn := 0) params
group by id, name;
Grouping things by three minute intervals, from the first record in the interval is much harder. This requires either variables or recursive CTEs.
Looks like you need something like this:
select *
from mytable t
where not exists (
select *
from mytable t1
where t1.name = t.name
and t1.id <> t.id
and t1.time >= t.time - interval 3 minute
and t1.time < t.time
);
Demo: http://sqlfiddle.com/#!9/03cf16/1
It will select rows only if no row with the same name exists within a three munutes interval.

I want create query using sum and substarct function in single query using multiple conditions using mysql

In the above table I want to sum where ledgertype='Earning' and substract where
ledgertype='Deduction' and display both values..... how to write query?
Thanks in advance...
You can achieve using this. As you wanted to print earnings and deductions as well so I used sub query.
select sum_earnings , sum_deduction , sum_earnings - sum_deduction
from ( select sum(case when ledgertype = 'Earning' then ledgervalue end) sum_earrnings, sum(case when ledgertype = 'Deductions' then ledgervalue end) as sum(sum_deduction)
from ratecard ) a
I am unable to understand "Both Values" but you can get Aggregate of both types by:
Select SUM(ledgerValue), ledgerType FROM ratecard group by ledgerType
SELECT (SUM_VAL - SUBSTRACT_VAL) as balance FROM
(
select sum(ledgerValue) AS SUM_VAL FROM ratecard WHERE ledgerType ='Earning',
select sum(ledgerValue) AS SUBSTRACT_VAL FROM ratecard WHERE ledgerType = 'substract'
) t1
If you want a running total you could use a variable to calculate.
DROP TABLE IF EXISTS T;
CREATE TABLE T (ID INT,AMOUNT INT, TYP VARCHAR(10));
INSERT INTO T VALUES
(1,12500,'Earnings'),(2,3200,'Earnings'),(3,1200,'Earnings'),
(4,1200,'Deductions'),(5,200,'Deductions'),(6,600,'Deductions'),(7,500,'Deductions'),
(8,12000,'Earnings'),(9,3200,'Deductions');
select t.*,
if(t.`typ` = 'Earnings' ,#rt:=#rt+amount,#rt:=#rt-amount) RunningTotal
from t
,(select #rt:=0) rt;
order by t.id
+------+--------+------------+--------------+
| ID | AMOUNT | TYP | RunningTotal |
+------+--------+------------+--------------+
| 1 | 12500 | Earnings | 12500 |
| 2 | 3200 | Earnings | 15700 |
| 3 | 1200 | Earnings | 16900 |
| 4 | 1200 | Deductions | 15700 |
| 5 | 200 | Deductions | 15500 |
| 6 | 600 | Deductions | 14900 |
| 7 | 500 | Deductions | 14400 |
| 8 | 12000 | Earnings | 26400 |
| 9 | 3200 | Deductions | 23200 |
+------+--------+------------+--------------+
9 rows in set (0.00 sec)

SQL select query with multiple conditions issue

I have a problem with a SQL select query, I can't figure out what it needs to be.
This is what my items table look like:
| id | i_id | last_seen | spot |
----------------------------------------------------
| 1 | ls100 | 2017-03-10 15:30:40 | spot800 |
| 2 | ls100 | 2017-03-10 16:20:15 | spot753 |
| 3 | ls200 | 2017-03-10 16:33:10 | spot800 |
| 4 | ls300 | 2017-03-10 15:30:40 | spot800 |
| 5 | ls300 | 2017-03-10 12:10:30 | spot800 |
| 6 | ls400 | 2017-03-10 10:30:10 | spot800 |
This is what I'm trying to obtain:
| id | i_id | last_seen | spot |
----------------------------------------------------
| 3 | ls200 | 2017-03-10 16:33:10 | spot800 |
| 5 | ls300 | 2017-03-10 12:10:30 | spot800 |
So I need to have the rows where spot= 'spot800', last_seen = MAX(but only if the DateTime is the newest compared to all spots with the samei_id`), and at last the DateTime must be bigger than '2017-03-10 11:00:00'.
This is what I have so far:
SELECT *
FROM items
WHERE spot = 'spot800'
HAVING MAX(`last_seen`)
AND `last_seen` > '2017-03-10 11:00:00'
E.g.:
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,i_id INT NOT NULL
,last_seen DATETIME NOT NULL
,spot INT NOT NULL
);
INSERT INTO my_table VALUES
(1,100,'2017-03-10 15:30:40',800),
(2,100,'2017-03-10 14:20:15',753),
(3,200,'2017-03-10 16:33:10',800),
(4,300,'2017-03-10 15:30:40',800),
(5,300,'2017-03-10 12:10:30',800),
(6,400,'2017-03-10 10:30:10',800);
SELECT [DISTINCT] x.*
FROM my_table x
LEFT
JOIN my_table y
ON y.i_id = x.i_id
AND y.last_seen < x.last_seen
WHERE x.last_seen > '2017-03-10 11:00:00'
AND x.spot = 800
AND y.id IS NULL;
----+------+---------------------+------+
| id | i_id | last_seen | spot |
+----+------+---------------------+------+
| 3 | 200 | 2017-03-10 16:33:10 | 800 |
| 5 | 300 | 2017-03-10 12:10:30 | 800 |
+----+------+---------------------+------+
2 rows in set (0.00 sec)
Use MAX and GROUP BY.
SELECT id, i_id, MAX(last_seen), spot
FROM items
WHERE spot = 'spot800'
AND last_seen > '2017-03-10 11:00:00'
GROUP BY id, i_id, spot
There is several things wrng with your statement.
Firstly, HAVING must be accompanied with a GROUP BY clause, so it's not what you are looking for.
Also, MAX is an aggregate, not a boolean, function. That is, it cannot be used in filters, such as a where clause or a having clause. Also, if it did work, MAX would only return the entry that contains the time as '2017-03-10 16:33:10'. Not what you expected.
Try this instead:
SELECT * FROM items WHERE (spot='spot800' AND last_seen > '2017-03-10 11:00:00');

MySQL date difference between column3 of first row and column2 of second row

I searched for the above topic and only getting query in Oracle which uses certain keywords specific to oracle.
+----------+------------+--------------------+
| Agent_id | valid_from | last_modified_date |
+----------+------------+--------------------+
| 13002 | 2010-12-25 | 2011-01-03 |
| 13002 | 2011-01-03 | 2011-08-25 |
| 13002 | 2011-08-26 | 2012-12-30 |
| 13002 | 2013-01-01 | 2013-01-01 |
| 12110 | 2014-02-27 | 2014-03-03 |
| 12110 | 2014-03-25 | 2014-12-25 |
+----------+------------+--------------------+
I have the above table values and want to retrieve difference between last_modified_date of 1st row and valid_from date of 2nd row and likewise for the same agent(agent id here).
Result table:
+----------+------------+--------------------+-----------+
| Agent_id | valid_from | last_modified_date | datediff |
+----------+------------+--------------------+-----------+
| 13002 | 2010-12-25 | 2011-01-03 | 0 |
| 13002 | 2011-01-03 | 2011-08-25 | 0 |
| 13002 | 2011-08-26 | 2012-12-30 | 1 |
| 13002 | 2013-01-01 | 2013-01-01 | 1 |
| 12110 | 2014-02-27 | 2014-03-03 | 0 |
| 12110 | 2014-03-25 | 2014-12-25 | 22 |
+----------+------------+--------------------+-----------+
If there is no date for comparison on first row diff should be 0.
These are set of dates where the status gets changed from Y to D and to find when the agent is without any activity.
please help!!
Use DATEDIFF function.
Example for MySQL:
SELECT
DATEDIFF(valid_from,last_modified_date) AS 'days'
FROM
table
Working example on SQLFiddle.
This will return difference in days. Source
Same for SQL Server 2005-2012:
SELECT
DATEDIFF(day,valid_from,last_modified_date ) AS 'days'
FROM
table
This will return difference in days. Source
Difference in days.
SELECT *,MIN(COALESCE(DATEDIFF(t1.valid_from, t2.last_modified_date),0))
FROM agents t1
LEFT JOIN agents t2 ON t1.agent_id=t2.agent_id AND t1.valid_from >= t2.last_modified_date
GROUP BY t1.agent_id, t1.valid_from
http://sqlfiddle.com/#!2/8b6ee/4
Try this query. Change the order by clause accordingly.
I have assumed the order based on your result.
select agent_id,
max(valid_from) as valid_from,max(last_modified_date) as last_modified_date,
ifnull(datediff(max(valid_from),max(last_modified_date)),0)as difference
from
(
select #a:=#a+1,
case when (#a+1)%2 = 0 then #b:=#a-2 else #b end as b , agent_id,
case when #a%2=0 then valid_from else 0 end as valid_from,
case when #a%2<>0 then last_modified_date else 0 end as last_modified_date
from table a ,(select #a:=0,#b:=0) b
order by agent_id desc ,valid_from
) a
group by agent_id,b
The easiest way to do this in MySQL is using variables:
select t.*
from (select t.*,
if(agent_id = #agent_id, datediff(valid_from, #last_modified_date), 0) as datediff,
#last_modified_date := last_modified_date,
#agent_id := agent_id
from table t cross join
(select #agent_id := 0, #last_modified_date := 0) const
order by agent_id, valid_from
) t;
You can also calculate the previous date using correlated subqueries.
By the way, those keywords that you would use in Oracle are not Oracle-specific. They are ANSI standard functionality that MySQL does not support.
just change the order of fields in result a little bit
mysqli_multi_query('
set #i='';
select
Agent_id, if(#i='', 0,datediff(valid_from,#i)) as datediff, valid_from, (#i:=last_modified_date) as last_modified_date
from
your_table'
);

query to fetch records and their rank in the DB

I have a table that holds usernames and results.
When a user insert his results to the DB, I want to execute a query that will return
the top X results ( with their rank in the db) and will also get that user result
and his rank in the DB.
the result should be like this:
1 playername 4500
2 otherplayer 4100
3 anotherone 3900
...
134 current player 140
I have tried a query with union, but then I didnt get the current player rank.
ideas anyone?
The DB is MYSQL.
10x alot and have agreat weekend :)
EDIT
This is what I have tried:
(select substr(first_name,1,10) as first_name, result
FROM top_scores ts
WHERE result_date >= NOW() - INTERVAL 1 DAY
LIMIT 10)
union
(select substr(first_name,1,10) as first_name, result
FROM top_scores ts
where first_name='XXX' and result=3030);
SET X = 0;
SELECT #X:=#X+1 AS rank, username, result
FROM myTable
ORDER BY result DESC
LIMIT 10;
Re your comment:
How about this:
SET X = 0;
SELECT ranked.*
FROM (
SELECT #X:=#X+1 AS rank, username, result
FROM myTable
ORDER BY result DESC
) AS ranked
WHERE ranked.rank <= 10 OR username = 'current';
Based on what I am reading here:
Your table structure is:
+--------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+-------------+------+-----+---------+-------+
| name | varchar(50) | YES | | NULL | |
| result | int(11) | YES | | NULL | |
+--------+-------------+------+-----+---------+-------+
Table Data looks like:
+---------+--------+
| name | result |
+---------+--------+
| Player1 | 4500 |
| Player2 | 4100 |
| Player3 | 3900 |
| Player4 | 3800 |
| Player5 | 3700 |
| Player6 | 3600 |
| Player7 | 3500 |
| Player8 | 3400 |
+---------+--------+
You want a result set to look like this:
+------+---------+--------+
| rank | name | result |
+------+---------+--------+
| 1 | Player1 | 4500 |
| 2 | Player2 | 4100 |
| 3 | Player3 | 3900 |
| 4 | Player4 | 3800 |
| 5 | Player5 | 3700 |
| 6 | Player6 | 3600 |
| 7 | Player7 | 3500 |
| 8 | Player8 | 3400 |
+------+---------+--------+
SQL:
set #rank = 0;
select
top_scores.*
from
(select ranks.* from (select #rank:=#rank+1 AS rank, name, result from ranks) ranks) top_scores
where
top_scores.rank <= 5
or (top_scores.result = 3400 and name ='Player8');
That will do what you want it to do
assuming your table has the following columns:
playername
score
calculated_rank
your query should look something like:
select calculated_rank,playername, score
from tablename
order by calculated_rank limit 5
I assume you have PRIMARY KEY on this table. If you don't, just create one. My table structure (because you didn't supply your own) is like this:
id INTEGER
result INTEGER
first_name VARCHAR
SQL query should be like that:
SELECT #i := #i+1 AS position, first_name, result FROM top_scores, (SELECT #i := 0) t ORDER BY result DESC LIMIT 10 UNION
SELECT (SELECT COUNT(id) FROM top_scores t2 WHERE t2.result > t1.result AND t2.id > t1.id) AS position, first_name, result FROM top_scores t1 WHERE id = LAST_INSERT_ID();
I added additional condition into subquery ("AND t2.id > t1.id") to prevent multiple people with same result having same position.
EDIT: If you have some login system, it would be better to save userid with result and get current user result using it.