There is a Best Practices page in official Doctrine documentation.
https://www.doctrine-project.org/projects/doctrine-orm/en/2.14/reference/best-practices.html#don-t-use-identifier-quoting
It's said: "Don't use identifier quoting". I don't quite understand that. Can you explain me what is identifier quoting and why it's not rocommended to use it? What to use instead? Please write some SQL queries for the example.
Normally you can't make a table whose name conflicts with an SQL reserved keyword, or contains punctuation or whitespace.
mysql> CREATE TABLE interval (begin INT, end INT);
ERROR 1064 (42000): You have an error in your SQL syntax ...
near 'interval (begin INT, end INT)'
But you can delimit an identifier with back-ticks (or double-quotes in standard SQL) to allow tables with normally illegal names.
mysql> CREATE TABLE `interval` (begin INT, end INT);
Query OK, 0 rows affected (0.01 sec)
Doctrine is recommending that you solve this problem by just avoiding naming your tables in a way that requires them to be delimited. They are not specific about the edge cases they think will happen.
One could be if you try to create a table whose name contains a literal back-tick character. This is permitted too, but it requires care to escape the back-tick.
mysql> create table `my``table` (begin INT, end INT);
Query OK, 0 rows affected (0.03 sec)
mysql> show tables;
+----------------+
| Tables_in_test |
+----------------+
| my`table |
+----------------+
The other problem is that you must remember to delimit illegal table names in every query that references them. Since Doctrine generates a lot of SQL, it's possible they don't think they can do this consistently, and their solution is to put the responsibility on you to avoid the issue.
Their solution to avoid tables that have reserved words has a problem: every release of MySQL has new reserved words. A table that is allowed in MySQL 5.7 may conflict with a new reserved word in MySQL 8.0. If you can't delimit table names, then you must study the list of new reserved words in 8.0 and rename tables before you upgrade.
In fact, in the MySQL 8.0 era, they are more comfortable introducing new backward-incompatible features in minor releases, so there could be new reserved words introduced at any time. For example, FULL was made a keyword in MySQL 8.0.32, so it now generates a warning:
mysql> create table full ( i int);
Query OK, 0 rows affected, 1 warning (0.01 sec)
mysql> show warnings;
+---------+------+----------------------------------------------------------------------------------------------+
| Level | Code | Message |
+---------+------+----------------------------------------------------------------------------------------------+
| Warning | 4119 | Using FULL as unquoted identifier is deprecated, please use quotes or rename the identifier. |
+---------+------+----------------------------------------------------------------------------------------------+
Related
If i modify the max number of allowed digits in MYSQL 5.7 from double(8,2) to double(12,2), is the change immediate or will it need to process all rows??
You can test this to see if it can be changed as an instant change:
mysql> create table mytable (id serial primary key, d double(8,2));
mysql> alter table mytable modify column d double(25,2), algorithm=inplace, lock=none;
Query OK, 0 rows affected, 1 warning (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 1
The options algorithm=inplace, lock=none mean you want the alter to run as an instant change, without performing a table copy. By default, MySQL runs the DDL change in that mode if the change can be done in that mode.
If you request it explicitly, but the change cannot be done in that mode, then you'll get an error.
For example:
mysql> alter table mytable modify column d float(8,2), algorithm=inplace, lock=none;
ERROR 1846 (0A000): ALGORITHM=INPLACE is not supported.
Reason: Cannot change column type INPLACE. Try ALGORITHM=COPY.
In this example I'm changing the 8-byte DOUBLE to a 4-byte FLOAT. Any change to the size of a data type cannot be done without copying the table. So the request to do it as an instant change fails and the error shown is returned.
So if you're in doubt about whether a given change can be done instantly, you can use this method to test it. You don't have to do the test against your production table! I did this test on my local instance, without even adding any data to the table. I just created an empty table as I showed above, and ran the DDL.
You should read https://dev.mysql.com/doc/refman/8.0/en/innodb-online-ddl-operations.html for details on which types of DDL changes can be done inplace.
As written in the MySQL Reference Manual all ALTER Table statements needs processing time. On top of this MySQL will convert the data:
For data type changes using CHANGE or MODIFY, MySQL tries to convert existing column values to the new type as well as possible.
Therefore all Columns will be visited.
Beside the fact that specifying number of digits for floating point data types is deprecated, a change from double(8,2) to double(12,2) doesn't change the column type, it is still a double precision 8-byte number, so not even a single row will change it's value.
Example:
CREATE TABLE t1 (a double(6,2));
INSERT INTO t1 VALUES (1234.56);
# change the precision
ALTER TABLE t1 change a a double(4,2);
INSERT INTO t1 VALUES (1234.56);
ERROR 1264 (22003): Out of range value for column 'a' at row 1
SELECT a FROM t1;
+---------+
| a |
+---------+
| 1234.56 |
+---------+
Even if 1234.56 doesn't fit in double(4,2) it is still unchanged.
Can you create a table in mysql with dots (.) in it?
I´m having trouble manipulating a table with a dot in its name. I did it this way as a shortcut for a php query.
You can create table names with punctuation by delimiting the identifiers with back-ticks.
mysql> create table `my...table` (id serial primary key);
Query OK, 0 rows affected (0.05 sec)
mysql> insert into `my...table` values (1234);
Query OK, 1 row affected (0.02 sec)
mysql> select * from `my...table`;
+------+
| id |
+------+
| 1234 |
+------+
It's legal, but probably more trouble than it's worth. You have to remember to use the back-ticks every time you reference the table.
A good compromise is to use _ instead of a dot, because you don't have to delimit the table name for that character.
You could quote the table name(s) with back-ticks — or, if you have control over it, and it's not too late, substitute the special characters with a standardized alternative:
select * from `table.name`
Permissible characters (without the need for quotes):
[0-9,a-z,A-Z$_]
I'm running into a strange problem with mysql does not like my table name.
mysql> DROP TABLE IF EXISTS 6e0OU1QgkU7Pj6ycQF0U_results;
ERROR 1064 (42000): 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 '6e0OU1QgkU7Pj6ycQF0U_results' at line 1
mysql> DROP TABLE IF EXISTS 6epGz4xKzfKd6A9e1ASP_results;
Query OK, 0 rows affected (0.00 sec)
mysql>
Any idea why the first query has a syntax error while the second query is allowed?
It's probably the 6e0, which the SQL parser thinks is a number in scientific notation: 6 * 100.
Thats because mysql does not recognize the table name since it starts with a digit MeN
Identifiers may begin with a digit but unless quoted may not consist
solely of digits.
https://dev.mysql.com/doc/refman/5.0/en/identifiers.html
It is recommended that you do not use names that begin with Me or MeN,
where M and N are integers. For example, avoid using 1e as an
identifier, because an expression such as 1e+3 is ambiguous. Depending
on context, it might be interpreted as the expression 1e + 3 or as the
number 1e+3.
You can try as
DROP TABLE IF EXISTS `6e0OU1QgkU7Pj6ycQF0U_results`;
In MySQL, an overflow creates only a warning and MySQL destroys the data you feed it:
mysql> create table tmp(data tinyint primary key);
Query OK, 0 rows affected (7.83 sec)
mysql> insert into tmp set data = 200;
Query OK, 1 row affected, 1 warning (2.27 sec)
mysql> select * from tmp;
+------+
| data |
+------+
| 127 |
+------+
1 row in set (0.00 sec)
This is a huge problem, especially when using MySQL via a programming language, where one (usually) does not check any MySQL-warnings. This may cause not only data-corruption, but data-corruption that is hidden and may remain undetected - one of the worst things that may happen in a database.
Is there a way to configure MySQL so that an overflow causes an ERROR (similar to a syntax error) and not only a warning?
See Mysql documentation
you have to activate the strict SQL mode:
If strict SQL mode is enabled, MySQL rejects the out-of-range value with an error, and the insert fails, in accordance with the SQL standard.
If no restrictive modes are enabled, MySQL clips the value to the appropriate endpoint of the range and stores the resulting value instead.
I'm using MySQL in strict mode (SET sql_mode = 'STRICT_TRANS_TABLES') to convert all warnings to errors. However, I have a query that is expected to create warnings because it tries to convert a VARCHAR field that might be empty or contain letters to an integer.
Example:
mysql> select CAST("123b" AS SIGNED);
+------------------------+
| CAST("123b" AS SIGNED) |
+------------------------+
| 123 |
+------------------------+
1 row in set, 1 warning (0.00 sec)
mysql> show warnings;
+---------+------+-------------------------------------------+
| Level | Code | Message |
+---------+------+-------------------------------------------+
| Warning | 1292 | Truncated incorrect INTEGER value: '123b' |
+---------+------+-------------------------------------------+
1 row in set (0.00 sec)
Is there a way to suppress the warning caused by the CAST() without disabling strict mode? Or alternatively, can the strict mode be disabled for a single query or function (something like the # operator in PHP) without calling SET twice to temporarily switch off the strict mode?
Background: I have a table with street numbers. Most of them are numeric but some contain letters at the end. To implement a simplistic "natural sort" I'd like to use ORDER BY CAST (StreetNr AS SIGNED), StreetNr and the value returned by CAST() would be just fine for 1st level sorting.
I'm assuming the problem is that you are trying to insert data from one table into another using a query like this:
INSERT INTO ...
SELECT ...
FROM ...
WHERE ...
ORDER BY ...
And the inserts are failing because of the CAST() problem you described in your question.
Is that accurate?
If so, the easiest way around this is to use INSERT IGNORE. That syntax is useful for ignoring duplicate key errors, but it can also be used to ignore the CAST() errors that are affecting you.
Your updated query would look something like this:
INSERT IGNORE INTO target_table
SELECT ...
FROM source_table
WHERE ...
ORDER BY CAST (StreetNr AS SIGNED), StreetNr
I had the same issue with ordering and used regexp first:
ORDER BY CAST(CONCAT('0', REGEXP_REPLACE(StreetNr, '[^0-9]', '')) AS INTEGER)
The concat-0 is to handle an empty string.
(My server is MariaDB, but it should be the same as MySQL.)