MySQL conditionally create a table - mysql

Is it possible to do a conditional create table in MySql based on the result of a select statement? Essentially, if I have a schema used by multiple clients but only some of them need a table can I create it based on another value...for example...if I have a user John then Create this table...because John's app needs it.
Something along the lines of:
IF EXISTS (SELECT * FROM USERS WHERE name='JOHN')
CREATE TABLE CREATE_THIS
(
ID VARCHAR(255) COLLATE utf8_bin NOT NULL,
NAME VARCHAR(255) COLLATE utf8_bin NOT NULL
)
ENGINE=MyISAM DEFAULT CHARSET=utf8;

This sort of business logic does not really belong within the database layer of your application. It would normally reside at a higher level, within whatever code you use to connect to the database.
However, for completeness, there is a (very hackish) way to accomplish what you ask by preparing a statement from a string that contains the SQL to be executed:
SET #sql := CASE WHEN EXISTS (SELECT * FROM USERS WHERE name='JOHN') THEN
'CREATE TABLE CREATE_THIS (
ID VARCHAR(255) COLLATE utf8_bin NOT NULL,
NAME VARCHAR(255) COLLATE utf8_bin NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8'
END;
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

You can also use
CREATE Table IF NOT EXISTS <Table_Name> ( ....
...)

Related

How to use mysql unix_timestamp() as the starting point for auto increment?

I would like to know if it is possible do create a table having the auto increment start with the value of the unix timestamp (the time of create of the table), i.e.
create table something(
something_id bigint not null primary key auto_increment,
something_name varchar(10) not null,
something_random varchar(3) not null
) engine=InnoDB auto_increment=round(unix_timestamp(curtime(4)) * 1000);
Note that I can just replace auto_increment=round(unix_timestamp(curtime(4)) * 1000) with the value of select round(unix_timestamp(curtime(4)) * 1000);. But what I want is a way to do it automatically when creating my tables.
After reading mysql select section everything I tried gave me a compiler error.
Thanks.
One way could be to use a stored procedure that does this.
Procedure that receives the table creation command and concatenates (CONCAT function) the value of the expression at the end. Since the query is in string format, the Prepared Statement is used to execute it.
DELIMITER $$
DROP PROCEDURE IF EXISTS `createTable`$$
CREATE PROCEDURE createTable(IN strCreateQuery TEXT)
BEGIN
SET #query = CONCAT(strCreateQuery, " auto_increment=", ROUND(UNIX_TIMESTAMP(CURTIME(4)) * 1000));
PREPARE stmt FROM #query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END$$
DELIMITER ;
Once created, simply execute the procedure with the creation query of the desired table.
CALL createTable("
create table something(
something_id bigint not null primary key auto_increment,
something_name varchar(10) not null,
something_random varchar(3) not null
) engine=InnoDB");

MYSQL stored procedure create dynamic table from date

I'm facing a little problem to create the table name dynamically.
For example ,
This query (SELECT DATE_FORMAT(NOW(),'%Y%m')) returns 201702
My motive is to check whether the table exist.
If it doesn't exist , it will create it automatically.
Now here's my problem.
messages_`,datereturn
It seems I'm getting error for even compiling. How do we pass parameter to create the table?
Really appreciate your help.
Full Code:
BEGIN
SET datereturn = (SELECT DATE_FORMAT(NOW(),'%Y%m'));
CREATE TABLE IF NOT EXISTS `messages_`,datereturn (
`mid` bigint(17) NOT NULL,
`uid` int(11) NOT NULL,
`csid` int(11) NOT NULL,
`content` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`content_type` tinyint(4) NOT NULL,
`datecreated` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`datecreated1` bigint(13) DEFAULT NULL,
PRIMARY KEY (`mid`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
END
EDIT: it's different than the answer proposed.
I'm making the table dynamic with MySQL 5.6.
The above suggested thread doesn't work for my version.
So, I don't know why it's a possible of duplicate.
Stored procedures don't allow you to combine variables into SQL identifiers. In other words, things like table names must be set before the query is parsed.
So you need to create a dynamic SQL statement and make the stored procedure prepare the CREATE TABLE statement at runtime:
BEGIN
SET #sql = CONCAT('CREATE TABLE IF NOT EXISTS messages_',datereturn,' (',
'mid bigint NOT NULL, ',
'uid int NOT NULL, ',
'csid int NOT NULL, ',
'content varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, ',
'content_type tinyint NOT NULL, ',
'datecreated timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, ',
'datecreated1 bigint DEFAULT NULL, ',
'PRIMARY KEY (mid)',
') ENGINE=InnoDB DEFAULT CHARSET=latin1');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END
(Note: I have not tested this code, so if I missed a quote, I expect you know how to fix that.)
In this example, I also removed your back-ticks because they're not necessary, and I removed the length options for int types because they have no effect.
All that said, I have to question why you need a table per date. Seems like a code smell to me. Have you considered creating a table partitioned by date instead?
SET #CrtTbl = CONCAT('CREATE TABLE', CONCAT('`messages_', datererun), '(mid bigint(17) NOT NULL, .... and so on
...
PREPARE CrtTblStmt FROM #CrtTbl;
EXECUTE CrtTblStmt;
DEALLOCATE PREPARE CrtTblStmt;
something like that

how to create mysql table names based on the current month and year

I create mysql event script with the aim to create mysql tables automatically every month. I want the name of the tables based on the current month and year. How do I do that?
I am trying this:
CREATE DEFINER = `root`#`localhost` EVENT `event_name`
ON SCHEDULE EVERY 1 MONTH
ON COMPLETION NOT PRESERVE ENABLE
DO
CREATE TABLE IF NOT EXISTS `currentMonth_currentYear_tableName` (
`column1` INT( 11 ) NOT NULL ,
`column2` DATETIME NOT NULL ,
`column3` DECIMAL( 10, 1 ) NOT NULL ,
PRIMARY KEY ( `column1` )
) ENGINE = INNODB DEFAULT CHARSET = utf8
I haven't tested this from an Events declaration, but it seems to work in my query browser:
SET #SQL = CONCAT('CREATE TABLE IF NOT EXISTS ', DATE_FORMAT(NOW(),'%m_%Y'), '_tableName (
`column1` INT( 11 ) NOT NULL ,
`column2` DATETIME NOT NULL ,
`column3` DECIMAL( 10, 1 ) NOT NULL ,
PRIMARY KEY ( `column1` )
) ENGINE = INNODB DEFAULT CHARSET = utf8'
);
PREPARE stmt FROM #SQL;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
The idea for CONCAT/PREPARE/EXECUTE was taken from https://dba.stackexchange.com/a/14401
You can't use expressions or variables as identifiers in SQL. Identifiers names must be constant at the time you prepare the query.
To make a variable table name like you describe, you'll have use format an SQL statement as as string, and then use PREPARE and EXECUTE to run it.
In other words, the expression or variable must resolve to a constant identifier name before you prepare the statement.

Operand should contain 1 column(s)

Getting a Operand should contain 1 column(s) mysql error whenever I try to insert into the table sets.
I googled and found a hoard of similar questions but they are always pin point specific to solving their immediate problem. I have mysql 5.6 by the way. I am allowed multiple TIMESTAMPS.
Here is my code:
INSERT INTO `sets` (`tabler_name`) VALUES ("leads_auto");
Here is my table:
CREATE TABLE IF NOT EXISTS `lms`.`sets` (
`set_id` BIGINT NOT NULL AUTO_INCREMENT,
`on_off` SMALLINT NOT NULL DEFAULT 0,
`tabler_name` VARCHAR(45) NULL,
`origin_date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`last_modified_date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`original_count` INT NULL,
`current_count` INT NULL,
`source_type` VARCHAR(45) NULL,
`source` VARCHAR(45) NULL,
`method` VARCHAR(45) NULL,
`agent` VARCHAR(45) NULL,
`dupes` INT NULL,
`bads` INT NULL,
`aged` INT NULL COMMENT 'This table keeps track of the record sets that enter the system. Example: a set of leads imported into the database.',
PRIMARY KEY (`set_id`)
) ENGINE = InnoDB;
Stored Procedure:
DELIMITER //
CREATE PROCEDURE `lms`.`leads_to_bak` ()
BEGIN
SET #table1 = (SELECT `tabler_name` FROM sets WHERE on_off=0 LIMIT 1);
SET #table2 = CONCAT(#table1, '_bak');
SET #SQL1 = CONCAT('INSERT INTO ',#table2, '(', (SELECT
REPLACE(GROUP_CONCAT(COLUMN_NAME), 'lead_id,', '') FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #table2), ')', ' SELECT ', (SELECT REPLACE(GROUP_CONCAT(COLUMN_NAME), 'lead_id,', '') FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = #table1), ' FROM ', #table1);
PREPARE stmt FROM #sql1;
EXECUTE stmt;
END//
DELIMITER ;
USE `lms`;
Trigger
DELIMITER $$
USE `lms`$$
CREATE TRIGGER `lms`.`after_insert_into_leads`
AFTER INSERT ON `sets` FOR EACH ROW
BEGIN
IF (SELECT * FROM sets WHERE on_off=0 LIMIT 1) THEN
CALL lms.leads_to_bak();
END IF;
END$$
DELIMITER ;
USE `lms`;
I don't see anything wrong with my routines. Removing the routines and trigger seems to make the problem go away.
In your trigger, did you mean to put EXISTS after IF? Like this:
CREATE TRIGGER `lms`.`after_insert_into_leads`
AFTER INSERT ON `sets` FOR EACH ROW
BEGIN
IF EXISTS (SELECT * FROM sets WHERE on_off=0 LIMIT 1) THEN
CALL lms.leads_to_bak();
END IF;
END$$
Escape your table name, it seems to be a reserved function. I'm not sure if you've defined one locally.
INSERT INTO `sets` (tabler_name) VALUES ("leads_auto");
Also, you can't have two timestamp fields in a single database afaik. Change one of the two timestamps to a DATETIME field if it caused you issues as well
Besides escaping the field name in your INSERT-statement, it cannot be improved very much. But it doesn't generate any error in my test enviroment. Is this really the exact statement throwing you an error?
However, there's a slight problem in your table definition, it will throw you an
Incorrect table definition; there can be only one TIMESTAMP column
with CURRENT_TIMESTAMP in DEFAULT or ON UPDATE clause.
As the error message indicates, you can only use one timestamp column with CURRENT_TIMESTAMP, if you need more than one, you can do this using a trigger.

Mysql Prepare Statement error while executing

I am trying to create a table though prepare statement but it is giving me syntax error. Well if I try to execute the same statement individually then it works fine.
Here's my statement -
SET #Stmt1 = Concat('DROP TABLE IF EXISTS ',DB,'.`county`;\n'
'CREATE TABLE IF NOT EXISTS ',DB,'.`County`
(
`CountyID` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`CountyName` VARCHAR(45) NOT NULL,
`CountyCode` VARCHAR(30) NOT NULL,
PRIMARY KEY (`CountyID`)
)');
Prepare stmt2 From #stmt1;
Execute stmt2;
Please can anyone tell me what am I missing in this statement?
It is giving me an error on this line:
'CREATE TABLE IF NOT EXISTS ',DB,'.`County`
(
`CountyID` INT UNSIGNED NOT NULL AUTO_INCREMENT,
http://dev.mysql.com/doc/refman/5.1/en/prepare.html says:
The text [of the preparable statement] must represent a single SQL statement, not multiple statements.
So you'll have to execute your DROP TABLE statement first, and then prepare and execute the CREATE TABLE statement separately.
are you not missing a comma between the two strings in the concat?
should be
SET #Stmt1 = Concat('DROP TABLE IF EXISTS ',DB,'.county;\n', 'CREATE TABLE IF NOT EXISTS ',DB,'.County ( CountyID INT UNSIGNED NOT NULL AUTO_INCREMENT, CountyName VARCHAR(45) NOT NULL, CountyCode VARCHAR(30) NOT NULL, PRIMARY KEY (CountyID) )');