Converting text column to integer in MySQL - mysql

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.

Related

Create table with some column's type defined as some function's returning type

The following SQL statement will create a table t, with a column c1, whose type is defined as bigint(21) unsigned, which is the returning type of function INET_ATON:
CREATE TABLE t AS SELECT INET_ATON('0.0.0.0') AS c1;
But the statement also inserts a row. I wonder if there is a way to create the table and make its columns' type defined as some function's returning type, without inserting a row.
I tried the following:
CREATE TABLE t LIKE SELECT INET_ATON('0.0.0.0') AS c1;
But this doesn't match the SQL syntax.
Any ideas on how to achieve this? Thanks!
PS: Any standard SQL statement is OK, the MariaDB or MySQL supported statement is also OK.
use where clause to create a condition which will never be true like below:
CREATE TABLE t AS SELECT INET_ATON('0.0.0.0') AS c1 where 1=2;
This will create the table without adding any rows into it.

Use auto incremented ID in another column

I have two columns in my DB table, where the first one is auto incremented ID. In the second one I would like to have that ID mirrored. (I know it sound like design error, but I really need it)
Is it possible to configure it that way?
If you're using MySql 5.7.6 or later you can define the second id as a virtual column like this:
CREATE TABLE my_table (
id INT UNSIGNED AUTO_INCREMENT,
id_mirrored INT UNSIGNED AS (id)
);
This way the id_mirrored column isn't really stored in your database but instead it's evaluated when the row is read.
If you're using an earlier version of MySql creating a view is probably your best option. Views are basically virtual tables.
CREATE VIEW my_view AS
SELECT
t.id AS id,
t.id AS id_mirrored
FROM my_table t
Third option is to define the id_mirrored as a real column and add a trigger to give it it's value. The way to do this has already been described in other answers.
use TRIGGER
DELIMITER |: # switch delimiter to prevent execution of ;
CREATE TRIGGER `copy_id2c_name` BEFORE INSERT ON tb_name
FOR EACH ROW BEGIN
SELECT AUTO_INCREMENT INTO #AI FROM information_schema.tables WHERE table_schema = 'db_name' and table_name = 'tb_name';
set NEW.c_name = #AI;
END;
|: # execute code
DELIMITER ; // switch back original delimiter
You could use smth like LAST_INSERT_ID() in case you are using stored procedure.

MySQL Trigger INSERT copy's rows

I am trying to create a trigger (this is my first trigger, and question, so be gentle) that will insert new rows into two different tables.
* Edit *
Adding this in as I forgot to mention it until ypercube answered.
I am trying to avoid listing all of the column names, as in the real world usage the table this will be used on has a very large number of columns (not my design, too late to refactor).
* End Edit *
Here's what I have so far.
CREATE TABLE test_table (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
message VARCHAR(255)
);
CREATE TABLE test_table_copy LIKE test_table;
DELIMITER $$
CREATE TRIGGER copy_test_table_data AFTER INSERT ON test_table
FOR EACH ROW
BEGIN
INSERT INTO test_table_copy SELECT * FROM NEW;
END;$$
DELIMITER ;
Unfortunately this results in an error.
mysql> INSERT INTO test_table VALUES (1, 'This is a message');
ERROR 1146 (4502): Table 'test_db.NEW' doesn't exist
I am not quite sure what the problem is, I thought NEW referred the table data was being inserted into?
You could possibly get a list of column names in that table from information_schema views, then use them to create a prepared statement (using cursor to iterate column names) and CONCAT() function to glue together the query string. Then execute the prepared statement.
Seems very contrived even to me, and I'm not sure it would work (and if it did, how efficient it would be)

Cannot access temporary tables from within a function

I would like to get count of specific records. So my query will look like the following...
SELECT
ID,
NAME,
(SELECT...) AS UserCount // Stmt1
FROM MyTable
The issue is that, 'Stmt1' is a complex statement and it cannot be written as innerquery.
Well, I can use functions, but the statement includes 'CREATE TABLE' so I get the following error message
Cannot access temporary tables from within a function.
What is the best way to accomplish the task ?
You can use user defined table type to solve your problem.
You just create a table variable like
CREATE TYPE [dbo].[yourTypeName] AS TABLE(
[columeName1] [int] NULL,
[columeName2] [varchar](500) NULL,
[columeName3] [varchar](1000) NULL
)
GO
and you can declare this table variable in your function like
CREATE FUNCTION [dbo].[yourFunctionName]
(
#fnVariable1 INT ,
#yourTypeNameVariable yourTypeName READONLY
)
RETURNS VARCHAR(8000)
AS
BEGIN
SELECT .................
FROM #yourTypeNameVariable
WHERE ........
RETURN #r
END
On your procedure you can declare your table type like
DECLARE #yourTypeNamevaribale AS yourTypeName
And you can insert values to this table like
insert into #yourTypeNamevaribale (col,col,..)values(val,val,..)
pass this to your function like
dbo.yourFunctionName(fnVariable1 ,#yourTypeNamevaribale )
please go for this method, thank you
Yes you can not use #temp table.
As you are using SQL Server 2008, why don't you use table variable instead of #temp tables?
Give it a try.
I came across this post as I started using table variables and switched to temporary tables for performance reasons only to find temporary tables couldn't be used in a function.
I would be hesitant about using table variables especially if you are playing with large result sets, as these are held in memory. See this post...
http://totogamboa.com/2010/12/03/speed-matters-subquery-vs-table-variable-vs-temporary-table/
Other alternatives would be..
Extracting the temporary table result into another table function.
Converting the code into using sub-queries
In 99,99% of cases there is no need for any tricks with temp tables or subqueries, but use aggregation functions like COUNT, SUM or AVG in combination with OVER clause and (often) PARTITION BY.
I am not sure what the OP tried to achieve but I assume that the UserCount is somehow related to the values in MyTable. So there must be a way to join MyTable to whatever table that produces UserCount.
The most simple example is to show all users and the total number of users
SELECT id
, name
, user_count = COUNT(*) OVER()
FROM MyUsers

User-defined Table Variables in MySQL 5.5?

I've recently moved from MSSQL to MySQL.
I would like to use a table variable (or equivalent) inside a MySQL 5.5 stored routine, to populate a dataset for an online report.
In MS SQL, I would do it this way
...
...
DECLARE #tblName TABLE
WHILE <condition>
BEGIN
Insert Row based on iteration value
END
...
...
From what I understand, I can't declare table variables in MySQL (correct me if I'm wrong) How do I implement the above logic in a MySQL stored procedure?
You could create a table or temporary table and populate it with data you need.
CREATE TABLE Syntax
You understand that limitation correctly. The MySQL user manual clearly states that user-defined variables cannot refer to a table:
http://dev.mysql.com/doc/refman/5.5/en/user-variables.html
User variables are intended to provide data values. They cannot be used directly in an SQL statement as an identifier or as part of an identifier, such as in contexts where a table or database name is expected, or as a reserved word such as SELECT.
create temporary table tmp
(
id int unsigned not null,
name varchar(32) not null
)
engine=memory; -- change engine type if required e.g myisam/innodb
insert into tmp (id, name) select id, name from foo... ;
-- do more work...
select * from tmp order by id;
drop temporary table if exists tmp;
I think this covers it. Also, this may be helpful.