I have a string that is defined as one or more dot-separated integers like 12345, 543.21, 109.87.654, etc. I'm storing values in a MySQL database and then need to find the rows that compare with a provided value. What I want is to select rows by comparing each component of the string against the corresponding component of the input string. With standard string comparison in MySQL, here's where this breaks down:
mysql> SELECT '543.21' >= '500.21'
-> 1
mysql> SELECT '543.21' >= '5000.21'
-> 1
This is natural because the string comparison is a "dictionary" comparison that doesn't account for string length, but I want a 0 result on the second query.
Is there a way to provide some hint to MySQL on how to compare these? Otherwise, is there a way to hint to ActiveRecord how to do this for me? Right now, the best solution I have come up with is to select all the rows and then filter the results using Ruby's split and reject methods. (The entire data set is quite small and not likely to grow terribly much for the foreseeable future, so it is a reasonable option, but if there's a simpler way I'm not considering I'd be glad to know it.)
You can use REPLACE to remove dots and CAST to convert string to integer:
SELECT CAST(REPLACE("543.21", ".", "") AS SIGNED) >= CAST(REPLACE("5000.21", ".", "") AS SIGNED)
mysql> SELECT '543.21' >= '5000.21';
+-----------------------+
| '543.21' >= '5000.21' |
+-----------------------+
| 1 |
+-----------------------+
1 row in set (0.00 sec)
mysql> SELECT '543.21'+0 >= '5000.21'+0;
+---------------------------+
| '543.21'+0 >= '5000.21'+0 |
+---------------------------+
| 0 |
+---------------------------+
1 row in set (0.00 sec)
This indeed only works for valid floats. Doing it for more then 1 dot would require a LOT of comparing of SUBSTRING_INDEX(SUBSTRING_INDEX(field, '.', <positionnumber you're comparing>), '.', -1) (with a manual repeat for the maximum number of position's you are comparing)
Related
I am wondering if it is possible to avoid a second equal calculation within a CASE statement of MySQL 5.7?
CASE
WHEN char_length(cat.DESCRIPTION) > 0 THEN char_length(cat.DESCRIPTION)
ELSE ''
END AS D_LENGTH
The second char_length seems redundant to me and might be reducing query performance. Is there a way to improve this?
Since you seem to want to display empty string when the character length of the column be zero, you could try using a TRIM trick here:
SELECT TRIM(LEADING '0' FROM CHAR_LENGTH(cat.DESCRIPTION)) AS D_LENGTH
FROM yourTable cat;
This works because whenever the character length of the description be greater than zero, it would never have any leading zeroes. When that length is actually zero, the call to TRIM above would just strip off the single zero, leaving behind an empty string.
Regarding your current approach, no, there isn't much you can do directly to avoid the double call to CHAR_LENGTH. But, as shown above, there are ways out which completely avoid the duplication.
You may try to use intermediate user-defined variable:
CASE
WHEN (#tmp:=char_length(cat.DESCRIPTION)) > 0
THEN #tmp
ELSE ''
END AS D_LENGTH
On "clear" model (a table with one varchar column, data lengths 20-250, 1kk rows, no indices, the whole table is cached) it takes ~15% less time to execute on my system (5.11-5.32s against 5.97-6.23s).
The query produces a warning "1287 Setting user variables within expressions is deprecated and will be removed in a future release. Consider alternatives: 'SET variable=expression, ...', or 'SELECT expression(s) INTO variables(s)'." - ignore it.
First, the performance cost of evaluating any expression is much less than the cost of the rest of the query. So don't bother with such optimization.
Second, to rise to the challenge:
mysql> SELECT COALESCE(NULLIF(CHAR_LENGTH('asdf'), 0), 'empty');
+---------------------------------------------------+
| COALESCE(NULLIF(CHAR_LENGTH('asdf'), 0), 'empty') |
+---------------------------------------------------+
| 4 |
+---------------------------------------------------+
1 row in set (0.00 sec)
mysql> SELECT COALESCE(NULLIF(CHAR_LENGTH(''), 0), 'empty');
+-----------------------------------------------+
| COALESCE(NULLIF(CHAR_LENGTH(''), 0), 'empty') |
+-----------------------------------------------+
| empty |
+-----------------------------------------------+
1 row in set (0.00 sec)
I want to convert string to xml column ..
I used below query for that :
Select CONVERT(xml,'<x>' + Replace(A.name,':','</x><x>')+'</x>' ) as xDim from Erecharge;
but it shows error of incorrect sql syntax..
I want to know whats wrong in above query
I also tried this:
Select Cast('<x>' + Replace(A.name,':','</x><x>')+'</x>' as XML) as xDim from Erecharge;
check the manual that corresponds to your MySQL server version for the right syntax to use near 'XML) as xDim from Erecharge'
This means that XML is incorrect in a expression like this:
CAST('foo' AS XML)
As per the docs, the values allowed for CAST type do not include XML.
Additionally, using the + operator on strings is just a convoluted way to render zero:
mysql> SELECT 'a' + 'b';
+-----------+
| 'a' + 'b' |
+-----------+
| 0 |
+-----------+
1 row in set, 2 warnings (0.00 sec)
It's not entirely clear what you're trying to do. MySQL has XML Functions but it doesn't have XML data types. If you just want to produce a string that happens to contain XML code then you need to CONCAT():
mysql> SELECT CONCAT('<date>', CURRENT_TIMESTAMP, '</date>') AS foo;
+----------------------------------+
| foo |
+----------------------------------+
| <date>2018-10-12 11:44:29</date> |
+----------------------------------+
1 row in set (0.00 sec)
... but of course you still need to ensure that angle brackets and similar stuff don't break the XML. CDATA may help. (No idea about XML functions, I'm not familiar with them.)
Here is the code
mysql> SELECT id FROM tbl WHERE id = '1h';
+----+
| id |
+----+
| 1 |
+----+
1 row in set
There is indeed a field with id 1 (but not '1h').
Here is an extraction from MySQL docs: http://dev.mysql.com/doc/refman/5.1/en/type-conversion.html
mysql> SELECT 1 > '6x';
-> 0
mysql> SELECT 7 > '6x';
-> 1
So this bug is documented, so to say. The question is what's the reason for such behavior and how to correct it to make this not cast strings with char symbols? I can cast all field values like
mysql> SELECT id FROM tbl WHERE cast(`id`, BINARY) = '1h';
but i don't like this variant too much
This is not a bug.
The solution is not to query on numeric columns using a string value for your condition.
Never rely on implicit type casting.
None of your observations are bugs. They are the result of relying in implicit type casting.
In all of your examples, you're requiring MySQL to convert a string to an int. If you read the very page that you linked to, you will see that MySQL follows some rules in achieving this. As a result
'1h' -> 1
'6x' -> 6
'x6' -> 0
So, if you follow these rules, you'll be OK.
Better still, just don't put MySQL in a position where it needs to be doing these conversion. Such situations usually point to some kind of logic bug elsewhere in the system.
My table filed's value is "<script type="text/javascript"src="http://localhost:8080/db/widget/10217EN/F"></script>",
I want to analyse this string and fetch the id 10217,how to do use mysql regex?
I know python regex group function can return the id 10217,but i'm not familiar with mysql regex.
Please help me,Thank you very much.
MySQL regular expressions do not support subpattern extraction. You will probably have better luck iterating over all of the rows in your database and storing the results in a new column.
As far as I know, you can't use MySQL's REGEXP for substring retrieval; it is designed for use in WHERE clauses and is limited to returning 0 or 1 to indicate failure or success at a match.
Since your pattern is pretty well defined, you can probably retrieve the id with a query that uses SUBSTR and LOCATE. It will be a bit of a mess since SUBSTR wants the start index and the length of the substring (it would be easier if it took the end index). Perhaps you could use TRIM to chop off the unwanted trailing part.
This query get the Id from the field
SELECT substring_index(SUBSTRING_INDEX(testvar,'/',-3),'EN',1) from testtab;
where as testtab - is table name , testvar - is field name
inner substring get string starts with last 3 / which is
mysql> SELECT SUBSTRING_INDEX(testvar,'/',-3) from testtab;
+----------------------------+
| SUBSTRING_INDEX(testvar,'/',-3) |
+----------------------------+
| 10217EN/F"> |
| 10222EN/F"> |
+----------------------------+
2 rows in set (0.00 sec)
outer substring get
mysql> SELECT substring_index(SUBSTRING_INDEX(testvar,'/',-3),'EN',1) from testtab;
+----------------------------------------------------+
| substring_index(SUBSTRING_INDEX(testvar,'/',-3),'EN',1) |
+----------------------------------------------------+
| 10217 |
| 10222 |
+----------------------------------------------------+
2 rows in set (0.00 sec)
I recently just fixed a bug in some of my code and was hoping someone could explain to me why the bug occurred.
I had a query like this:
SELECT * FROM my_table WHERE my_field=13
Unexpectedly, this was returning rows where my_field was equal to either 13 or 13a. The fix was simple, I changed the query to:
SELECT * FROM my_table WHERE my_field='13'
My question is, is this supposed to be the case? I've always thought that to return a similar field, you would use something like:
SELECT * FROM my_table WHERE my_field LIKE '13%'
What is the difference between LIKE + a Wild Card vs an equals operator with no quotes?
This statement returns rows for my_field = '13a':
SELECT * FROM my_table WHERE my_field=13
Because MySQL performs type conversion from string to number during the comparison, turning '13a' to 13. More on that in this documentation page.
Adding quotes turns the integer to a string, so MySQL only performs string comparison. Obviously, '13' cannot be equal to '13a'.
The LIKE clause always performs string comparison (unless either one of the operands is NULL, in which case the result is NULL).
My guess would be that since you didn't enclose it in quotes, and the column was a char/varchar column, MySQL tried to do an implicit conversion of the varchar column to an int.
If one of the rows in that table contained a value that couldn't be converted to an int, you would probably get an error. Also, because of the conversion, any indexes you might have had on that column would not be used either.
This has to do with types and type conversion. With my_field=13 , 13 is an integer, while my_field is in your case likely some form of text/string. In such a case, mysql will try to convert both to a floating point number and compare those.
So mysql tries to convert e,g, "13a" to a float, which will which be 13, and 13 = 13
In my_field = '13' , both operands are text and will be compared as text using =
In my_field like '13%' both operands are also text and will be compared as such using LIKE, where the special % means a wildcard.
You can read about the type conversion mysql uses here.
This is because the MySQL type conversion works this way. See here: http://dev.mysql.com/doc/refman/5.0/en/type-conversion.html
It releases a warning as well. see the code below
mysql> select 12 = '12bibo';
+---------------+
| 12 = '12bibo' |
+---------------+
| 1 |
+---------------+
1 row in set, 1 warning (0.00 sec)
mysql> show warnings;
+---------+------+--------------------------------------------+
| Level | Code | Message |
+---------+------+--------------------------------------------+
| Warning | 1292 | Truncated incorrect DOUBLE value: '12bibo' |
+---------+------+--------------------------------------------+
1 row in set (0.00 sec)
Looks like someone raised a bug as well: http://bugs.mysql.com/bug.php?id=42241