i have a database with very much rows from a gps sender. The gps have 1 seconds delay to send next row to the database. So what i want to do is a web interface that shows travels, i dont want to show much rows, i want to group the rows to trips. So i want to do is a query who can declare a trip/travel by checking if its more then 14 minutes to next row, if it is then make a row of all rows before a give it a trip number, else add it to the "travel" collection.
Try this (example is at http://sqlfiddle.com/#!2/a0c86/39)
SELECT Trip, MIN(Date_Time), MAX(Date_Time)
FROM (
SELECT #Trip := IF(TIMESTAMPDIFF(MINUTE, #Date_Time, Date_Time) <= 20, #Trip, #Trip+1) AS TRIP
, logid
, #Date_Time := Date_time AS Date_Time
FROM gpslog
JOIN (SELECT #TRIP := 1, #Date_Time := null ) AS tmp
ORDER BY Date_Time) AS triplist
GROUP BY Trip
Related
Get only the biggest date:
These are check-in and check-out records of employees, some times they do twice or more entries on the system in a row. In this sample there were two check-out in a row. Assuming these rows always gonna be ordered, in the case of check-out I would like have the biggest date, and in the case of the check-in the smallest date.
In that case I would like to have this:
The smaller date was excluded:
DEMO
Try this, in this big CASE statement I increment column by one, if checkin switches from null to not null and the other way around. Then it's enough to group by this column taking max and min of checkout and checkin respectively:
select #checkinLag := null, #rn := 0;
select max(id),
functionario,
loja,
min(checkin),
max(checkout)
from (
select case when (checkinLag is null and checkin is not null) or
(checkinLag is not null and checkin is null)
then #rn := #rn + 1 else #rn end rn,
checkin,
checkout,
loja,
id,
functionario
from (
select #checkinLag checkinLag,
#checkinLag := checkin,
checkin,
checkout,
loja,
id,
functionario
from dummyTable
order by coalesce(checkin, checkout)
) a
) a group by functionario, loja, rn
I have used subqueries, to guarantee order of evaluating expressions (assigning and using of #checkinLag), as Gordon Linoff pointed.
Demo
My solution:
Select
*
from dummyTable base
where (base.checkout is null or not exists (
select
1
from dummyTable co
where co.checkout between base.checkout and DATE_ADD(base.checkout, INTERVAL 5 SECOND)
and base.id <> co.id
and base.functionario = co.functionario
and base.loja = co.loja
)) and (base.checkin is null or not exists (
select
1
from dummyTable ci
where ci.checkin between DATE_SUB(base.checkin, INTERVAL 5 SECOND) and base.checkin
and base.id <> ci.id
and base.functionario = ci.functionario
and base.loja = ci.loja
));
you can test the query here. There is no need that the rows are orderd. I choose 5 seconds as the interval where check-in/outs should be ignored.
I'm trying in MySql to count the number of users created each day and then get an accumulative figure on a row by row basis. I have followed other suggestions on here, but I cannot seem to get the accumulation to be correct.
The problem is that it keeps counting from the base number of 200 and not taking account of previous rows.
Where was I would expect it to return
My Sql is as follows;
SELECT day(created_at), count(*), (#something := #something+count(*)) as value
FROM myTable
CROSS JOIN (SELECT #something := 200) r
GROUP BY day(created_at);
To create the table and populate it you can use;
CREATE TABLE myTable (
id INT AUTO_INCREMENT,
created_at DATETIME,
PRIMARY KEY (id)
);
INSERT INTO myTable (created_at)
VALUES ('2018-04-01'),
('2018-04-01'),
('2018-04-01'),
('2018-04-01'),
('2018-04-02'),
('2018-04-02'),
('2018-04-02'),
('2018-04-03'),
('2018-04-03');
You can view this on SqlFiddle.
Use a subquery:
SELECT day, cnt, (#s := #s + cnt)
FROM (SELECT day(created_at) as day, count(*) as cnt
FROM myTable
GROUP BY day(created_at)
) d CROSS JOIN
(SELECT #s := 0) r;
GROUP BY and variables have not worked together for a long time. In more recent versions, ORDER BY also needs a subquery.
I have a SQL table, one row is the revenue in the specific day, and I want to add a new column in the table, the value is the incremental (could be positive or negative) revenue between a specific day and the previous day, and wondering how to implement by SQL?
Here is an example,
original table,
...
Day1 100
Day2 200
Day3 150
...
new table (add incremental column at the end, and for first column, could assign zero),
Day1 100 0
Day2 200 100
Day3 150 -50
I am using MySQL/MySQL Workbench.
thanks in advance,
Lin
SELECT a.day, a.revenue , a.revenue-COALESCE(b.revenue,0) as previous_day_rev
FROM DailyRevenue a
LEFT JOIN DailyRevenue b on a.day=b.day-1
the query assume that each day has one record in the table. If there could be more than 1 row for each day you need to create a view that sums up all days grouping by day.
If you're okay with re-ordering the columns slightly, something like this is pretty simple to understand:
SET #prev := 0;
SELECT day, revenue - #prev AS diff, #prev := revenue AS revenue
FROM revenue ORDER BY day ASC;
The trick is that we calculate the difference to the previous first, then set the previous to the current and display it as the current in one step.
Note, this depends on the order being correct since the calculations are done during the returning of the rows, so you need to make sure you have an ORDER BY clause that returns the days in the correct order.
Try;
select
t.date_col, t.val_col,
case when t1.val_col is null then 0
else t.val_col - t1.val_col end diff
from (
select t.* , #r := #r + 1 lev
from tbl t,
(select #r := 0) r
order by t.date_col
) t
left join (
select t.* , #r1 := #r1 + 1 lev
from tbl t,
(select #r1 := 1) r
order by t.date_col
) t1
on t.lev = t1.lev
This will calculate value diff even if there is a missing date
I have a table "channel_001" with timestamp column Time, and i did separate it by 10 minutes.
2013-01-01;00:10:04;
2013-01-01;00:20:00;
2013-01-01;00:30:02;
2013-01-01;00:40:04;
But there are missing datas. How can i detect a missing row? And then insert a row there?!
For example:
2013-01-01;00:10:04;
2013-01-01;00:20:00;
2013-01-01;00:30:02
2013-01-01;00:40:04;
2013-01-01;01:00:02;
then it would be missing:
2013-01-01;00:50:00;
I was thinking of using Join the table to itself, but im new in SQL and too much of a novice to finde the answere alone.
Any ideas?
You can find rows that don't have a "next" time with something like:
select c.*
from channel_001 c
where not exists (select 1
from channel_001 c2
where c2.timestamp > c.timestamp + interval 9 minute and
c2.timestamp < c.timestamp + interval 11 minute
);
If your table is large (tens of thousands of rows), you will probably want to use variables. The following code gets the previous timestamp:
select c.*,
(case when (#tmp := #prevts) is null then null
when (#prevts := timestamp) is null then null
else #tmp
end) as prev_timestamp
from channel_001 c cross join
(select #prevts := 0, #tmp := 0) vars
order by timestamp;
You can use this as a subquery to get gaps that are outside your range.
How can I get the date for the latest value change in one column with one SQL query?
Possible database situation:
Date State
2012-11-25 state one
2012-11-26 state one
2012-11-27 state two
2012-11-28 state two
2012-11-29 state one
2012-11-30 state one
So result should return 2012-11-29 as latest change state. If I group by State value, I will get the date for first time I have that state in database.
The query will group the table on state and show the state and in the date field the latest date created of that state.
From the given input the output would be
Date State
2012-11-30 state one
2012-11-28 state two
This will get you the last state:
-- Query 1
SELECT state
FROM tableX
ORDER BY date DESC
LIMIT 1 ;
Encapsulating the above, we can use it to get the date just before the last change:
-- Query 2
SELECT t.date
FROM tableX AS t
JOIN
( SELECT state
FROM tableX
ORDER BY date DESC
LIMIT 1
) AS last
ON last.state <> t.state
ORDER BY t.date DESC
LIMIT 1 ;
And then use that to find the date (or the whole row) where the last change occurred:
-- Query 3
SELECT a.date -- can also be used: a.*
FROM tableX AS a
JOIN
( SELECT t.date
FROM tableX AS t
JOIN
( SELECT state
FROM tableX
ORDER BY date DESC
LIMIT 1
) AS last
ON last.state <> t.state
ORDER BY t.date DESC
LIMIT 1
) AS b
ON a.date > b.date
ORDER BY a.date
LIMIT 1 ;
Tested in SQL-Fiddle
And a solution that uses MySQL variables:
-- Query 4
SELECT date
FROM
( SELECT t.date
, #r := (#s <> state) AS result
, #s := state AS prev_state
FROM tableX AS t
CROSS JOIN
( SELECT #r := 0, #s := ''
) AS dummy
ORDER BY t.date ASC
) AS tmp
WHERE result = 1
ORDER BY date DESC
LIMIT 1 ;
I believe this is the answer:
SELECT
DISTINCT State AS State, `Date`
FROM
Table_1 t1
WHERE t1.`Date`=(SELECT MAX(`Date`) FROM Table_1 WHERE State=t1.State)
...and the test:
http://sqlfiddle.com/#!2/8b0d8/5
If you add another column 'changed datetime' you can fill this using an update trigger that inserts NOW(). If you query your table ordering on the changed column, it will endup first.
CREATE TRIGGER `trigger` BEFORE UPDATE ON `table`
FOR EACH ROW
BEGIN
SET ROW.changed = NOW();
END$$
Try this ::
Select
MAX(`Date`), state from mytable
group by state
If you had been using postgres, you could compare different rows in the same table using "LEAD .. OVER" I have not managed to find the same functionallity in mysql.
A bit hairy, but I think this will do:
select min(t1.date) from table_1 t1 where
(select count(distinct state) from table_1 where table_1.date>=t1.date)=1
Basically, this asks for the first time no changes in state is found for any later values. Be warned, it may be this query scales terribly for large data sets....
I think your best choice here are analytical functions. Try this - it should be OK performance-wise:
SELECT *
FROM test
WHERE my_date = (SELECT MAX (my_date)
FROM (SELECT MY_DATE
FROM ( SELECT MY_DATE,
STATE,
LAG (state) OVER (ORDER BY MY_DATE)
lag_val
FROM test
ORDER BY MY_DATE) a
WHERE state != lag_val))
In the inner select, the LAG function gets the previous value in the STATE column and in the outer select I mark the date of a change - those with lag value different than the current state value. And outside, I'm getting the latest date from those dates of a change... I hope that this is what you needed.
SELECT MAX(DATE) FROM YOUR_TABLE
Above answer doesn't seem to satisfy what OP needs.
UPDATED ANSWER WITH AFTER INSERT/UPDATE TRIGGER
DELCARE #latestState varchar;
DELCARE #latestDate date;
CREATE TRIGGER latestInsertTrigger AFTER INSERT ON myTable
FOR EACH ROW
BEGIN
IF OLD.DATE <> NEW.date THEN
SET #latestState = NEW.state
SET #latestDate = NEW.date
END IF
END
;
CREATE TRIGGER latestUpdateTrigger AFTER UPDATE ON myTable
FOR EACH ROW
BEGIN
IF OLD.DATE = NEW.date AND OLD.STATE <> NEW.STATE THEN
SET #latestState = NEW.state
SET #latestDate = NEW.date
END IF
END
;
You may use the following query to get the latest record added/updated:
SELECT DATE, STATE FROM myTable
WHERE STATE = #latestState
OR DATE = #latestDate
ORDER BY DATE DESC
;
Results:
DATE STATE
November, 30 2012 00:00:00+0000 state one
November, 28 2012 00:00:00+0000 state two
November, 27 2012 00:00:00+0000 state two
The above query results needs to be limitted to 2, 3 or n based on what you need.
Frankly it seems like you want to get max from both columns based on the data sample you have given. Assuming that your state only increases with the date. Only I wish if the state was an integer :D
Then union of two max sub queries on both columns would have solved it easily. Still a string manipulation regex can find what's the max in state column. Finally this approach needs limit x. However it still has lope hole. Anyway it took me sometime to figure your need out :$