String handling in MySQL - calculate days since date - mysql

I have a table that looks like this:
table
---------------------------------------
id last_update
---------------------------------------
1 2020-02-22T00:32:04.254975Z
2 2020-02-22T02:09:27.057131Z
3 2020-02-22T01:38:48.739303Z
4 2020-02-21T06:19:17.832257Z
5 2020-02-14T03:10:02.551126Z
6 2020-02-21T23:17:01.907037Z
id is INT and last_update is VARCHAR (because the format is not supported as a DATE TIME type in MySQL).
What I want to do is to create a new column called "days_since_last_update" that calculates the number of days between today's date and the "last_updated" date.
How can this be done in a MySQL query?
Thanks, x

You should be storing your timestamps in a proper datetime column. That being said, we can try working around the text dates using STR_TO_DATE. I would actually suggest not adding a new column, since your ask is just for derived data. Instead, generate this column when you query:
SELECT
id,
last_update,
DATEDIFF(STR_TO_DATE(last_update, '%Y-%m-%dT%H:%i:%s'),
(SELECT STR_TO_DATE(t2.last_update, '%Y-%m-%dT%H:%i:%s')
FROM yourTable t2
WHERE t2.id < t1.id ORDER BY t2.id DESC)) AS days_since_last_update
FROM yourTable t1
ORDER BY t1.id;
This would place the first days since last update value as NULL, as there is no earlier update recorded for the table. Though, if you want a default value, that can easily be included.
If you instead want the difference in days between the last_update column and today's date, then use:
SELECT
id,
last_update,
DATEDIFF(CURDATE(),
STR_TO_DATE(last_update, '%Y-%m-%dT%H:%i:%s')) AS days_since_last_update
FROM yourTable
ORDER BY id;

Related

Store calendar week and year in one (date) column in SQL

My table includes two columns: calendar week and year.
If I want to get the latest entries by calendar week and year, I currently perform:
SELECT * FROM table WHERE calyear = (SELECT MAX(calyear) FROM table) AND calweek = (SELECT MAX(calweek) FROM table WHERE calyear = (SELECT MAX(calyear) FROM table))
which is super long. I'd like to replace this with a combination of week and year e.g. 'calweek-calyear' column. Is there a date format for that or should I save this as a tiny text?
I want to be able to perform MAX() on it and performance shouldn't suffer singificantly.
Im open for better solutions, thanks.
Your super long query can be simplified to:
SELECT *
FROM tablename
ORDER BY calyear DESC, calweek DESC
LIMIT 1;
if you expect only 1 row as a result.
If there are more than 1 rows for the max calyear and calweek combination, you could use RANK() window function:
SELECT t.*
FROM (
SELECT *, RANK() OVER (ORDER BY calyear DESC, calweek DESC) rnk
FROM tablename
) t
WHERE t.rnk = 1;
Also, I would advice against the use of a combination of year and week.
Keep your data as simple as possible.
For presentation purposes you could easily concatenate the 2 columns.
If you concatenate YYYYWW in a column TINYTEXT, or other text type I think it will do what you want.
If you make sure that your week numbers are 2 digit ie 01 and not 1 you could use INT.
I would rather advise the use of a column DATE and a modified query.

MySQL Matching date-based First Instance of value

I have a table containing stock market data (open, hi, lo, close prices) but in a random order of date:
Date Open Hi Lo Close
12/10/2019 313.82 314.54 312.81 313.58
11/22/2019 311.09 311.24 309.85 310.96
11/25/2019 311.98 313.37 311.98 313.37
11/26/2019 313.41 314.28 313.06 314.08
11/27/2019 314.61 315.48 314.37 315.48
11/29/2019 314.86 315.13 314.06 314.31
12/2/2019 314.59 314.66 311.17 311.64
12/3/2019 308.65 309.64 307.13 309.55
I have another value in a PHP variable (say $BaseValue),and a start date and end date ($startdt and $enddt).
1) My requirement is to pick-up the value from the HI column, if it exceeds the $BaseValue on the very FIRST date in a chronological order between the given start and end dates.
For example, if the $BaseValue=314, startdt=11/22, enddt=12/2, then I want to retrieve the Date (11/26/19) as it is the earliest date on which the Hi value (314.28) exceeded the $Basevalue within the given date range. The select statement should return both the Hi value (314.28) and the Date (11/26/19).
2) Additionally, I also need to retrieve the HIGHEST value and date from the HI column during the given date duration. In the above scenario, it should return 315.48 and corresponding date 11/27.
The table is NOT in a chronological order - its randomly filled.
I am unable to get the first query at all with the use of MAX function and its various combinations. Makes me wonder if that is possible at all in SQL or not.
While the second is straightforward, I was wondering if it is more efficient and less complex to club the two queries and get the four values in one single shot.
Any ideas on how can I approach the need to fulfill this requirement please?
Thanks
You could use two subqueries for filtering, one per criteria, like:
select t.*
from mytable t
where
t.date = (
select min(t1.date)
from mytable t1
where t1.date between :datedt and :enddt and t1.hi >= :basevalue
)
or t.hi = (
select max(t1.hi)
from mytable t1
where t1.date between datedt and :enddt and t1.hi >= :basevalue
)
Another option is to union two queries with orer by and limit:
(
select t.*
from mytable
where t.date between :datedt and :enddt and t1.hi >= :basevalue
order by t.date
limit 1
)
union
(
select t.*
from mytable t
where t.date between :datedt and :enddt and t1.hi >= :basevalue
order by t.hi desc, t.date
limit 1
)
Please note that both queries do not do exactly the same thing. If there are ties for the highest hi in the period, the first query will return all ties, while the second will pick the earliest one. It's up to you to decide which solution better fits your use case.

How to filter SQL table relative to a certain datetime?

I'm trying to write a SQL command that would recreate report data from a certain date. The table has three columns (location (varchar), value (int), datetime (datetime)). Over time, rows are added to the table to provide updated values for each location.
What SQL should I be using to:
For each location, return only the row with the most recent datetime before a given datetime.
Example:
Location Value Datetime
A 5 2011-01-01
B 6 2011-01-01
A 7 2012-01-01
A 8 2012-06-01
If I'm curious about 2012-05-01, I would like to get back rows 2 and 3.
What I'm thinking is to GROUP BY location, and specify to keep the largest datetime that is less than the given datetime. I'm assuming there is some built in functionality with datetime objects in SQL that I should be using.
The straight-forward way: Get the maximum date per location, then select the complete record:
select *
from mytable
where (location, datetime) in
(
select location, max(datetime)
from mytable
where datetime < date '2012-05-01'
group by location
);
In MySQL 8.x you can use the ROW_NUMBER() window function. For example:
with x as (
select
location, value, datetime,
row_number() over (partition by location order by datetime desc) as rn
from my_table
where datetime < '2012-05-01' -- cutoff date
)
select * from x where rn = 1

Selecting the row which has the latest datetime for a specific year

Say that there is a MySQL table which has a column with data type datetime and there are many rows with year 2015,2016,2017... at this column.
The problem is that i want to write a query that finds the row with highest(newest) datetime column for a specific year. For example i want the query to find the row of highest datetime value for 2015. (I assumed that 31 December is higher than 30 December).
You can do:
select t.*
from t
where t.datetimecol = (select max(t2.datetimecol)
from t t2
where year(t2.datetimecol) = year(t.datetimecol)
);
SELECT MAX(mydate)
FROM mytable
WHERE YEAR(mydate) = 2017
or
SELECT YEAR(mydate), MAX(mydate)
FROM mytable
GROUP BY YEAR(mydate)

MySQL: Need to Query for missing records from various start dates

I have two tables that are linked by an ID, and one table has a start date, and the child (linked) table has weekly entries of data. I need to be able to query and determine the ID's, that are missing a week's data, without knowing the actual dates.
Table1
ID INT
START_DATE DATE
Table2
ID INT (foreign Key to Table 1)
TRAN_DATE DATE
VALUE INT
Each INT might have a different start date, and the values are saved weekly (every Monday, Tuesday, etc... based on Start Date)
Some IDs will have missed posting their value one week, and I need to look back historically for when a record is missing.
Assuming a Start_Date of Sept 9, 2013, the dates would be (9/9/2013. 9/16/2013, 9/23/2013,...) I need to see if TRAN_DATE for ID 1 is 9/9/2013, then add 7 days (9/16/2013), and check for that record, then add 7 days (9/23/2013) and check for that record to exist. Then repeat for the different IDs. This would end with the current date, or any date into the future (if this is easier).
I can do this with a program simply enough, but I need to do this at a customer site and I can not distribute code into the site, so I need to try to do it with a query).
The following query returns any gaps in table2:
select distinct id
from table2 t2
where t2.tran_date < now() - interval 7 day and
not exists (select 1
from table2 t2a
where t2a.id = t2.id and
datediff(t2a.tran_date, t2.tran_date) = 7
);
This assumes that the first transaction is not missing. Is that possible?