rails difference between two dates inside .where - mysql

Here is my logic
I want to get the closest 4 more expensive mobiles to a specific mobile #mobile but under one condition the difference between the release dates of the two mobiles is not more than a year and half
Here is the query
high = Mobile.where("price >= #{#mobile.price} AND id != #{#mobile.id} AND visible = true").where("ABS(release_date - #{#mobile.release_date}) > ?", 18.months).order(price: :ASC).first(4)
The first .where() works perfectly but the second is not working and I get this error
Mysql2::Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '00:00:00 UTC) > 46656000) ORDER BY `mobiles`.`price` ASC LIMIT 4' at line 1: SELECT `mobiles`.* FROM `mobiles` WHERE (price >= 123123.0 AND id != 11 AND visible = true) AND (ABS(release_date - 2016-04-10 00:00:00 UTC) > 46656000) ORDER BY `mobiles`.`price` ASC LIMIT 4
I think now you can get my logic. What is the right syntax to achieve it?

A couple of tips here:
It is a dangerous practice to concatenate variables into your queries using the "#{}" operator. Doing so bypasses query parameterization and could leave your app open to SQL injection. Instead, use "?" in your where clause.
The reason MySQL is giving you an error is because you are concatenating a string into your query without encapsulating it in quotes.
With these two things in mind, I would start by refactoring your query like so:
high = Mobile.where("price >= ?", #mobile.price)
.where.not(id: #mobile.id)
.where(visible: true)
.where("ABS(release_date - ?) > 46656000", #mobile.release_date)
.order(price: :ASC).first(4)
You will note that I replaced 18.months with 46656000. This saves a few clock cycles in the Rails app. Depending on your database schema, the last where clause may not work. The modification below may end up working better.
As a further refinement, you could refactor your last where clause to look for a release date that is between 18 months before #mobile.release_date and 18 months after. The saves your MySql database from having to do the math on each record and may lead to better performance:
.where(release_date: (#mobile.release_date - 18.months)..(#mobile.release_date + 18.months) )
I do not know your database schema, so you may run into date conversion problems with the code above. I recommend you play with it in the Rails console.

Use a Range to query between dates/times:
Mobile.where("price >= ?", #mobile.price)
.where.not(id: #mobile.id)
.where(release_date: 18.months.ago..Time.now)
.order(price: :ASC)
.first(4)

Related

Filtering SQL results by date

Here is my query for a maintenance dates list.
SELECT `checkdates`.`checkdateplanneddate`, `checkdates`.`checkdatevehicle`, `checktypes`.`checktype`, `checktypes`.`emailto`, `checktypes`.`daysnotice`
FROM `checkdates`
, `checktypes`
WHERE `checktypes`.`checktype` = `checkdates`.`checkdatechecktype`;
The idea is..
Everyday the server will email customers to let them know which checkdates are coming, based on the days notice that is set for that type of check. (see image)
Currently it is showing all checkdates.
All i need to do is filter the list so it only shows the dates that are
"Todays date plus checktypes.daysnotice"
I have tried many different queries, but cannot seem to get the right combo.
Thank you in advance
I have attached an image to show that the data is available
If I understand your question correctly, and assuming that you are running MySQL (as the use of backticks for quoting and the phpmyadmin screen copy indicate), you can use date arithmetics as follows:
SELECT cd.checkdateplanneddate, cd.checkdatevehicle, ct.checktype, ct.emailto, ct.daysnotice
FROM checkdates cd
INNER JOIN checktypes ct ON ct.checktype = cd.checkdatechecktype
WHERE cd.checkdateplanneddate = current_date + interval ct.daysnotice day
The where condition implements the desired logic.
Side notes:
Use standard, explicit joins! Implicit joins (with commas in the from clause) is a very old syntax, that should not be used in new code
Table aliases make the query easier to write and read

I need to use the MySQL column in a REGEXP on the right hand side

I am trying to run the following query
SELECT * FROM tbl_messenger_keyword AS a
WHERE (a.shortCode = ? OR a.shortCode = '-1') AND ? REGEXP a.rule
I now get
[Err] 3685 - Illegal argument to a regular expression.
This used to work in previous versions of MySQL and I am wondering if there is anyway around this.
The field rule contains the regular expressions that need to be compared against.
I upgraded my local development box to v8.0.16 and it stopped this part of the application from working. I'd like to future proof the applcation for when we upgrade our production database.

MySQL 5.7 RAND() and IF() without LIMIT leads to unexpected results

I have the following query
SELECT t.res, IF(t.res=0, "zero", "more than zero")
FROM (
SELECT table.*, IF (RAND()<=0.2,1, IF (RAND()<=0.4,2, IF (RAND()<=0.6,3,0))) AS res
FROM table LIMIT 20) t
which returns something like this:
That's exactly what you would expect. However, as soon as I remove the LIMIT 20 I receive highly unexpected results (there are more rows returned than 20, I cut it off to make it easier to read):
SELECT t.res, IF(t.res=0, "zero", "more than zero")
FROM (
SELECT table.*, IF (RAND()<=0.2,1, IF (RAND()<=0.4,2, IF (RAND()<=0.6,3,0))) AS res
FROM table) t
Side notes:
I'm using MySQL 5.7.18-15-log and this is a highly abstracted example (real query is much more difficult).
I'm trying to understand what is happening. I do not need answers that offer work arounds without any explanations why the original version is not working. Thank you.
Update:
Instead of using LIMIT, GROUP BY id also works in the first case.
Update 2:
As requested by zerkms, I added t.res = 0 and t.res + 1 to the second example
The problem is caused by a change introduced in MySQL 5.7 on how derived tables in (sub)queries are treated.
Basically, in order to optimize performance, some subqueries are executed at different times and / or multiple times leading to unexpected results when your subquery returns non-deterministic results (like in my case with RAND()).
There are two easy (and likewise ugly) workarounds to get MySQL to "materialize" (aka return deterministic results) these subqueries: Use LIMIT <high number> or GROUP BY id both of which force MySQL to materialize the subquery and return the expected results.
The last option is turn off derived_merge in the optimizer_switch variable: derived_merge=off (make sure to leave all the other parameters as they are).
Further readings:
https://mysqlserverteam.com/derived-tables-in-mysql-5-7/
Subquery's rand() column re-evaluated for every repeated selection in MySQL 5.7/8.0 vs MySQL 5.6

Rails how to check what an sql query is producing

This query doesn't produce an error but I'm pretty sure EXTRACT(EPOCH FROM relationships.created_at) isn't doing what it's meant to.
last_check = #user.last_check.to_i
#new_relationships = User.select('"rels_unordered".*')
.from("(#{#rels_unordered.to_sql}) AS rels_unordered")
.joins("
INNER JOIN relationships
ON rels_unordered.id = relationships.character_id
WHERE EXTRACT(EPOCH FROM relationships.created_at) > #{last_check}
GROUP BY relationships.created_at
ORDER BY relationships.created_at DESC
")
How do I check exactly what EXTRACT(EPOCH FROM relationships.created_at) is producing? The server logs don't show it, they just repeat the query. (At least the logs do show that #{last_check} correctly produces a number like 1471364015, which is why I think the problem is with the epoch code.)
I would just go to mysql and try it out:
SELECT EXTRACT(EPOCH FROM relationships.created_at) FROM relationships limit 0,1;
and see what kind of answer you get. Alter the above to specify a particular record if need be.
A larger problem may be the EPOCH parameter; I'm not sure it's valid. See the mySQL reference for EXTRACT and its parameters.

Could this simple T-SQL update fail when running on multiple processors?

Assuming that all values of MBR_DTH_DT evaluate to a Date data type other than the value '00000000', could the following UPDATE SQL fail when running on multiple processors if the CAST were performed before the filter by racing threads?
UPDATE a
SET a.[MBR_DTH_DT] = cast(a.[MBR_DTH_DT] as date)
FROM [IPDP_MEMBER_DEMOGRAPHIC_DECBR] a
WHERE a.[MBR_DTH_DT] <> '00000000'
I am trying to find the source of the following error
Error: 2014-01-30 04:42:47.67
Code: 0xC002F210
Source: Execute csp_load_ipdp_member_demographic Execute SQL Task
Description: Executing the query "exec dbo.csp_load_ipdp_member_demographic" failed with the following error: "Conversion failed when converting date and/or time from character string.". Possible failure reasons: Problems with the query, "ResultSet" property not set correctly, parameters not set correctly, or connection not established correctly.
End Error
It could be another UPDATE or INSERT query, but the otehrs in question appear to have data that is proeprly typed from what I see,, so I am left onbly with the above.
No, it simply sounds like you have bad data in the MBR_DTH_DT column, which is VARCHAR but should be a date (once you clean out the bad data).
You can identify those rows using:
SELECT MBR_DTH_DT
FROM dbo.IPDP_MEMBER_DEMOGRAPHIC_DECBR
WHERE ISDATE(MBR_DTH_DT) = 0;
Now, you may only get rows that happen to match the where clause you're using to filter (e.g. MBR_DTH_DT = '00000000').
This has nothing to do with multiple processors, race conditions, etc. It's just that SQL Server can try to perform the cast before it applies the filter.
Randy suggests adding an additional clause, but this is not enough, because the CAST can still happen before any/all filters. You usually work around this by something like this (though it makes absolutely no sense in your case, when everything is the same column):
UPDATE dbo.IPDP_MEMBER_DEMOGRAPHIC_DECBR
SET MBR_DTH_DT = CASE
WHEN ISDATE(MBR_DTH_DT) = 1 THEN CAST(MBR_DTH_DT AS DATE)
ELSE MBR_DTH_DT END
WHERE MBR_DTH_DT <> '00000000';
(I'm not sure why in the question you're using UPDATE alias FROM table AS alias syntax; with a single-table update, this only serves to make the syntax more convoluted.)
However, in this case, this does you absolutely no good; since the target column is a string, you're just trying to convert a string to a date and back to a string again.
The real solution: stop using strings to store dates, and stop using token strings like '00000000' to denote that a date isn't available. Either use a dimension table for your dates or just live with NULL already.
Not likely. Even with multiple processors, there is no guarantee the query will processed in parallel.
Why not try something like this, assuming you're using SQL Server 2012. Even if you're not, you could write a UDF to validate a date like this.
UPDATE a
SET a.[MBR_DTH_DT] = cast(a.[MBR_DTH_DT] as date)
FROM [IPDP_MEMBER_DEMOGRAPHIC_DECBR] a
WHERE a.[MBR_DTH_DT] <> '00000000' And IsDate(MBR_DTH_DT) = 1
Most likely you have bad data are are not aware of it.
Whoops, just checked. IsDate has been available since SQL 2005. So try using it.