Get LAST_INSERT_ID() from a multi-insert in MySQL - php

I need to get my id value after inserting multiply rows in my MySQL database
INSERT INTO table (`row1`, `row2`, `row3`, `row4`)
VALUES
('value1','value2','value3','value4'),
('value1','value2','value3','value4');
in the fact I need to something like
SQL Server - Return value after INSERT
but for MySQL and I don't need to return last id because I can do it by myself but I thought it is not good for what I doing
is any better way exist for my question?

If you want to insert blocks of rows into the parent and child table, your rows must have unique columns so you can look up the parent rows after insert:
INSERT INTO table (col1, col2, col3, col4)
VALUES
('value1','value2','value3','value4'),
('value5','value6','value7','value8');
INSERT INTO childtable (colx, coly, id_table)
SELECT 1, 2, id FROM table WHERE col1 = 'value1' and col2 = 'value2' and col3 = 'value3' and col4 = 'value4'
UNION ALL
SELECT 1, 2, id FROM table WHERE col1 = 'value5' and col2 = 'value2' and col3 = 'value3' and col4 = 'value4'
Even if there was a method for getting all inserted IDs, you'd have to do the same thing, because tables contain an unordered set of rows. If you got back IDs 11 and 12 for the first insert statement, which row would ID 11 refer to and which ID 12? There is no way to tell that, because the DBMS is free to insert the rows in any order.
If the row data is not unique as in your example, where both rows contain ('value1','value2','value3','value4'), then you must either insert all the data in a loop or use some CTE to number the rows first and play with those numbers.
Anyway, having said all that, I would just insert the data row by row in a loop (i.e. one parent, all its children, next parent, ...), as long as you don't get unbearable performance issues.

Related

Insert rows on one table based on the values of a field of another table in PHP-MySQL

I would like to ask for a solution on how to insert n rows based on the values of a field in another table.
Consider the following tables:
CREATE TABLE Input (
ID INT AUTO_INCREMENT PRIMARY KEY,
Name VARCHAR(128),
Ticket_Piece INT
);
CREATE TABLE Output (
Ticket_ID INT AUTO_INCREMENT PRIMARY KEY,
Transaction_ID INT,
Ticket_Number VARCHAR(23) UNIQUE,
FOREIGN KEY (Transaction_ID)
REFERENCES Input (ID)
ON UPDATE CASCADE
ON DELETE CASCADE
);
If a row from the Input table has n in the Ticket_Number column, then n rows should be inserted into the Output table, with Ticket_Number having values "ID-1" through "ID-n" (e.g. (4, "D", 5) in Input should result in rows with ticket numbers "4-1" through "4-5" being added to Output). How can rows for Output be generated in a range of numbers based on the Ticket_Piece column using PHP and MySQL?
For example, with the input:
INSERT INTO Input (ID, Name, Ticket_Piece)
VALUES
(1, 'A', 2),
(2, 'B', 1),
(3, 'C', 3)
;
the result should be:
Ticket_ID
Transaction_ID
Ticket_Number
1
1
1-1
2
1
1-2
3
2
2-1
4
3
3-1
5
3
3-2
6
3
3-3
For each row you fetch from the input table, use a for loop to insert multiple rows into the output table.
$res = $pdo->query("SELECT id, ticket_piece FROM Input_Table");
$insert_stmt = $pdo->prepare("INSERT INTO Output_Table (transaction_id, ticket_number) VALUES (:id, :ticket)");
while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
$pieces = $row['ticket_piece'];
$id = $row['transaction_id'];
for ($i = 1; $i <= $pieces; $i++) {
$insert_stmt->execute([':id' => $id, ':ticket' => "$id-$i"]);
}
}
A solution in PHP or SQL will likely need to use a loop.
If this comes from the data model rather than business rules (and depending on other factors), a trigger might be a fairly simple option. The trigger body could have a WHILE or other loop to iterate over the ticket piece numbers, and CONCAT to combine the ID and piece number into a ticket number, inserting each in turn.
DELIMITER ;;
CREATE TRIGGER create_ticket_pieces
AFTER INSERT ON Input
FOR EACH ROW
BEGIN
DECLARE piece INT DEFAULT 1;
WHILE piece <= NEW.Ticket_Piece DO
INSERT INTO Output (Transaction_ID, Ticket_Number) Values (NEW.ID, Concat(NEW.ID, '-', piece));
SET piece := piece + 1;
END WHILE;
END;;
DELIMITER ;
An alternative some use is to pregenerate a table of numbers, then join with this table to generate rows:
INSERT INTO Output (Transaction_ID, Ticket_Number)
SELECT Input.ID, Concat(Input.ID, '-', Numbers.Number)
FROM Input
JOIN Numbers ON Numbers.Number <= Input.Ticket_Piece
WHERE ... -- select Input rows
It should be noted that by duplicating information (the transaction ID) in two different columns, the Output table isn't normalized. In particular, it violates 3rd normal form, due to a functional dependency of Transaction_ID on Ticket_Number. The way to resolve this is to leave the transaction ID out of the ticket number field (i.e. Output.Ticket_Number holds only the generated integer ≤ Input.Ticket_Piece). (See: "Third Normal Form: Composite PRIMARY KEY vs System-Generated Surrogate (IDENTITY)")

Sql INSERT INTO combining value and select failure

I wanted to insert two values, one is filled by a fixed number and the other one is the id from another table.
Now I got the error
#1242 - Subquery returns more than 1 row.
INSERT INTO table1 (value1, value2) VALUES
(6 , (SELECT id FROM table2 WHERE name = 'Peter'))
Maybe you can help me.
If you want to insert record into your table1 for every record in table2 where name is Peter this approach should work.
This insert query will insert all records from table2 where name is "Peter" into table1. If you want to insert only one record you could use LIMIT as Macmee has explained in his answer
insert into dbo.table1
(
value1,
value2
)(
select
6,
table2.id
from
table2
where
name = 'Peter'
)
try using LIMIT 1:
INSERT INTO table1 (value1, value2) VALUES (6 , (SELECT id FROM table2 WHERE name = 'Peter' LIMIT 1))
This way in your nested query (SELECT id FROM table2 WHERE name = 'Peter' LIMIT 1) it will only return the first match, and your insert should go through.
Keep in mind that if your intent was to insert new rows for EVERY row in table2 who's name is "Peter" then this will only insert the first one.

SQL - Insertion of row with selection from other row IF the row doesn't exits

So, I'm wanting to insert a row into the table teams_views IF the row doesn't already exist. This is a simple query, but I'm having issues including a selection into this query. Basically, one of the fields in the row is in need of being fetched from another table.
Here's a breakdown of what I am wanting to build in SQL:
INSERT INTO
teams_views (col1, col2)
VALUES
(SELECT col1 FROM teams WHERE teams.identifier = 1234, col2)
WHERE
teams_views.col1
IS NULL
What can I do to get this query work?
Thanks.
INSERT INTO teams_views (col1, col2)
SELECT t.col1 , t.col2
FROM teams t
WHERE t.identifier = 1234
AND NOT EXISTS (SELECT 1 FROM teams_views
WHERE t.col1 = col1)

Insert distinct values only for one column

Say, I have a table with five columns col1, col2, col3, col4 and user_id. Now, I have an array of user_id values, say a thousand. I want to insert thousand of records where only distinct column value is user_id. If there is a simplier way rather than make a thousand of ('col1value','col2value','col3value','col4value',someUserId) and concatenate them in single insert into tbl (col1,col2,col3,col4,user_id) values query?
Update: I guess it needs some clarification
So here's simple example. Let's say I have an events table with fields event and user_id. Some call event occurs for users with id 1, 2, 5, 101, 233, 422 and 1000. So I need to insert 7 records into table so it should look like
+-------+---------+
| event | user_id |
|-------|---------|
| call | 1 |
| call | 2 |
| call | 5 |
| call | 101 |
| call | 233 |
| call | 422 |
| call | 1000 |
+-------+---------+
I want to do it as efficiently database-wise as possible. So far, I think I have to make such SQL query:
insert into events (event,user_id) values ('call',1),('call',2),('call',5),('call',101),('call',233),('call',422),('call',1000);
then perform a single query
But maybe there is some more simple and efficient way? Maybe something with SQL parameters or such?
I understand your question that you have a high number of different user_id values that you want to insert, but for each new record you want the same values for col1, col2, col3 and col4.
The easiest way to achieve this would be to set a DEFAULT value for each of the columns:
ALTER TABLE mytable CHANGE `col1` `col1` INTEGER NOT NULL DEFAULT 1234
This sets the default value for col1 to 1234. I assumed the column to be of data type INTEGER, you need to change it accordingly.
If changing the table is not an option for you, then you could still build an insert statement that would insert all records in a single transaction:
$user_ids = array(5,6, 7, 8, 9, 10); // these are the user ids you want to insert
$default_vals = array(1, 2, 3, 4); // These are the default values used for col1, col2, col3 and col4
$con = new mysqli('mydomain', 'myuser', 'mypw', 'mydb');
$rows = array();
foreach ($user_ids as $id)
$rows[] = "(".implode(',', $default_vals).",$id)";
$sql = "INSERT INTO mytbl (col1, col2, col3, col4, id) VALUES " . implode(',', $rows) . ";";
$con->query($sql);
Or, if for academic reason you'd rather write a single INSERT statement to do the whole job, you could write:
INSERT INTO mytbl (col1, col2, col3, col4, id)
SELECT * FROM
(SELECT 1, 2, 3, 4) AS DEFAULT_VALS,
(SELECT 5
UNION SELECT 6
UNION SELECT 7
UNION SELECT 8
UNION SELECT 9) AS USER_IDS
However, the last approach really isn't very nice. Neither in terms of readability nor of performance.
Edit
I made a quick benchmark for you:
Inserting 10k different records like in my PHP sample took 1.5086660385132 seconds on my webserver.
The SELECT INTO approach with 10k UNIONs took 3.3481941223145 seconds.
Easy choice, though.
Use :
INSERT INTO tbl (col1,col2,col3,col4,user_id)
VALUES
(1,2,3,4,1000),
(2,3,4,5,1000),
(6,7,8,9,1000),
(1,2,3,4,1234),
(4,3,2,1,1235);
Create unique index on user_id field. use INSERT IGNORE or INSERT ... ON DUPLICATE KEY UPDATE for ignoring duplicate insertion of user_id.
For details, kindly refer MySQL documentation.
http://dev.mysql.com/doc/refman/5.6/en/insert.html

Copy From One Table To Another Specific Cols And Variables

Hi,
I Want to copy from one table to another with the specific cols.
In addition i want to add to the query an external variable. for example in one table i have col1,col2,col3 and in the second table i have the same cols ( col1,col2,col2 )
but now i want to insert to the col4 a variable. it will be like : insert into col1,col2,col3,col4 select (col1,col2,col3,$VARIABLE)
$copy_cols= 'INSERT INTO A (COL1,COL2,COL3,COL4) SELECT COL1,COL2,COL3,$VAR FROM B WHERE Descriptiontable="AAA"';
mysql_cols($copy_query);
the col4 is not can be found in table B , so i want to insert to this col some variable.. for example : the variable can be a constant number.
$copy_cols = 'INSERT INTO A (COL1,COL2,COL3,COL4)
SELECT COL1,COL2,COL3,'FOO' AS COL4
FROM B WHERE Descriptiontable="AAA"';
mysql_cols($copy_query);

Categories