Calculate running day difference in MySQL - mysql

I'd like tot calculate the difference in days from the current row, compared to the previous row. What I have now:
Here is my column is day difference
**Day_Diff**
351
363
363
But what I actually want:
**Day_Diff**
351
12
12
What query would I need to accomplish this?

This should do the work (if what you want is minimum in first row and then difference to the minimum, with table being the name of your table and day_diff the name of the column you named Day_diff):
See sqlfiddle :
SELECT
CASE WHEN t1.day_diff = t2.min_day_diff
THEN t1.day_diff
ELSE t1.day_diff - t2.min_day_diff
END
FROM mytable t1
LEFT JOIN (SELECT MIN(day_diff) AS min_day_diff FROM mytable) t2
ON True;

Related

SQL join each row in a table with a one row from another table

The Problem
I have a table window with start and end timestamps. I have another table activity that has a timestamp. I would like to create a query that:
For each row in activity it joins with a single row from window, where the timestamp occurs between start and end, choosing the older window.
Window Table
Start
End
ISBN
0
10
"ABC"
5
15
"ABC"
20
30
"ABC"
25
35
"ABC"
Activity Table
Timestamp
ISBN
7.5
"ABC"
27.5
"ABC"
Desired Result
Start
End
ISBN
Timestamp
0
10
"ABC"
7.5
20
30
"ABC"
27.5
The Attempt
My attempt at solving this so far has ended with the following query:
SELECT
*
FROM
test.activity AS a
JOIN test.`window` AS w ON w.isbn = (
SELECT
w1.isbn
FROM
test.window as w1
WHERE a.`timestamp` BETWEEN w1.`start` AND w1.`end`
ORDER BY w1.`start`
LIMIT 1
)
The output of this query is 8 rows.
When there is guaranteed to be a single oldest window (i.e. no two Start times are the same for any ISBN)
with activity_window as (
select
a.`Timestamp`,
a.`ISBN`,
w.`Start`,
w.`End`,
row_number() over (partition by a.`ISBN`, a.`Timestamp` order by w.`Start`) rn
from
`Activity` a
inner join `Window` w on a.`ISBN` = w.`ISBN` and a.`Timestamp` between w.`Start` and w.`End`
)
select `Start`, `End`, `ISBN`, `Timestamp` from activity_window where rn = 1;
Result:
Start
End
ISBN
Timestamp
0
10
ABC
7.5
20
30
ABC
27.5
(see complete example at DB<>Fiddle)
CTEs are available from MySQL 8.0. Use subqueries when you are still on MySQL 5. Try to avoid table- and column names that are reserved words in SQL (things like Window, Start, End or Timestamp are examples for bad name choices).
Keeping an index over (ISBN, Start, End) on Window (or clustering the entire table that way by defining those three columns as the primary key) helps this query.

How to get distinct records but on base on first 2 digits only in MySQL

I want to get distinct records (only one field) from a MySQL table, and that field contain only digits.
Example:
00010000
01111100
01112000
01118000
02301201
But distinct records is considered on base on first 2 digits. So in the case above, I need to get back only 3 records:
00010000
01112000
02301201
More over, I would like to trim the rest of the digits, so the actual end result should be:
00
01
02
So distinct and group by will not cut here. Any idea?
Assuming you wanted the least value from among duplicates, you could try:
SELECT t1.col
FROM yourTable t1
INNER JOIN
(
SELECT LEFT(col, 2) AS prefix, MIN(col) AS min_col
FROM yourTable
GROUP BY LEFT(col, 2)
) t2
ON LEFT(t1.col, 2) = t2.prefix AND
t1.col = t2.min_col;
Note: Numbers in MySQL don't start with zeroes, so your requirement (and this answer) only make sense if your column is text.
DISTINCT will work fine with LEFT to give the results you want:
SELECT DISTINCT(LEFT(value, 2)) AS value
FROM data
ORDER BY value
Output
00
01
02
Demo on dbfiddle
If you only want the trimmed value, try this:
SELECT SUBSTRING(yourColumn,1,2) as trimmedval
FROM Table
GROUP BY SUBSTRING(yourColumn,1,2)

How to retrieve Closest date from another table dynamically MySQL

I have two tables:
INFO table
ID LockDate Investor
157 10/15/2018 TEST1
VF1 09/02/2018 TEST2
LO2 05/01/2018 TEST3
09K 03/03/2012 TEST4
098 05/01/2012 TEST5
099 09/09/2012 TEST6
2YT 08/25/2012 TEST7
NUMBERS table
Dates Amount
10/10/2018 25.10
08/31/2018 200.15
05/10/2018 15.251
03/03/2012 10.10
05/10/2012 15.251
08/31/2012 548.0
I want the query to select all the values in the INFO table and find the closest or equal date in the Numbers table and give me the Amount. So my results would be:
ID LockDate Investor Amount
157 10/15/2018 TEST1 25.10
VF1 09/02/2018 TEST2 200.15
LO2 05/01/2018 TEST3 15.251
09K 03/03/2012 TEST4 10.10
098 05/01/2012 TEST5 15.251
099 09/09/2012 TEST6 548.0
2YT 08/25/2012 TEST7 548.0
By closest I mean equal or closest one whether it is lesser or greater than lock date.
This is the query I'm using but it is just retrieving the greater or equal one which doesn't work at all for me since I have to do it dynamically...
SELECT I.* ,
N.Amount FROM
(
SELECT A.*, MIN(NUM.Dates) AS XDATE
FROM INFO A
INNER JOIN NUMBERS AS NUM
ON NUM.Dates >= A.LockDate
GROUP BY A.ID
)AS RES
INNER JOIN NUMBERS AS N
ON N.Dates = I.XDATE
I will appreciate any help.
You just need to find the absolute minimum value for LockDate minus Dates. This will give you the closest date; lesser or greater. Rest is easy.
SELECT info.*, numbers.*
FROM info
INNER JOIN (
SELECT ID, MIN(DATEDIFF(GREATEST(LockDate, Dates), LEAST(LockDate, Dates))) Delta
FROM info
CROSS JOIN numbers
GROUP BY ID
) g ON info.ID = g.ID
INNER JOIN numbers ON DATEDIFF(GREATEST(LockDate, Dates), LEAST(LockDate, Dates)) = g.Delta
SQL Fiddle
Not sure what is your definition of "closest".
Here is my approach to get dates<=LockDate
http://sqlfiddle.com/#!9/8eea46/8
SELECT i.*, n.amount
FROM info i
LEFT JOIN numbers n
ON i.LockDate >= n.dates
LEFT JOIN numbers n_all
ON i.LockDate >= n_all.dates
AND n_all.dates > n.dates
WHERE n_all.dates IS NULL
Note: expected result is different since the definition of "closest" kind changed.
PS
Q: Why do I think that is a good approach?
A: Because whenever we deal with the data related to the timeline usually we expect data to know what (state, events, results) was before the moment we have on the timeline but not what will happen in future. That mean 31/12/2017 line/record can have/collect data from any table/records before 31/12/2017 but none from 2018. This strategy helps to keep reports consistent. They less dependent on the date when we generate the report. if we generate report about Dec 2017 at 1st Jan 2018 it will output same result as if we generate same report on same period Dec 2017 but week or month later 10th Jan 2018 or 1st of Feb.

Mysql join query failure

For the tables below I need to output the rows that are not in the member_booking table from master_vehicle_inventory table.
I also need the results from master_vehicle_inventory which don't fall between the range of mb_startdate and mb_returndate.
master_vehicle_inventory table
mvi_id
1
2
3
4
member_booking table
mb_id mb_startdate mb_returndate mvi_id
100 22-04-2016 30-04-2016 2
101 23-01-2016 02-05-2016 3
So far I have tried this:
SELECT
mb.mb_id,
mb.mb_startdate,
mb.mb_returndate,
mvi.mvi_id
FROM master_vehicle_inventory AS mvi LEFT JOIN LEFT
JOIN member_booking AS mb ON mvi.mvi_id = mb.mvi_id
WHERE CURDATE() NOT BETWEEN mb.mb_startdate
AND mb.mb_returndate
AND mvi.mvi_id NOT IN (SELECT mvi.mvi_id
FROM member_booking)
But it doesn't give the results want.
To return rows from `master_vehicle_inventory` which do not have a "matching" row in `member_booking` table, we can use an anti-join pattern.
Assuming \mb_startdate` and `mb_enddate` are defined as datatype DATE.
Something like this:
SELECT mvi.mvi_id
FROM master_vehicle_inventory mvi
LEFT
JOIN member_booking mb
ON mb.mvi_id = mvi.mvi_id
AND mb.mb_startdate <= DATE(NOW())
AND mb.mb_enddate > DATE(NOW())
WHERE mb.mvi_id IS NULL
(The specification for a "matching" row is a bit vague.)
Given DATE(NOW()) returns '2016-04-25' we expect that query to return rows
mvi_id
------
1
4
(We are suspicious that the datatype of the columns may not be DATE because the values shown in the example data are not in the YYYY-MM-DD format we expect returned for a DATE expression.)

Mysql single column result to multiple column result

I have a problem with a MySQL query, the problem is I have the following table:
id, rep, val dates
1 rep1 200 06/01/2014
2 rep2 300 06/01/2014
3 rep3 400 06/01/2014
4 rep4 500 06/01/2014
5 rep5 100 06/01/2014
6 rep1 200 02/06/2014
7 rep2 300 02/06/2014
8 rep3 900 02/06/2014
9 rep4 700 02/06/2014
10 rep5 600 02/06/2014
and I want a result like this:
rep 01/06/2014 02/06/2014
rep1 200 200
rep2 300 300
rep3 400 900
rep4 500 700
rep5 100 600
thank you very much!
You seem to want the most recent row for each rep. Here is an approach that often performs well:
select t.*
from table t
where not exists (select 1
from table t2
where t2.repid = t.repid and
t2.id > t.id
);
This transforms the problem to: "Get me the rows in table t where there is no other row with the same repid and a larger id." That is the same logic as getting the last one, just convoluted a bit to help the database know what to do.
For performance reasons, an index on t(repid, id) is helpful.
You seem to want the val for each of the dates.
Assuming the dates you are interested in are fixed then you can do that as follows. For output date column you check of the row matches the date for that column. If so you use the value of val , if not you just use 0. Then you sum all the resulting values, grouping by rep. I have assumed a fixed format of date.
SELECT rep, SUM(IF(dates='2014/06/01'), val, 0) AS '2014/06/01', SUM(IF(dates='2014/06/02'), val, 0) AS '2014/06/02'
FROM sometable
GROUP BY rep
Or if you just wanted the highest val for each day
SELECT rep, MAX(IF(dates='2014/06/01'), val, 0) AS '2014/06/01', MAX(IF(dates='2014/06/02'), val, 0) AS '2014/06/02'
FROM sometable
GROUP BY rep
If the number of dates is variable then not really a direct way to do it (as the number of resulting columns would vary). It would be easiest to do this manly in your calling script based on the following, giving you one row per rep / possible date with a sum of the values of val for that rep / date combination:-
SELECT rep, sub0.dates, SUM(IF(sometable.dates=sub0.dates), val, 0)
FROM sometable
CROSS JOIN
(
SELECT DISTINCT dates
FROM sometable
) sub0
GROUP BY rep, sub0.dates