Date compares in Rails SQL - mysql

I have this query in Rails (mysql database).
planSubscriptions = PlanSubscription.find_by_sql(["SELECT ps.*
FROM plan_subscriptions ps, plans p, states s
WHERE p.name = 'Unlimitted'
AND s.name = 'Confirmed'
AND p.id = ps.plan_id
AND s.id = ps.state_id
AND date_format(ps.updated_at + INTERVAL 1 MONTH,'%Y-%m-%d') <= date_format(NOW(),'%Y-%m-%d')"])
It´s not working because malformed format string - %Y
I need to compare only the date (not the times). What is the right syntax?

MySQL is perfectly capable of comparing date values directly:
... (DATE(ps.updated_at + INTERVAL 1 MONTH) < curdate())

Related

mysql max condition use in where clause

SELECT MAX(r.endTime) as maxETime,r.vehicleId,v.emixis_id
FROM reservation r , vehicle v
WHERE r.vehicleId = v.vehicleId AND
r.workingDay = CURRENT_DATE() AND
r.isNote=0 AND
r.isDeleted=0 AND
r.status NOT IN("cancelled") AND
DATE_FORMAT(NOW(),'%H:%i:%s') > DATE_SUB(MAX(r.endTime), INTERVAL 1 HOUR)
GROUP BY r.vehicleId;
This is my code, what want to do is get MAX(r.endTime) value in to DATE_SUB. MAX not working with where condition, is there way to do this kind of thing ? I tried with #var:=MAX(r.endTime) and set it inside DATE_SUB, but #var always store last row's value.
SELECT MAX(r.`endTime`) as `maxETime`,r.`vehicleId`,v.`emixis_id`
FROM `reservation` r , `vehicle` v
WHERE r.`vehicleId` = v.`vehicleId` AND
r.`workingDay` = CURRENT_DATE() AND
r.`isNote`=0 AND
r.`isDeleted`=0 AND
r.`status` NOT IN("cancelled")
GROUP BY r.`vehicleId`
HAVING DATE_FORMAT(NOW(),'%H:%i:%s') > DATE_ADD(MAX(r.`endTime`), INTERVAL 1 HOUR)

Add months in db date using interval in mysql

I want to add month in transaction date using mysql interval function by join plan table and transaction table,however this method not working but If I add months in static way to transaction date it is working.
plan table:
plan_id plan
1 6 month
2 12 month
3 3 month
transaction table:
id user_id subscribed_on plan_id
1 2 2020-04-04 1
2 4 2019-02-22 2
Mysql query (not working):
SELECT t.* FROM transaction t inner join plan p on p.plan_id=t.plan_id
where t.user_id=2 and DATE_ADD(date(t.subscribed_on), INTERVAL p.plan) >= CURDATE()
order by t.id desc
If I add month in static way than it is working fine:
SELECT t.* FROM transaction t inner join plan p on p.plan_id=t.plan_id
where t.user_id=2 and DATE_ADD(date(t.subscribed_on),
INTERVAL 6 month) >= CURDATE()
order by t.id desc
MySQL does not support using interval that way. Unlike in other databaes (such as Postgres for example), the unit argument is a keyword, not a literal string.
I would suspect that your table may store other intervals than just months (say, years, days, and so on). If so, you can use string functions and a case expression to accommodate the different possible values, like:
select t.*
from transaction t
inner join plan p on p.plan_id = t.plan_id
where
t.user_id = 2
and date(t.subscribed_on) + case substring_index(p.plan, ' ', -1)
when 'year' then interval substring_index(p.plan, ' ', 1) year
when 'month' then interval substring_index(p.plan, ' ', 1) month
when 'day' then interval substring_index(p.plan, ' ', 1) day
end
>= current_date
order by t.id desc
The logic here is to split the stored interval string into two parts: the number, and the unit; the case expression processes the unit and generate the proper literal interval accordingly.
Unfortunately a string in the data is not equivalent to an interval. One method is:
date(t.subscribed_on) + interval substring_index(plan, ' ') + 0 month
Note here that month is a keyword, not a string.
Try to force the plan column in the plan table to be an integer. Does not seem to be possible to cast a string to an interval.
I tried like so:
WITH
plan( plan_id,plan) AS (
SELECT 1,'6 month'
UNION ALL SELECT 2,'12 month'
UNION ALL SELECT 3,'3 month'
)
,
transaction(id,user_id,subscribed_on,plan_id) AS (
SELECT 1,2,DATE '2020-09-04',1
UNION ALL SELECT 2,4,DATE '2019-02-22',2
)
SELECT t.*
FROM transaction t
INNER JOIN plan p ON p.plan_id = t.plan_id
WHERE t.user_id = 2
AND DATE_ADD(
DATE(t.subscribed_on)
, INTERVAL CAST(REPLACE(plan,' month','') AS SIGNED) MONTH
) >= CURDATE()
ORDER BY t.id DESC
(returns no results, as you don't have any dates high enough in your example data...)

Select depending CURDATE MySQL

I have the following query:
SELECT tbl_usuarios.Correo,
tbl_alertas.Id,
tbl_alertas.Purpose,
tbl_alertas.Status,
tbl_alertas.OpenDate,
tbl_alertas.CloseDate,
tbl_alertas.Owner,
tbl_alertas.ValueStream,
tbl_alertas.Family
FROM tbl_usuarios
INNER JOIN tbl_alertas ON tbl_usuarios.Nombre = tbl_alertas.Owner
WHERE (STATUS = 'En aprobacion' OR STATUS = 'Activa')
AND CloseDate < CURDATE()
As a result of this query I have the following table:
Everything it's ok.
Now, the problem is that I 'm going to use that query in my website, I create a JavaScript function which is going to be executed every 5th and every 19th of a month. My question, what can I do to only select the row which his CloseDate is <= yyyy-mm-07 or CloseDate >= yyyy-mm-07 and <= yyyy-mm-21 depending of CURDATE(5th or 19th), example:
Curdate = yyyy-mm-05.
Expected result: Data from Id 00003
What I tried was adding another AND to the query, something like this:
AND CloseDate < CURDATE() AND CloseDate <= '2016-09-07'
If my question is not much clear let me explain with this, I want to select CloseDate '2016-09-07' when curdate = '2016-whatever-05, 06', 07 or select CloseDate '2016-09-21' where curdate = '2016-whatever-19, 20, 21'.
Don't know if I understood your problem correctly but check the DAYOFMONTH() or simply DAY() function.
Returns the day of the month for date, in the range 1 to 31, or 0 for
dates such as '0000-00-00' or '2008-00-00' that have a zero day part.
You can use it like this
SELECT tbl_usuarios.Correo,
tbl_alertas.Id, tbl_alertas.Purpose,
tbl_alertas.Status, tbl_alertas.OpenDate,
tbl_alertas.CloseDate,
tbl_alertas.Owner,
tbl_alertas.ValueStream,
tbl_alertas.Family
FROM tbl_usuarios
INNER JOIN tbl_alertas
ON tbl_usuarios.Nombre = tbl_alertas.Owner
WHERE (STATUS = 'En aprobacion' OR STATUS = 'Activa')
AND (CASE
WHEN DAY(CURDATE()) = '5' AND DATE(CloseDate) <= DATE_FORMAT(NOW() ,'%Y-%m-07')
THEN 1
WHEN DAY(CURDATE()) = '19' AND (DATE(CloseDate) >= DATE_FORMAT(NOW() ,'%Y-%m-07') AND DATE(CloseDate) <= DATE_FORMAT(NOW() ,'%Y-%m-21'))
THEN 1
ELSE 0
END)

SQL query union best practice

I am trying to classify data as I extract it from a table. the data has a history kept via "valid_from" and "valid_to" date fields in each row.
I want to extract the data and qualify it as follows:
NEW => WHERE CURRENT_DATE BETWEEN valid_from AND (valid_from + 1 MOTNH)
CURRENT => WHERE CURRENT_DATE > (valid_from + 1 MOTNH)
RETIRED => the rest of the rows, so the "dish_id" items not in the tables above, BUT
returning the values from the row containing MAX(valid_to) date.
Am I doing this the best / more efficient way? Thanks in advance!
SELECT
menu_table.dish_id,
menu_table.dish_title,
menu_table.marketing_desc,
menu_table_status.rrp_inc_gst,
menu_table_status.lowest_rrp,
menu_table_status.highest_rrp,
'n' as status
FROM
menu_table,
menu_table_status
WHERE
CURRENT_DATE BETWEEN menu_table_status.valid_from_date AND DATE_ADD(menu_table_status.valid_from_date, INTERVAL 1 MONTH)
AND CURRENT_DATE < menu_table_status.valid_to_date
AND menu_table.dish_id = menu_table_status.dish_id
UNION
SELECT
menu_table.dish_id,
menu_table.dish_title,
menu_table.marketing_desc,
menu_table_status.rrp_inc_gst,
menu_table_status.lowest_rrp,
menu_table_status.highest_rrp,
'c' as status
FROM
menu_table,
menu_table_status
WHERE
CURRENT_DATE > DATE_ADD(menu_table_status.valid_from_date, INTERVAL 1 MONTH)
AND CURRENT_DATE < menu_table_status.valid_to_date
AND menu_table.dish_id = menu_table_status.dish_id
UNION
SELECT
menu_table.dish_id,
menu_table.dish_title,
menu_table.marketing_desc,
menu_table_status.rrp_inc_gst,
menu_table_status.lowest_rrp,
menu_table_status.highest_rrp,
'r' as status
FROM
menu_table,
menu_table_status
WHERE
menu_table_status.valid_to_date
AND menu_table.dish_id NOT IN (SELECT inside_table1.dish_id
FROM menu_table_status AS inside_table1
WHERE CURRENT_DATE BETWEEN inside_table1.valid_from_date
AND inside_table1.valid_to_date)
AND menu_table_status.valid_to_date = (SELECT MAX(inside_table2.valid_to_date)
FROM menu_table_status AS inside_table2
WHERE inside_table2.dish_id = menu_table_status.dish_id)
AND menu_table.dish_id = menu_table_status.dish_id
Without much looking at it you are certainly confusing dates in your last where clause. Anyhow, your statement is way to complicated. Simply select all records (which you want to do anyhow) and look at each record's dates to decide for the status to give:
SELECT
menu_table.dish_id,
menu_table.dish_title,
menu_table.marketing_desc,
menu_table_status.rrp_inc_gst,
menu_table_status.lowest_rrp,
menu_table_status.highest_rrp,
CASE
WHEN
CURRENT_DATE BETWEEN menu_table_status.valid_from_date AND DATE_ADD(menu_table_status.valid_from_date, INTERVAL 1 MONTH)
AND CURRENT_DATE < menu_table_status.valid_to_date
THEN 'n'
WHEN
CURRENT_DATE > DATE_ADD(menu_table_status.valid_from_date, INTERVAL 1 MONTH)
AND CURRENT_DATE < menu_table_status.valid_to_date
THEN 'c'
ELSE 'r'
END as status
FROM menu_table
INNER JOIN menu_table_status ON menu_table.dish_id = menu_table_status.dish_id;
BTW: Please don't use that old join syntax where you list all tables comma-separated. It's prone to errors, which is why there is a "new" syntax available as of 1992.
EDIT: I've spotted your error. Instead of checking for CURRENT_DATE < menu_table_status.valid_to_date you check for menu_table_status.valid_to_date only thus treating the date as a boolean value, which is something special in MySQL.
One more remark: When unioning sets that are distinct (yours are because of different status letters)use UNION ALL, not UNION. UNION is used to remove duplicates. Why have the dbms check all your records when you know there are no duplicates?
If you don't need to perform this in one go, I would recommend to extract step one into a temporary table, and then define step two as left join on dish_id with that temporary table, where dish_id is NULL:
CREATE TEMPORARY TABLE step1 AS (
SELECT
mt.dish_id,
mt.dish_title,
mt.marketing_desc,
mts.rrp_inc_gst,
mts.lowest_rrp,
mts.highest_rrp,
(
if(CURRENT_DATE<DATE_ADD(mts.valid_from_date, INTERVAL 1 MONTH),
'n', 'c')
) as status
FROM
menu_table mt
JOIN menu_table_status mts ON mt.dish_id=mts.dish_id
WHERE CURRENT_DATE BETWEEN mts.valid_from_date AND mts.valid_to_date-1
);
SELECT step1.*
UNION
SELECT
mt.dish_id,
mt.dish_title,
mt.marketing_desc,
mts.rrp_inc_gst,
mts.lowest_rrp,
mts.highest_rrp,
'r' as status
FROM
menu_table mt
LEFT JOIN step1 s1 on s1.dish_id=mt.dish_id WHERE s1.dish_id is NULL
JOIN menu_table_status mts ON mt.dish_id=mts.dish_id;

MySQL Where date is greater than one month?

I have a datetime column called 'last_login'.
I want to query my database to select all records that haven't logged in within the last month. How do I do this?
This is what I have currently:
$query = $this->query("SELECT u.id, u.name, u.email, u.registered, g.name as group_name FROM `:#users` AS u LEFT JOIN `:#groups` AS g on u.group_id = g.id WHERE u.last_login = ...... LIMIT {$limit_start}, {$limit_end}");
:# = database prefix
Try using date_sub
where u.last_login < date_sub(now(), interval 1 month)
(Similar to the first answer but in my mind it is more "natural" to use positive integers)
You can use date_add combined with now:
...where u.last_login < date_add(now(), interval -1 month)
Naturally, as both are MySQL-specific this limits you to MySQL backends. Alternately, you can figure out what the date was a month ago with PHP (I'm not a PHP person, but I'm guessing DateTime::sub would help with that) and then include that date in your query in the normal way you would any other date/time field.
matthewh was almost correct, except the > should have been a right one.
where u.last_login > date_sub(now(), interval 1 month)