MYSQL: INSERT + ON DUPLICATE KEY UPDATE + SELECT in *one* prepared statement? [duplicate] - mysql

Is it possible to insert a row and get the values inserted in the same query?
Something like...
INSERT INTO `items` (`item`, `number`, `state`)
(SELECT '3', `number`, `state` FROM `item_bug` WHERE `id`='3')
And then, get ID and execute a
SELECT * FROM `items` WHERE `id`='$id'
But using only one query.

Execute your insert statement and then you can do this:
SELECT * FROM `items` WHERE `id`= LAST_INSERT_ID()

you can call a stored procedure which will perform the insert and return a resultset in a single call from your app layer to mysql:
Stored procedure call
mysql> call insert_user('bar');
+---------+----------+
| user_id | username |
+---------+----------+
| 1 | bar |
+---------+----------+
1 row in set (0.02 sec)
$sqlCmd = sprintf("call insert_user('%s')", ...);
Simple example:
drop table if exists users;
create table users
(
user_id int unsigned not null auto_increment primary key,
username varchar(32) unique not null
)
engine=innodb;
drop procedure if exists insert_user;
delimiter #
create procedure insert_user
(
in p_username varchar(32)
)
begin
declare v_user_id int unsigned default 0;
insert into users (username) values (p_username);
set v_user_id = last_insert_id();
-- do more stuff with v_user_id e.g. logs etc...
select * from users where user_id = v_user_id;
end#
delimiter ;
call insert_user('bar');

No, it's not possible in MySQL (unlike PostgreSQL, SQL Server and PL/SQL in Oracle).
You will have to do it in separate queries.

You can do this using multiple statements if you like to choose that route. Firstly when you connect to your database make sure that multiple statements is set to true:
var connection = mysql.createConnection({
host: databaseHost,
user: databaseUser,
password: databasePassword,
database: databaseName,
multipleStatements: true
});
Then you can just define your sql as:
var sql = "your insert statement; your select statement";
Just separate individual statements using the semi colon. Your select result will be results[1] in this example.

if you are using php then
instead of this you can use
mysql_insert_id();
which will give the id of last inserted record.
The other data will be same as inserted.
Only id will change which you can get by mysql_insert_id()
So you do not need to run second query.

Related

How to `SELECT FROM` a table that is a part of a query itself using MySQL?

Say, if I have multiple tables that have the same schema:
CREATE TABLE `tbl01`
(
`id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`name` TINYTEXT,
`data` INT
);
CREATE TABLE `tbl02`
(
`id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`name` TINYTEXT,
`data` INT
);
CREATE TABLE `tbl03`
(
`id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`name` TINYTEXT,
`data` INT
);
-- etc. ------------------
INSERT INTO `tbl01` (`name`, `data`) VALUES
('row 1', 1),
('row 2', 1),
('row 3', 3);
INSERT INTO `tbl02` (`name`, `data`) VALUES
('cube', 1),
('circle', 0);
INSERT INTO `tbl03` (`name`, `data`) VALUES
('one', 1);
and then one table that contains names of all other tables in one of its columns:
CREATE TABLE `AllTbls`
(
`id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`tblnm` VARCHAR(64) NOT NULL UNIQUE,
`desc` TINYTEXT,
`flgs` BIGINT UNSIGNED
);
INSERT INTO `AllTbls` (`tblnm`, `desc`, `flgs`) VALUES
('tbl01', 'Table 1', 0),
('tbl02', 'Table two', 1),
('tbl03', '3rd table', 0);
So if I want to write a query to retrieve contents of AllTbls and also in one column to include count of rows in each of corresponding tables, I thought the following would be the way to do it:
SELECT *, `tblnm` as TblName, (SELECT COUNT(*) FROM TblName) as cntRws
FROM `AllTbls` ORDER BY `id` ASC LIMIT 0,30;
But this returns an error:
#1146 - Table 'database.TblName' doesn't exist
I know that I can do this in multiple queries (using a loop in a programming language), but is it possible to do it in one query?
PS. I'm using MySQL v.5.7.28
The simple answer is: "you can't"
Table names are not supposed to be used like variables, to hold data, in this way. What you're supposed to have is one table:
tblContractCounts
Client, ContractCount
-------------------
IBM, 1
Microsoft, 3
Google, 2
Not three tables:
tblIBMContractCounts
ContractCount
1
tblMicrosoftContractCounts
ContractCount
3
tblGoogleContractCounts
ContractCount
2
If your number of tables is known and fixed you can perhaps remedy things by creating a view that unions them all back together, or embarking on an operation to put them all into one table, with separate views named the old names so things carry in working til you can change them. If new tables are added all the time it's a flaw in the data modelling and need to be corrected. In that case you'd have to use a programming language (front end or stored procedure) to build a single query:
//pseudo code
strSql = ""
for each row in dbquery("Select name from alltbls")
strSql += "select '" + row.name + "' as tbl, count(*) as ct from " + row.name + " union all "
next row
strSql += "select 'dummy', 0"
result = dbquery(strSql)
It doesn't have to be your front end that does this - you could also do this in mysql and leverage the dynamic sql / EXECUTE. See THIS ANSWER how we can concatenate a string using logic like above so that the string contains an sql query and then execute the query. The information schema will give you the info you need to get a list of all current table names
But all you're doing is working around the fact that your data modelling is broken; I recommend to fix that instead
ps: the INFORMATION_SCHEMA has rough counts for tables with their names, which may suffice for your needs in this particular case
select table_name, table_rows from infornation_schema.tables where table_name like ...
I managed to solve the problem using the following stored procedure.
-- DROP PROCEDURE sp_Count_Rows;
Delimiter $$
CREATE PROCEDURE sp_Count_Rows()
BEGIN
DECLARE table_name TEXT DEFAULT "";
DECLARE finished INTEGER DEFAULT 0;
DECLARE table_cursor
CURSOR FOR
SELECT tblnm FROM alltbls;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished = 1;
OPEN table_cursor;
DROP TABLE IF EXISTS RowsCount;
CREATE TABLE IF NOT EXISTS RowsCount(Tlbnm text, ctnRws int);
table_loop: LOOP
FETCH table_cursor INTO table_name;
IF finished = 1 THEN
LEAVE table_loop;
END IF;
SET #s = CONCAT("insert into RowsCount select '", table_name ,"', count(*) as cntRws from ", table_name);
PREPARE stmt1 FROM #s;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
END LOOP table_loop;
CLOSE table_cursor;
SELECT * FROM RowsCount;
DROP TABLE RowsCount;
END
$$
And then when you call the procedure
CALL sp_Count_Rows();
You get this result

insert in to two table using one query inner join

I have two tables. One is user_info and the other is user_login. I want to insert data into both these tables in one query using an inner join. The data comes from user registration.
My login table:
user_id(pk) user_name user_password
1 jinesh secret
2 mahesh secret
My info table:
user_id(fk) name address phno
1 jinesh n banglore 9845***
2 mahesh m chennai 7345**
You can't make one query to insert values in two tables
However if you want to have the two querys in only one statement then
this can be done if you seperate the statements with ;
$query = "insert into table_1 (data1, data2) values ('value1', 'value2');insert into table_2 (data1,data2) values ('value1', 'value2')";
The best way is to use Stored Procedure.
CREATE PROCEDURE `Proc_User` (IN username VARCHAR(225), IN password VARCHAR(255),
IN uname VARCHAR(255),IN addr VARCHAR(255),IN phno VARCHAR(20))
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION, SQLWARNING
BEGIN
-- ERROR or WARNING
ROLLBACK;
END;
START TRANSACTION;
DECLARE userid INT;
INSERT INTO login (user_name,user_password) VALUES(username,password);
SET userid = (SELECT LAST_INSERT_ID());
INSERT INTO info(user_id,name,address,phno)
VALUES(userid,uname,addr,password);
COMMIT;
END
And to call the Stored Procedure in PHP
$proc = mysqli_prepare('CALL Proc_User(?, ?, ?,?,?);');
mysqli_stmt_bind_param('sssss', $username, $password, $name,$address,$phoneno);
mysqli_stmt_execute($proc);

How to return the value of a column immediately after an INSERT statement? [duplicate]

Is it possible to insert a row and get the values inserted in the same query?
Something like...
INSERT INTO `items` (`item`, `number`, `state`)
(SELECT '3', `number`, `state` FROM `item_bug` WHERE `id`='3')
And then, get ID and execute a
SELECT * FROM `items` WHERE `id`='$id'
But using only one query.
Execute your insert statement and then you can do this:
SELECT * FROM `items` WHERE `id`= LAST_INSERT_ID()
you can call a stored procedure which will perform the insert and return a resultset in a single call from your app layer to mysql:
Stored procedure call
mysql> call insert_user('bar');
+---------+----------+
| user_id | username |
+---------+----------+
| 1 | bar |
+---------+----------+
1 row in set (0.02 sec)
$sqlCmd = sprintf("call insert_user('%s')", ...);
Simple example:
drop table if exists users;
create table users
(
user_id int unsigned not null auto_increment primary key,
username varchar(32) unique not null
)
engine=innodb;
drop procedure if exists insert_user;
delimiter #
create procedure insert_user
(
in p_username varchar(32)
)
begin
declare v_user_id int unsigned default 0;
insert into users (username) values (p_username);
set v_user_id = last_insert_id();
-- do more stuff with v_user_id e.g. logs etc...
select * from users where user_id = v_user_id;
end#
delimiter ;
call insert_user('bar');
No, it's not possible in MySQL (unlike PostgreSQL, SQL Server and PL/SQL in Oracle).
You will have to do it in separate queries.
You can do this using multiple statements if you like to choose that route. Firstly when you connect to your database make sure that multiple statements is set to true:
var connection = mysql.createConnection({
host: databaseHost,
user: databaseUser,
password: databasePassword,
database: databaseName,
multipleStatements: true
});
Then you can just define your sql as:
var sql = "your insert statement; your select statement";
Just separate individual statements using the semi colon. Your select result will be results[1] in this example.
if you are using php then
instead of this you can use
mysql_insert_id();
which will give the id of last inserted record.
The other data will be same as inserted.
Only id will change which you can get by mysql_insert_id()
So you do not need to run second query.

Mysql - Determinate number of inserted rows in a stored procedure

So, basically I have this sql stored procedure:
drop procedure if exists registrar_cuenta;
delimiter //
create procedure registrar_cuenta (in p_email varchar(255), in p_passwordHash varchar(40), in p_salt varchar(40))
begin
if (exists(select 1 from usuarios where email = p_email and registrado = 0)) then
update usuarios set passwordHash = p_passwordHash, salt = p_salt, fechaRegistro = now(), registrado = 1 where email = p_email and registrado = 0;
else
insert into usuarios (email, passwordHash, salt, fechaRegistro, registrado) values (p_email, p_passwordHash, p_salt, now(), 1);
end if;
end
//
delimiter ;
Which runs great, BUT I want to change this piece of code:
else
insert into usuarios (email, passwordHash, salt, fechaRegistro, registrado) values (p_email, p_passwordHash, p_salt, now(), 1);
end if;
For something like this:
insert into usuarios (email, passwordHash, salt, fechaRegistro, registrado) values (p_email, p_passwordHash, p_salt, now(), 1);
if (inserted_rows == 0) then
alter table usuarios auto_increment = auto_increment - 1;
end if;
The thing is that I have an unique field (email) which can produce a duplicate entry error, if so, then the auto_increment value will increase anyways and I want to avoid that.
Is there any way I could archieve this task?
Sounds like you want to use the ROW_COUNT function.
http://dev.mysql.com/doc/refman/5.0/en/information-functions.html#function_row-count
MySQL Solution:
You can use alter table ... as in the example below:
alter table usuarios
auto_increment = ( SELECT ( AUTO_INCREMENT - 1 )
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME='usuarios' and TABLE_SCHEMA=DATABASE()
);
But, this practice is discouraged. If you define a column with int unsigned max value you can store is 4294967295 and if it is bigint unsigned max value is 18446744073709551615. If the database engine can insert 100,000 records per second, calculate your self, how many hours ( days / months / years ) would it take to cross the max value. Hence, you can omit the auto incremented value that is wasted.

MySql: Insert a row and get the content

Is it possible to insert a row and get the values inserted in the same query?
Something like...
INSERT INTO `items` (`item`, `number`, `state`)
(SELECT '3', `number`, `state` FROM `item_bug` WHERE `id`='3')
And then, get ID and execute a
SELECT * FROM `items` WHERE `id`='$id'
But using only one query.
Execute your insert statement and then you can do this:
SELECT * FROM `items` WHERE `id`= LAST_INSERT_ID()
you can call a stored procedure which will perform the insert and return a resultset in a single call from your app layer to mysql:
Stored procedure call
mysql> call insert_user('bar');
+---------+----------+
| user_id | username |
+---------+----------+
| 1 | bar |
+---------+----------+
1 row in set (0.02 sec)
$sqlCmd = sprintf("call insert_user('%s')", ...);
Simple example:
drop table if exists users;
create table users
(
user_id int unsigned not null auto_increment primary key,
username varchar(32) unique not null
)
engine=innodb;
drop procedure if exists insert_user;
delimiter #
create procedure insert_user
(
in p_username varchar(32)
)
begin
declare v_user_id int unsigned default 0;
insert into users (username) values (p_username);
set v_user_id = last_insert_id();
-- do more stuff with v_user_id e.g. logs etc...
select * from users where user_id = v_user_id;
end#
delimiter ;
call insert_user('bar');
No, it's not possible in MySQL (unlike PostgreSQL, SQL Server and PL/SQL in Oracle).
You will have to do it in separate queries.
You can do this using multiple statements if you like to choose that route. Firstly when you connect to your database make sure that multiple statements is set to true:
var connection = mysql.createConnection({
host: databaseHost,
user: databaseUser,
password: databasePassword,
database: databaseName,
multipleStatements: true
});
Then you can just define your sql as:
var sql = "your insert statement; your select statement";
Just separate individual statements using the semi colon. Your select result will be results[1] in this example.
if you are using php then
instead of this you can use
mysql_insert_id();
which will give the id of last inserted record.
The other data will be same as inserted.
Only id will change which you can get by mysql_insert_id()
So you do not need to run second query.