I am creating a INSERT script and I am coming across a problem. There is a middle initial field that is a char(1)
Sometimes the records don't have anything in that field so I put a NULL. This causes a Data too long for column error. I don't want to just put ' ' as that leaves just a blanks space.
Is there another way around this?
It sounds like you may be attempting to insert the string 'NULL' into the char(1) field, rather than an SQL NULL, and you have strict SQL mode enabled, which prevents this being truncated to N.
If you are able, run
SHOW CREATE TABLE <your_table_name>
in the MySQL shell to determine whether your target field accepts NULLs. With no context (are you running this as pure SQL, or connecting to the database from some client program in another langauge?) it's difficult to provide the exact solution, but you may have something like this:
INSERT <your_table_name>
SELECT first_name, 'NULL', last_name
where 'NULL' is simply a string with no special meaning, when what you intend is
INSERT <your_table_name>
SELECT first_name, NULL, last_name
Here's an illustration:
mysql> CREATE TABLE my_table ( middle_initial CHAR(1) NULL );
mysql> INSERT INTO my_table SELECT 'NULL';
mysql> SHOW WARNINGS;
Level Code Message
Warning 1265 Data truncated for column 'middle_initial' at row 1
mysql> SELECT * FROM my_table;
middle_initial
--------------
N
mysql> set sql_mode = 'STRICT_TRANS_TABLES';
mysql> INSERT INTO my_table SELECT 'NULL';
ERROR 1406 (22001) at line 16: Data too long for column 'middle_initial' at row 1
mysql> INSERT INTO my_table SELECT NULL;
mysql> SELECT * FROM my_table;
middle_initial
--------------
N
NULL
Bit of a punt - apologies if no use ...
Related
Synopsis
I would like to use multiple REGEXP_REPLACE function calls in a SELECT statement. The idea is to replace different expressions with different values.
Using a single call to REGEXP_REPLACE works perfectly fine. Anyone with a first name that doesn't start with T is displayed as NOT TREVOR, and anyone who has a name starting with T is simply displayed as-is.
DROP DATABASE cbtnuggets;
CREATE DATABASE cbtnuggets;
CREATE TABLE IF NOT EXISTS cbtnuggets.people (firstName varchar(30));
INSERT INTO cbtnuggets.people (firstName) VALUES ('Trevor'), ('Daniel'), ('Sally');
SELECT
REGEXP_REPLACE(firstName, '^[^T].*', 'NOT TREVOR')
FROM cbtnuggets.people;
Problematic Query
The following query does not work the way that I would like for it to. Due to the OR statement, the result is a 0 or 1, instead of a string.
DROP DATABASE cbtnuggets;
CREATE DATABASE cbtnuggets;
CREATE TABLE IF NOT EXISTS cbtnuggets.people (firstName varchar(30));
INSERT INTO cbtnuggets.people (firstName) VALUES ('Trevor'), ('Daniel'), ('Sally');
SELECT
REGEXP_REPLACE(firstName, '^[^T].*', 'NOT TREVOR')
OR REGEXP_REPLACE(firstName, '^T', 'This is Trevor')
FROM cbtnuggets.people;
Expected Result
I would like to return different values for different expressions in a single SELECT statement (if possible), using the REGEXP_REPLACE function.
Question
Is there any way to handle this desired result in a select statement, or would I need to create a MySQL function to handle these multiple expressions?
You can run one regexp_replace(), which returns a string, and that string can be the input to another regexp_replace().
mysql> select regexp_replace(regexp_replace('Frank', '^T.*', 'This is Trevor'), '^[^T].*', 'This is not Trevor') as newlabel;
+--------------------+
| newlabel |
+--------------------+
| This is not Trevor |
+--------------------+
1 row in set (0.00 sec)
mysql> select regexp_replace(regexp_replace('Trevor', '^T.*', 'This is Trevor'), '^[^T].*', 'This is not Trevor') as newlabel;
+----------------+
| newlabel |
+----------------+
| This is Trevor |
+----------------+
1 row in set (0.00 sec)
This usage works, but it's pretty confusing to write, especially if you have more than two patterns to check. I'd try to use a different approach.
I'm not even sure you need regex here for your query:
SELECT
CASE WHEN firstName LIKE 'T%'
THEN 'This is Trevor'
ELSE 'NOT TREVOR' END AS label
FROM cbtnuggets.people;
The REGEXP_REPLACE function is intended to take an input string and output a modification of that input, rather than a boolean value. If you did want to assert a regex pattern against a string, then use REGEXP:
SELECT
CASE WHEN firstName REGEXP '^T'
THEN 'This is Trevor'
ELSE 'NOT TREVOR' END AS label
FROM cbtnuggets.people;
Like the title says, i am trying to store the results of a query in a variable so that i can use it in another query that would join them. I have been trying to do this using the INTO santex as it describes it on google. This is what i am entering:
mysql> select Name, Type, Region from table1 union select Name, Type, Region from Table2 into #temp1;
However, with this, i am getting this error:
ERROR 1222 (21000): The used SELECT statements have a different number of columns
Which does not make sense as the Select statement has the same number of columns. So i figure maybe its my variable, so i remove the # and tried it again, but i got this error:
ERROR 1327 (42000): Undeclared variable: temp
So now im at a lost, what am i doing wrong? I am following the syntax that is describe on several wikis on google but with no luck.
You can create a temporary table like this.
mysql> CREATE TEMPORARY TABLE SalesSummary (
-> product_name VARCHAR(50) NOT NULL
-> , total_sales DECIMAL(12,2) NOT NULL DEFAULT 0.00
-> , avg_unit_price DECIMAL(7,2) NOT NULL DEFAULT 0.00
-> , total_units_sold INT UNSIGNED NOT NULL DEFAULT 0
);
They have great tutorials here:http://www.tutorialspoint.com/mysql/mysql-temporary-tables.htm
You need to set your variable in your SELECT statement, and not after.
See: http://dev.mysql.com/doc/refman/5.0/en/example-user-variables.html
There are such table and functions:
CREATE TABLE `test` (`ID` BINARY(16)) ENGINE=InnoDB;
CREATE FUNCTION `UUID_ENCODE`(`uuid` CHAR(36))
RETURNS binary(16)
DETERMINISTIC
BEGIN
RETURN UNHEX(REVERSE(REPLACE(uuid,'-','')));
END
CREATE FUNCTION `UUID_DECODE`(`uuid` BINARY(16))
RETURNS char(36)
DETERMINISTIC
BEGIN
RETURN LOWER(CONCAT_WS('-',
REVERSE(HEX(RIGHT(uuid,6))),
REVERSE(HEX(MID(uuid,9,2))),
REVERSE(HEX(MID(uuid,7,2))),
REVERSE(HEX(MID(uuid,5,2))),
REVERSE(HEX(LEFT(uuid,4)))
));
END
Insert is working properly:
INSERT INTO test (ID) VALUES(UUID_ENCODE('323febe6-cd89-4773-a46c-aab794fb7cbc'));
SELECT UUID_DECODE(ID) FROM test;
+--------------------------------------+
| uuid_decode(id) |
+--------------------------------------+
| 323febe6cd89-4773-a46c-aab7-94fb7cbc |
+--------------------------------------+
Just create a trigger:
CREATE TRIGGER `test_uuid_encode` BEFORE INSERT ON `test` FOR EACH ROW BEGIN
SET NEW.ID = UUID_ENCODE(NEW.ID);
END
and get half truncated value in table:
INSERT INTO test (ID) VALUES('323febe6-cd89-4773-a46c-aab794fb7cbc');
Warning (Code 1265): Data truncated for column 'ID' at row 1
SELECT UUID_DECODE(ID) FROM test;
+--------------------------------------+
| uuid_decode(id) |
+--------------------------------------+
| 000000000000-0000-0032-3feb-e6cd8947 |
+--------------------------------------+
What is wrong with this trigger?
Software version is:
Ver 5.5.33a-MariaDB for Win64 on x86 (mariadb.org binary distribution)
The ID column of your table is declared as BINARY(16), and your UUID_ENCODE expects a CHAR(36). When you invoke your UUID_ENCODE function directly without a trigger, it correctly receives your 36 characters string. When you use the trigger instead, the value you are inserting is first converted to the type of the column, so NEW.ID will contain the results of CAST('323febe6-cd89-4773-a46c-aab794fb7cbc' AS BINARY(16). When the trigger invokes the function, it will cast again the value of NEW.ID to the type expected by your function. So, this is the value your function will receive:
SELECT CAST(CAST('323febe6-cd89-4773-a46c-aab794fb7cbc' AS BINARY(16)) AS CHAR);
323febe6-cd89-47
As you can see, your function receives the value truncated. The results you are getting are equivalent to:
SELECT UUID_DECODE(UUID_ENCODE('323febe6-cd89-47'));
000000000000-0000-0032-3feb-e6cd8947
UPDATE
One way to implement the desired functionality in a trigger would be to add a dummy nullable column with the type your function expects:
CREATE TABLE test (ID BINARY(16) KEY DEFAULT 0, CHARID CHAR(36) NULL);
CREATE TRIGGER test_uuid_encode BEFORE INSERT ON test FOR EACH ROW BEGIN
SET NEW.ID = UUID_ENCODE(NEW.CHARID)
, NEW.CHARID = NULL;
END
This way you can do:
INSERT test (CHARID) VALUES ('323febe6-cd89-4773-a46c-aab794fb7cbc');
SELECT UUID_DECODE(ID) FROM test;
+--------------------------------------+
| uuid_decode(id) |
+--------------------------------------+
| 323febe6cd89-4773-a46c-aab7-94fb7cbc |
+--------------------------------------+
I created a fiddle here.
Note that:
Although CHARID accepts NULLs, ID does not, so an attempt to insert a NULL value in CHARID would result in the trigger trying to set ID to NULL and the insertion being rejected.
Attempting to insert an invalid value into CHARID that causes UUID_ENCODE to return NULL will also fail.
The DEFAULT 0 clause for ID is there only to allow you to omit the column from the insert list. The value written to the table will always be the one generated by your trigger.
A nullable column that is always NULL should take between 0 and 2 bytes of additional storage per row depending on your table layout and row format (you would need to avoid for instance the FIXED format of the MyISAM engine).
There are many variations possible. If you don't mind the extra storage, you can keep the CHARID value without setting it to NULL. If you want to allow explicit insert of binary ID values you can add a check to the trigger so you compute NEW.ID only if it is 0, etc.
You should have a similar trigger for UPDATE operations, if they are allowed.
I'm trying to create a cleaned-up table that replaces a varchar(50) field with an integer. The original field has occasional text values that I'd like to convert to 0 or null values.
I can do this in a select statement just fine, but get an error when trying to create my table.
Below is a minimal example that uses strings:
/* This works */
DROP TABLE IF EXISTS mytest;
CREATE TABLE mytest
AS
SELECT convert("123", unsigned) AS mynum;
/* This works, returning a row of 0 */
SELECT convert("TEST", unsigned) AS mynum;
/* But this fails, with: Truncated incorrect INTEGER value: 'TEST'*/
DROP TABLE IF EXISTS mytest;
CREATE TABLE mytest
AS
SELECT convert("TEST", unsigned) AS mynum;`
What is wrong with the above, and is there a better way to accomplish what I want? Thanks for the help.
I don't have an explanation for why that error occurs, but I found a workaround using a subquery:
DROP TABLE IF EXISTS mytest;
CREATE TABLE mytest
AS
SELECT mynum
FROM (SELECT convert("TEST"), unsigned) AS mynum) t;
Demo: http://www.sqlfiddle.com/#!2/4909a/1
I'd use a case statement for it and also switch over to using CAST instead of convert so you'll get failures instead of implicit conversions.
CREATE TABLE mytest(myVarchar varchar(10));
INSERT mytest VALUES ('123'),('TEST');
SELECT CASE
WHEN myVarchar REGEXP '^[0-9]+$' THEN CAST(myVarchar,unsigned)
ELSE 0
END As mynum
FROM mytest;
I don't have a mysql instance handy to test this so hopefully I didn't goof any syntax errors.
Rather than convert, you could update the data and then alter the table:
UPDATE mytest SET mynum=mynum + 0;
ALTER TABLE mytest CHANGE COLUMN mynum mynum INT UNSIGNED;
It looks like you are implicitly defining the column name by the select statement in the create. This may be assuming the "TEST" string is the datatype. It would help to see the error message you get, but my assumption would be to explicitly define the column as:
CREATE TABLE mytest(mynum int);
Then
Insert into mytest(mynum)
select convert("TEST",unsigned) as mynum;
This is also why the answer with the subquery may work, by the time it gets to the outer query it is implicitly defined as an int.
In MySQL I have a table with Column1 as NOT NULL:
create table myTable
(
Column1 int not null,
Column2 int not null
)
I can still insert an empty value like this:
INSERT INTO `myTable` ( `Column1` , `Column2` )
VALUES ( '66', '' );
How can I make the MySQL column also disallow blankstring?
EMPTY STRINGS
In ORACLE an empty string is used to represent NULL. In virtually everything else, however, an empty string is still a string, and so not NULL.
INTS
In your case you're actually inserting STRINGS into an INT column. This forces an implicit CAST operation.
When your RDBMS is converting the string '' to an INT it must get the value 0. As 0 is not NULL, this gets inserted.
A more valid test would be:
INSERT INTO `plekz`.`countries` (`Column1 ` , `Column2`)
VALUES (66, NULL);
EDIT
Sorry, I only half read your question. You also ask how to stop '' being inserted.
Your first problem is that you're inserting STRINGS and the table is defined as having INT fields. You can put constraints on the data that gets inserted, but these constraints will apply the the value after an conversion to an INT. Unless you want to prevent the value 0 from also being inserted, there is nothing you can do to the table to prevent this scenario.
Your better bet is to address why you are inserting strings in the first place. You could use a stored procedure that takes, and checks, the strings before converting them to INTs and then inserting them. Or, better still, you could make the checks in your client application.
A technically available option is to make the fields CHAR fields, then put a constraint on the fields, preventing '' from being inserted. I would strongly recommend against this.
You're inserting an empty string, not NULL. The constraint is only against NULL values, and it would appear that your database is not coercing empty strings to NULL when it converts them to INT (which raises the additional question of why you're inserting string literals into INT columns...)
MySQL, how to disallow empty string:
Create your table:
mysql> create table yar (val VARCHAR(25) not null);
Query OK, 0 rows affected (0.02 sec)
Create your 'before insert' trigger to check for blankstring and disallow.
mysql> create trigger foo before insert on yar
-> for each row
-> begin
-> if new.val = '' then
-> signal sqlstate '45000';
-> end if;
-> end;$$
Query OK, 0 rows affected (0.01 sec)
Try to insert null and blankstring into your column:
mysql> delimiter ;
mysql> insert into yar values("");
ERROR 1644 (45000): Unhandled user-defined exception condition
mysql> insert into yar values(NULL);
ERROR 1048 (23000): Column 'val' cannot be null
mysql> insert into yar values ("abc");
Query OK, 1 row affected (0.01 sec)
mysql> select * from yar;
+-----+
| val |
+-----+
| abc |
+-----+
1 row in set (0.00 sec)
Finally, Grumble to self and smack the nearest person who was responsible for picking mysql over postgresql.
As Martin mentions, depends on your RDBMS. Oracle treats empty strings as NULLs while others do not. See this SO post.
NULL is not equal to emptiness. In MySQL, there is an additional byte with each column entry to hold the "is null" information. To save space, a column is often defined as "not null" to spare this extra byte if the null status doesn't add any thing to the data model.