MySQL making whitespace matter - mysql

Apparently a very rare issue, but IMO extremely annoying and WRONG: Trailing whitespace in MySQL aren't used in comparison:
mysql> SELECT "A" = "A ";
+------------+
| "A" = "A " |
+------------+
| 1 |
+------------+
1 row in set (0.00 sec)
This is especially problematic in the following scenario:
mysql> SELECT COUNT(*) FROM eq WHERE name != TRIM(name);
+------------+
| COUNT(*) |
+------------+
| 0 |
+------------+
1 row in set (0.00 sec)
mysql> UPDATE eq SET name=TRIM(name);
Query OK, 866 row affected (0.01 sec)
Rows matched: 650907 Changed: 866 Warnings: 0
Is there a way to configure MySQL to treat whitespace properly?

According to the manual, one quick fix is to use LIKE:
Per the SQL standard, LIKE performs matching on a per-character basis, thus it can produce results different from the = comparison operator:
...
In particular, trailing spaces are significant, which is not true for CHAR or VARCHAR comparisons performed with the = operator ...
as long as you don't use any wildcards, this should be identical to =. This Stack Overflow question seems to support the assumption: Equals(=) vs. LIKE
The manual doesn't state whether STRCMP() is stricter than = in terms of whitespace, and I can't try it out right now - that might be worth taking a look at, too, as it makes it clearer why = is not used.
Binary comparison as suggested by tombom is also an option, but will have other side-effects (like the stricter comparison of Umlauts, eg. A and Ä will be different) which you may or may not want. More info on the effects of using a binary comparison in this question.

You may use LIKE
SELECT "A" LIKE "A ";
will return 0 but
SELECT "A" LIKE "A";
returns 1

Binary comparison is the magic word.
Binary Comparison in MySQL Manual
mysql> SELECT 'a' = 'A';
-> 1
mysql> SELECT BINARY 'a' = 'A';
-> 0
mysql> SELECT 'a' = 'a ';
-> 1
mysql> SELECT BINARY 'a' = 'a ';
-> 0

Related

whats wrong in below query Select CONVERT(xml,'<x>' + Replace(A.name,':','</x><x>')+'</x>' ) as xDim from Erecharge;

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.)

SQL query for detect columns with special characters?

here and in a lot of other websites I have find a lot of posts regarding this question but for some strange reason no one works.
I have a Wordpress database and I need to find all the terms contained in wp_terms that have any special character.
In fact I need to find all that contains anything else of number or letter.
Why this doesn't work? The MySQL query return 0 results.
SELECT * FROM wp_terms WHERE name LIKE '%[^0-9a-zA-Z ]%'
You can use REGEXP to find out this. Also the ^must be outside from [].
SELECT * FROM wp_terms WHERE name REGEXP '[^0-9a-zA-Z ]'
Test
MariaDB [(none)]> SELECT "Hello" REGEXP '[^0-9a-zA-Z ]' as resut;
+-------+
| resut |
+-------+
| 1 |
+-------+
1 row in set (0.00 sec)
MariaDB [(none)]> SELECT "-Hello" REGEXP '[^0-9a-zA-Z ]' as resut;
+-------+
| resut |
+-------+
| 0 |
+-------+
1 row in set (0.00 sec)
It does not work because MySQL supports the ANSI version of LIKE. The form that you are using is an extended form associated with SQL Server.
On the other hand, MySQL supports regular expressions which are much more powerful. The regular expression for what you want is:
WHERE name REGEXP '[^0-9a-zA-Z ]'
Note that regular expressions match the pattern anywhere in the string, so you do not need wildcards at the beginning and the end.

Broken unicode after simple concat + left mysql commands

I found some very strange mysql behavior.
If I run the following command:
mysql> select left(concat("A", "B®"), 3);
Then the output is as expected:
+-----------------------------+
| left(concat("A", "B®"), 3) |
+-----------------------------+
| AB® |
+-----------------------------+
1 row in set (0.00 sec)
However, if I change "A" with some number (1 in this case):
mysql> select left(concat(1, "B®"), 3);
The unicode character "®" becomes corrupted:
+---------------------------+
| left(concat(1, "B®"), 3) |
+---------------------------+
| 1B? |
+---------------------------+
1 row in set (0.00 sec)
Anybody knows how to explain this strange behavior and how to avoid it?
The example above is only a reproduction, in the real life it's a concat of numbers together with strings unknown ahead (not hard-coded strings).
Thanks a lot!
Mysql doesn't convert integer to strings literally. It converts number into the binary representation of it, which is not the same. "if the arguments include any binary strings, the result is a binary string. A numeric argument is converted to its equivalent binary string form; if you want to avoid that, you can use an explicit type cast, as in this example:
SELECT CONCAT(CAST(int_col AS CHAR), char_col);
Refer this for details.
I would also like to read from others if someone has different opinion.

MySQL always returning BIT values as blank

From my create table script, I've defined the hasMultipleColors field as a BIT:
hasMultipleColors BIT NOT NULL,
When running an INSERT, there are no warnings thrown for this or the other BIT fields, but selecting the rows shows that all BIT values are blank.
Manually trying to UPDATE these records from the command line gives odd effect - shows that the record was match and changed (if appropriate), but still always shows blank.
Server version: 5.5.24-0ubuntu0.12.04.1 (Ubuntu)
mysql> update pumps set hasMultipleColors = 1 where id = 1;
Query OK, 0 rows affected (0.00 sec)
Rows matched: 1 Changed: 0 Warnings: 0
mysql> select hasMultipleColors from pumps where id = 1;
+-------------------+
| hasMultipleColors |
+-------------------+
| |
+-------------------+
1 row in set (0.00 sec)
mysql> update pumps set hasMultipleColors = b'0' where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select hasMultipleColors from pumps where id = 1;
+-------------------+
| hasMultipleColors |
+-------------------+
| |
+-------------------+
1 row in set (0.00 sec)
Any thoughts?
You need to cast the bit field to an integer.
mysql> select hasMultipleColors+0 from pumps where id = 1;
This is because of a bug, see: http://bugs.mysql.com/bug.php?id=43670. The status says: Won't fix.
You can cast BIT field to unsigned.
SELECT CAST(hasMultipleColors AS UNSIGNED) AS hasMultipleColors
FROM pumps
WHERE id = 1
It will return 1 or 0 based on the value of hasMultipleColors.
You need to perform a conversion as bit 1 is not printable.
SELECT hasMultipleColors+0 from pumps where id = 1;
See more here:
http://dev.mysql.com/doc/refman/5.0/en/bit-field-literals.html
The actual reason for the effect you see, is that it's done right and as expected.
The bit field has bits and thus return bits, and trying to output a single bit as a character will show the character with the given bit-value – in this case a zero-width control character.
Some software may handle this automagically, but for command line MySQL you'll have to cast it as int in some way (e.g. by adding zero).
In languages like PHP the ordinal value of the character will give you the right value, using the ord() function (though to be really proper, it would have to be converted from decimal to binary string, to work for bit fields longer than one character).
EDIT:
Found a quite old source saying that it changed, so a MySQL upgrade might make everything work more as expected: http://gphemsley.wordpress.com/2010/02/08/php-mysql-and-the-bit-field-type/

ActiveRecord / MySQL Select Condition Comparing String Components

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)