Rails how to check what an sql query is producing - mysql

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.

Related

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 difference between two dates inside .where

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)

Error: MySQL client ran out of memory

Can anyone please advise me on this error...
The database has 40,000 news stories but only the fields 'story' is large,
'old' is a numeric value 0 or 1,
'title' and 'shortstory' are very short or NULL.
any advice appreciated. This is the result of running a search database query.
Error: MySQL client ran out of memory
Statement: SELECT news30_access.usehtml, old, title, story, shortstory, news30_access.name AS accessname, news30_users.user AS authorname, timestamp, news30_story.id AS newsid FROM news30_story LEFT JOIN news30_users ON news30_story.author = news30_users.uid LEFT JOIN news30_access ON news30_users.uid = news30_access.uid WHERE title LIKE ? OR story LIKE ? OR shortstory LIKE ? OR news30_users.user LIKE ? ORDER BY timestamp DESC
The simple answer is: don't use story in the SELECT clause.
If you want the story, then limit the number of results being returned. Start with, say, 100 results by adding:
limit 100
to the end of the query. This will get the 100 most recent stories.
I also note that you are using like with story as well as other string columns. You probably want to be using match with a full text index. This doesn't solve your immediate problem (which is returning too much data to the client). But, it will make your queries run faster.
To learn about full text search, start with the documentation.

what is wrong with this line of SQL?

SELECT Batch.NumStud
FROM Batch
WHERE CourseID='$courseid'
INNER JOIN Course
ON Batch.CourseID=Course.CourseID"
an error that says mysql_fetch_array(): supplied argument is not a valid MySQL result resource in <b>F:\AppServ\www\anNoECourse.php
is shown.This code was written to feed in data to a google chart.
You put SQL in wrong order (JOIN and WHERE are switched):
SELECT Batch.NumStud
FROM Batch INNER JOIN Course
ON Batch.CourseID = Course.CourseID
WHERE Course.CourseID = '$courseid'
It seems, that your query can be simplified (check your data):
select Batch.NumStud
from Batch
where Batch.CourseID = '$courseid'
I think the error is a bit more complex. Due to the fact that your SQL is invalid, you're not getting a result set. This case is not handled correctly by your PHP code!
So in addition to correcting your SQL as the others have suggested, please make sure to handle the case where you get no results or your query results in an error correctly in your PHP code!
The second part to your solution is as follows:
$result = mysql_query(...);
if ($result)
{
while (...)
...
}
This makes sure that mysql_query actually returned a result set and not false, which it does in case of errors (due to your invalid SQL code, but also in other cases). So just fixing your SQL is not enough to make your script error proof.
But again, do no longer use the mysql_.... functions! They are deprecated.

MySQL order by problems

I have the following codes..
echo "<form><center><input type=submit name=subs value='Submit'></center></form>";
$val=$_POST['resulta']; //this is from a textarea name='resulta'
if (isset($_POST['subs'])) //from submit name='subs'
{
$aa=mysql_query("select max(reservno) as 'maxr' from reservation") or die(mysql_error()); //select maximum reservno
$bb=mysql_fetch_array($aa);
$cc=$bb['maxr'];
$lines = explode("\n", $val);
foreach ($lines as $line) {
mysql_query("insert into location_list (reservno, location) values ('$cc', '$line')")
or die(mysql_error()); //insert value of textarea then save it separately in location_list if \n is found
}
If I input the following data on the textarea (assume that I have maximum reservno '00014' from reservation table),
Davao - Cebu
Cebu - Davao
then submit it, I'll have these data in my location_list table:
loc_id || reservno || location
00001 || 00014 || Davao - Cebu
00002 || 00014 || Cebu - Davao
Then this code:
$gg=mysql_query("SELECT GROUP_CONCAT(IF((#var_ctr := #var_ctr + 1) = #cnt,
location,
SUBSTRING_INDEX(location,' - ', 1)
)
ORDER BY loc_id ASC
SEPARATOR ' - ') AS locations
FROM location_list,
(SELECT #cnt := COUNT(1), #var_ctr := 0
FROM location_list
WHERE reservno='$cc'
) dummy
WHERE reservno='$cc'") or die(mysql_error()); //QUERY IN QUESTION
$hh=mysql_fetch_array($gg);
$ii=$hh['locations'];
mysql_query("update reservation set itinerary = '$ii' where reservno = '$cc'")
or die(mysql_error());
is supposed to update reservation table with 'Davao - Cebu - Davao' but it's returning this instead, 'Davao - Cebu - Cebu'. I was previously helped by this forum to have this code working but now I'm facing another difficulty. Just can't get it to work. Please help me. Thanks in advance!
I got it working (without ORDER BY loc_id ASC) as long as I set phpMyAdmin operations loc_id ascending. But whenever I delete all data, it goes back as loc_id descending so I have to reset it. It doesn't entirely solve the problem but I guess this is as far as I can go. :)) I just have to make sure that the table column loc_id is always in ascending order. Thank you everyone for your help! I really appreciate it! But if you have any better answer, like how to set the table column always in ascending order or better query, etc, feel free to post it here. May God bless you all!
The database server is allowed to rewrite your query to optimize its execution. This might affect the order of the individual parts, in particular the order in which the various assignments are executed. I assume that some such reodering causes the result of the query to become undefined, in such a way that it works on sqlfiddle but not on your actual production system.
I can't put my finger on the exact location where things go wrong, but I believe that the core of the problem is the fact that SQL is intended to work on relations, but you try to abuse it for sequential programming. I suggest you retrieve the data from the database using portable SQL without any variable hackery, and then use PHP to perform any post-processing you might need. PHP is much better suited to express the ideas you're formulating, and no optimization or reordering of statements will get in your way there. And as your query currently only results in a single value, fetching multiple rows and combining them into a single value in the PHP code shouldn't increase complexety too much.
Edit:
While discussing another answer using a similar technique (by Omesh as well, just as the answer your code is based upon), I found this in the MySQL manual:
As a general rule, you should never assign a value to a user variable
and read the value within the same statement. You might get the
results you expect, but this is not guaranteed. The order of
evaluation for expressions involving user variables is undefined and
may change based on the elements contained within a given statement;
in addition, this order is not guaranteed to be the same between
releases of the MySQL Server.
So there are no guarantees about the order these variable assignments are evaluated, therefore no guarantees that the query does what you expect. It might work, but it might fail suddenly and unexpectedly. Therefore I strongly suggest you avoid this approach unless you have some relaibale mechanism to check the validity of the results, or really don't care about whether they are valid.