I have a Table Structure as below
CREATE TABLE `eatables` (
`Sno` int(11) NOT NULL auto_increment,
`Name` varchar(255) collate latin1_general_ci default NULL,
PRIMARY KEY (`Sno`)
);
The Table contains Rows as below
insert into `eatables`(`Sno`,`Name`) values (1,'Apples');
insert into `eatables`(`Sno`,`Name`) values (2,'Oranges');
insert into `eatables`(`Sno`,`Name`) values (3,'Papaya');
insert into `eatables`(`Sno`,`Name`) values (4,'Jackfruit');
insert into `eatables`(`Sno`,`Name`) values (5,'Pineapple');
insert into `eatables`(`Sno`,`Name`) values (6,'Mango');
I created a Procedure to get the Count based on Name as Constraint
DROP PROCEDURE IF EXISTS proc_fruit_count;
CREATE PROCEDURE mp_user_preference(pFruitName VARCHAR(255))
BEGIN
SELECT #lngCount = COUNT(Sno)
FROM eatables
WHERE Name = pFruitName;
SELECT #lngCount;
END
But my Procedure is returning Null every time I execute it.
You have to modify your stored procedure! You also need to use IN keyword:
CREATE PROCEDURE mp_user_preference(IN pFruitName VARCHAR(255))
BEGIN
SELECT #lngCount = COUNT(Sno)
FROM eatables
WHERE Name = pFruitName;
SELECT #lngCount;
END
See http://www.mysqltutorial.org/stored-procedures-parameters.aspx
Edit:If you want to return lngCount you can modify the stored procedure as follow:
CREATE PROCEDURE mp_user_preference(IN pFruitName VARCHAR(255), OUT toReturn INT)
BEGIN
SELECT #lngCount = COUNT(Sno)
FROM eatables
WHERE Name = pFruitName
INTO toReturn;
END
You don't need a stored procedure here. You can solve this conveniently with a prepared statement.
prepare stmt from 'select count(*) from eatables where name = ?';
set #whatever = 'Mango';
execute stmt using #whatever; /* #whatever replaces the ? in the query above */
/* and if you don't need the prepared statement any more you do... */
deallocate prepare stmt;
Read more about prepared statements here.
Related
In MySQL offcial document for count,there are descriptions as below:
For MyISAM tables, COUNT(*) is optimized to return very quickly if the SELECT retrieves from one > table, no other columns are retrieved, and there is no WHERE clause. For example:
mysql> SELECT COUNT(*) FROM student;
This optimization only applies to MyISAM tables, because an exact row count is stored for this >storage engine and can be accessed very quickly. COUNT(1) is only subject to the same >optimization if the first column is defined as NOT NULL.
I want to test it for myself, and make a test as below,I make a table called system_user and the first column is type,all the value of type is null,however when I use SELECT COUNT(1) and SELECT COUNT(*) to query, I found the time cost is nearly the same,even if tried for serval times.
I am wonder why the first column is null and the optimization in MyISAM is still working?
When I use SQL_NO_CACHE,the time cost is still nearly the same:
related table:
CREATE TABLE `system_user` (
`type` varchar(10) DEFAULT NULL,
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(8) NOT NULL,
`age` int(11) DEFAULT NULL,
`tag` varchar(8) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
related procdure to create massive data:
DELIMITER $$
USE `test`$$
DROP PROCEDURE IF EXISTS `add_user_batch`$$
CREATE DEFINER=`root`#`%` PROCEDURE `add_user_batch`(IN COUNT INT)
BEGIN
DECLARE i INT;
DECLARE t_name VARCHAR(8);
DECLARE t_tag VARCHAR(20);
DECLARE t_age INT(2);
DECLARE t_sql_template VARCHAR(100);
DECLARE t_sql TEXT;
DECLARE t_tag_mod_val INT DEFAULT(25);
DECLARE t_commit_mod_val INT DEFAULT(100);
DECLARE t_start_time DATETIME;
DECLARE t_end_time DATETIME;
TRUNCATE TABLE `system_user`;
SET t_start_time=NOW();
SET t_sql_template = “INSERT INTO `system_user`(NAME, age, tag) VALUES“;
SET t_sql = t_sql_template;
SET i = 1;
WHILE i <= COUNT
DO
SET t_age = FLOOR(1 + RAND() * 60);
SET t_name = LEFT(UUID(), 8);
IF MOD(i, t_tag_mod_val) = 0 THEN
SET t_tag = “NULL“;
ELSE
SET t_tag = CONCAT(“'“,LEFT(UUID(), 8),“'“);
END IF;
SET t_sql = CONCAT(t_sql,“('“,t_name,“',“,t_age,“,“,t_tag,“)“);
IF MOD(i,t_commit_mod_val) != 0 THEN
SET t_sql = CONCAT(t_sql,“,“);
ELSE
SET t_sql = CONCAT(t_sql,“;“);
SET #insert_sql = t_sql;
PREPARE stmt FROM #insert_sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
COMMIT;
SET t_sql=t_sql_template;
END IF;
SET i = i + 1;
END WHILE;
IF LENGTH(t_sql) > LENGTH(t_sql_template) THEN
SET t_sql=CONCAT(SUBSTRING(t_sql,1,LENGTH(t_sql)-1),';');
SET #insert_sql = t_sql;
PREPARE stmt FROM #insert_sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
COMMIT;
END IF;
SET t_end_time=NOW();
SELECT CONCAT('insert data success,time cost ',TIMEDIFF(t_end_time,t_start_time)) AS finishedTag;
END$$
DELIMITER ;
To not publicly disclose our amount of invoices, we want to add random value between 2 ids.
Instead of [1,2,3] we want something like [69,98,179]
UUID is not an option in that project, unfortunately.
Using Mysql 5.7, 8, or MariaDb get the same results.
Here is the approach is taken:
Consider a simple table invoices as follows:
CREATE TABLE `invoices` (
`id` bigint(20) NOT NULL,
`name` varchar(255) DEFAULT NULL,
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=42 DEFAULT CHARSET=utf8mb4;
The function to get random values:
DROP FUNCTION IF EXISTS random_integer;
CREATE FUNCTION random_integer(value_minimum INT, value_maximum INT)
RETURNS INT
LANGUAGE SQL
NOT DETERMINISTIC
RETURN FLOOR(value_minimum + RAND() * (value_maximum - value_minimum + 1));
The function to get the next id:
DROP FUNCTION IF EXISTS next_invoice_id_val;
DELIMITER //
CREATE FUNCTION next_invoice_id_val ()
RETURNS BIGINT(8)
LANGUAGE SQL
NOT DETERMINISTIC
BEGIN
DECLARE lastId BIGINT(8) DEFAULT 1;
DECLARE randId BIGINT(8) DEFAULT 1;
DECLARE newId BIGINT(8) DEFAULT 1;
DECLARE nextId BIGINT(8) DEFAULT 1;
SELECT (SELECT MAX(`id`) FROM `invoices`) INTO lastId;
SELECT (SELECT random_integer(1,10)) INTO randId;
SELECT ( lastId + randId ) INTO nextId;
IF lastId IS NULL
THEN
SET newId = randId;
ELSE
SET newId = nextId;
END IF;
RETURN newId;
END //
DELIMITER ;
SELECT next_invoice_id_val();
and the trigger:
DROP TRIGGER IF EXISTS next_invoice_id_val_trigger;
DELIMITER //
CREATE TRIGGER next_invoice_id_val_trigger
BEFORE INSERT
ON invoices FOR EACH ROW
BEGIN
SET NEW.id = next_invoice_id_val();
END//
DELIMITER ;
That work like a charm, now if we want to generalize the behaviour to all tables.
We need a procedure to execute the query on any specific tables:
DROP PROCEDURE IF EXISTS last_id;
DELIMITER //
CREATE PROCEDURE last_id (IN tableName VARCHAR(50), OUT lastId BIGINT(8))
COMMENT 'Gets the last id value'
LANGUAGE SQL
NOT DETERMINISTIC
READS SQL DATA
BEGIN
SET #s := CONCAT('SELECT MAX(`id`) FROM `',tableName,'`');
PREPARE QUERY FROM #s;
EXECUTE QUERY;
DEALLOCATE PREPARE QUERY;
END //
DELIMITER ;
CALL last_id('invoices', #nextInvoiceId);
SELECT #nextInvoiceId;
The procedure for the next id value:
DROP PROCEDURE IF EXISTS next_id_val;
DELIMITER //
CREATE PROCEDURE next_id_val (IN tableName VARCHAR(50), OUT nextId BIGINT(8))
COMMENT 'Give the Next Id value + a random value'
LANGUAGE SQL
NOT DETERMINISTIC
READS SQL DATA
BEGIN
DECLARE randId BIGINT(8) DEFAULT 1;
SELECT (SELECT random_integer(1,10)) INTO randId;
CALL last_id(tableName, #currentId);
IF #currentId IS NULL
THEN
SET nextId = randId;
ELSE
SELECT ( #currentId + randId ) INTO nextId;
END IF;
END //
DELIMITER ;
CALL next_id_val('invoices', #nextInvoiceId);
SELECT #nextInvoiceId;
and the trigger:
# Call the procedure from a trigger
DROP TRIGGER IF EXISTS next_invoice_id_val_trigger;
DELIMITER //
CREATE TRIGGER next_invoice_id_val_trigger
BEFORE INSERT
ON invoices FOR EACH ROW
BEGIN
CALL next_id_val('invoices', #nextInvoiceId);
SET NEW.id = #nextInvoiceId;
END//
DELIMITER ;
and we get => Dynamic SQL is not allowed in stored function or trigger
I've read that storing in a temporary table might be a workaround, but as all posts have between 5 to 10 years old, I think we might have a better solution for such a straightforward case.
What is the workaround for using dynamic SQL in a stored Procedure
#1336 - Dynamic SQL is not allowed in stored function or trigger
Calling stored procedure that contains dynamic SQL from Trigger
Alternatives to dynamic sql in stored function
I designed a system which received request with ResortId, DayId, SkierId, Time, LiftId.
I created a table called day_resort with ResortId, DayId, and a reference which equals the concatenation of ResortId and DayId. When I received a request with Reference (ResortId + DayId) exists in the day_resort. I will simply find the table with name = Reference and insert SkierId, Time, and LiftId there. On the contrary, if I don't find certain Reference in day_resort, I will create a table name after ResortId + DayId, and I will insert the record with ResortId, DayId, ResortId + DayId into day_resort, after that, I will go to the table name after ResortId + DayId and insert a record with SkierId, Time, and LiftId.
Here is the example I tried:
DELIMITER //
DROP PROCEDURE IF EXISTS createLogTable;
CREATE PROCEDURE createLogTable(tblName VARCHAR(255))
BEGIN
SET #tableName = tblName;
SET #sql := CASE
WHEN NOT EXISTS (SELECT * FROM day_resort WHERE Reference = #tableName) THEN
'INSERT INTO day_resort (ResortId, DayId, Reference) VALUES ("Marriot", "21", "Marriot21")';
ELSE
CONCAT('INSERT INTO `' , #tableName, '` (SkierId, TIme, LiftId) VALUES ("111", "222", "333")');
END;
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
CREATE TABLE IF NOT EXISTS #tableName (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
SkierId VARCHAR(255) NOT NULL
Time VARCHAR(255) NOT NULL,
LiftId VARCHAR(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
END //
I got an error of Error Code: 2014 Commands out of sync; you can't run this command now
Thanks for any help
I created this trigger:
CREATE TABLE `Invitations`
( `userId` varchar(255) NOT NULL, `name` varchar(255) DEFAULT NULL,
`status` varchar(1) DEFAULT NULL, `userId_inv` varchar(255) NOT NULL,
PRIMARY KEY (`userId`,`userId_inv`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1
CREATE TRIGGER `Invitation_001_after_trig` AFTER INSERT ON `invitation_001`
FOR EACH ROW
BEGIN
IF ((new.userId,'001') NOT EXISTS (SELECT `userId`,`userId_inv` FROM `Invitations`
WHERE `userId`= new.userId AND `userId_inv` = '001' LIMIT 1))
THEN
INSERT INTO `Invitations`(`userId`, `name`,`status`,`userId_inv`)
values (new.userId, new.name,new.status,'001');
DELETE FROM `invitation_001` WHERE `status` = 'a';
END IF;
END;
It did not work. I had this error:
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 'EXISTS (SELECT userId,userId_inv FROM Invitations WHERE
userId= new.user' at line 1
So could you help me.
I resolved the problem with this trigger:
CREATE TRIGGER `Invitation_001_after_trig` AFTER INSERT ON `invitation_001`
FOR EACH ROW
BEGIN
INSERT INTO `Invitations` (`userId`, `name` ,`status` ,`userId_inv`) SELECT * FROM
invitation_001 iv WHERE
NOT EXISTS (SELECT 1 FROM `Invitations` ivs WHERE ivs.`userId` = iv.`userId` AND
ivs.`userId_inv` = iv.`userId_inv`) AND iv.`userId_inv`='001';END;
Now I've an other issue. I would like to execute a query from a Concat String in a trigger but until now I did not succeed so here is my code:
CREATE TRIGGER `InvitationS_after_trig` AFTER INSERT ON `Invitations`
FOR EACH ROW
BEGIN
if(new.status ='c' OR new.status ='r')
BEGIN
SET #query = CONCAT("INSERT INTO", Invetations.userId,"values(new.userId,new.name,new.status,new.userId_inv);");
PREPARE stmt1 FROM #query;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
END IF;
END;
I resolved the syntax problem but I now have an other one:
Dynamic SQL is not allowed in stored function or trigger
I'm using MySQL version 5.1.69.
Finally I give up, I will resolve this problem using java Fonction
In MySQL, I have a table, and I want to set the auto_increment value to 5 instead of 1. Is this possible and what query statement does this?
You can use ALTER TABLE to change the auto_increment initial value:
ALTER TABLE tbl AUTO_INCREMENT = 5;
See the MySQL reference for more details.
Yes, you can use the ALTER TABLE t AUTO_INCREMENT = 42 statement. However, you need to be aware that this will cause the rebuilding of your entire table, at least with InnoDB and certain MySQL versions. If you have an already existing dataset with millions of rows, it could take a very long time to complete.
In my experience, it's better to do the following:
BEGIN WORK;
-- You may also need to add other mandatory columns and values
INSERT INTO t (id) VALUES (42);
ROLLBACK;
In this way, even if you're rolling back the transaction, MySQL will keep the auto-increment value, and the change will be applied instantly.
You can verify this by issuing a SHOW CREATE TABLE t statement. You should see:
> SHOW CREATE TABLE t \G
*************************** 1. row ***************************
Table: t
Create Table: CREATE TABLE `t` (
...
) ENGINE=InnoDB AUTO_INCREMENT=43 ...
How to auto increment by one, starting at 10 in MySQL:
create table foobar(
id INT PRIMARY KEY AUTO_INCREMENT,
moobar VARCHAR(500)
);
ALTER TABLE foobar AUTO_INCREMENT=10;
INSERT INTO foobar(moobar) values ("abc");
INSERT INTO foobar(moobar) values ("def");
INSERT INTO foobar(moobar) values ("xyz");
select * from foobar;
'10', 'abc'
'11', 'def'
'12', 'xyz'
This auto increments the id column by one starting at 10.
Auto increment in MySQL by 5, starting at 10:
drop table foobar
create table foobar(
id INT PRIMARY KEY AUTO_INCREMENT,
moobar VARCHAR(500)
);
SET ##auto_increment_increment=5;
ALTER TABLE foobar AUTO_INCREMENT=10;
INSERT INTO foobar(moobar) values ("abc");
INSERT INTO foobar(moobar) values ("def");
INSERT INTO foobar(moobar) values ("xyz");
select * from foobar;
'11', 'abc'
'16', 'def'
'21', 'xyz'
This auto increments the id column by 5 each time, starting at 10.
You can also do it using phpmyadmin. Just select the table than go to actions. And change the Auto increment below table options. Don't forget to click on start
Procedure to auto fix AUTO_INCREMENT value of table
DROP PROCEDURE IF EXISTS update_auto_increment;
DELIMITER //
CREATE PROCEDURE update_auto_increment (_table VARCHAR(64))
BEGIN
DECLARE _max_stmt VARCHAR(1024);
DECLARE _stmt VARCHAR(1024);
SET #inc := 0;
SET #MAX_SQL := CONCAT('SELECT IFNULL(MAX(`id`), 0) + 1 INTO #inc FROM ', _table);
PREPARE _max_stmt FROM #MAX_SQL;
EXECUTE _max_stmt;
DEALLOCATE PREPARE _max_stmt;
SET #SQL := CONCAT('ALTER TABLE ', _table, ' AUTO_INCREMENT = ', #inc);
PREPARE _stmt FROM #SQL;
EXECUTE _stmt;
DEALLOCATE PREPARE _stmt;
END//
DELIMITER ;
CALL update_auto_increment('your_table_name')
If you need this procedure for variable fieldnames instead of id this might be helpful:
DROP PROCEDURE IF EXISTS update_auto_increment;
DELIMITER //
CREATE PROCEDURE update_auto_increment (_table VARCHAR(128), _fieldname VARCHAR(128))
BEGIN
DECLARE _max_stmt VARCHAR(1024);
DECLARE _stmt VARCHAR(1024);
SET #inc := 0;
SET #MAX_SQL := CONCAT('SELECT IFNULL(MAX(',_fieldname,'), 0) + 1 INTO #inc FROM ', _table);
PREPARE _max_stmt FROM #MAX_SQL;
EXECUTE _max_stmt;
DEALLOCATE PREPARE _max_stmt;
SET #SQL := CONCAT('ALTER TABLE ', _table, ' AUTO_INCREMENT = ', #inc);
PREPARE _stmt FROM #SQL;
EXECUTE _stmt;
DEALLOCATE PREPARE _stmt;
END //
DELIMITER ;
CALL update_auto_increment('your_table_name', 'autoincrement_fieldname');
just export the table with data ..
then copy its sql like
CREATE TABLE IF NOT EXISTS `employees` (
`emp_badgenumber` int(20) NOT NULL AUTO_INCREMENT,
`emp_fullname` varchar(100) NOT NULL,
`emp_father_name` varchar(30) NOT NULL,
`emp_mobile` varchar(20) DEFAULT NULL,
`emp_cnic` varchar(20) DEFAULT NULL,
`emp_gender` varchar(10) NOT NULL,
`emp_is_deleted` tinyint(4) DEFAULT '0',
`emp_registration_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`emp_overtime_allowed` tinyint(4) DEFAULT '1',
PRIMARY KEY (`emp_badgenumber`),
UNIQUE KEY `bagdenumber` (`emp_badgenumber`),
KEY `emp_badgenumber` (`emp_badgenumber`),
KEY `emp_badgenumber_2` (`emp_badgenumber`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=111121326 ;
now change auto increment value and execute sql.