I'm learning PHP and MySQL and I'm creating a movies website for learning purposes.
I want to insert variables and arrays containing movie info into a database but in different tables.
This is what I have right now:
include('inc/connection.php');
$conn = dbConnect();
// create SQL
$sql = 'INSERT INTO movies (movieid, title, year, runtime, plot)
VALUES(?, ?, ?, ?, ?)';
// initialize prepared statement
$stmt = $conn->stmt_init();
if ($stmt->prepare($sql)) {
// bind parameters and execute statment
$stmt->bind_param('isiis', $movieid, $title, $year, $runtime, $plot);
$stmt->execute();
}
This insert the variables into the movies table.
But I also need to insert (at the same time) the genres of the movie, the actors, languages, etc... they are in arrays and go into different tables (and maybe also in different rows). For example the array containing the genres (Action, Crime, Drama) genres in the genres table, and the actors in the actors table and so on... I then will use many-to-many and one-to-many tables to display the info.
Can someone explain to me how to do this?? I need to connect to the database multiple times?? I need loops? I'm really new to PHP and MySQLi so please try to explain as much as you can.
Thanks.
INSERT supports only one table at a time, so you'll certainly have to execute multiple INSERT statements if you have complex data that goes into multiple tables.
If you've inserted a specific $movieid value to movies then you know what value you need to insert to the rows of the other tables, to satisfy referential integrity (pseudocode follows, not fully functional example):
$movies_sql = 'INSERT INTO movies (movieid, title, year, runtime, plot)
VALUES(?, ?, ?, ?, ?)';
// execute ($movieid, ...)
$actors_sql = 'INSERT INTO movie_actors (movieid, actorid)
VALUES (?, ?)';
foreach ($actorlist as $actorid) {
// execute ($movieid, $actorid)
}
$lang_sql = 'INSERT INTO movie_languages (movieid, language)
VALUES (?, ?)';
foreach ($languagelist as $language) {
// execute ($movieid, $language)
}
If your movies table uses an AUTO_INCREMENT primary key, then you can get the value generated during the first INSERT either by calling $mysqli->insert_id(), or by using the builtin function LAST_INSERT_ID().
$actors_sql = 'INSERT INTO movie_actors (movieid, actorid)
VALUES (LAST_INSERT_ID(), ?)';
foreach ($actorlist as $actorid) {
// execute ($actorid) -- only one param in this INSERT
}
Note that LAST_INSERT_ID() reports the most recent auto-generated value during your current session, so if you insert to another table that uses an auto primary key, the value changes. But the value reported is stable during your sessions; it does not change if another client session is doing inserts concurrently.
You only need one database connection
Yes you do need loops
Use an auto_increment primary key field in each table, and then use $stmt->insert_id() to extract the value after each insert
If you can, use InnoDB on the MySQL server to get transaction support, and then commit relevant updates together, to ensure that your database is consistent if your script drops out mid-way through an update. The more common MyISAM database table format doesn't support transactions.
You don't need to use more than one database connection - in fact that's what you shouldn't do at all, because you couldn't group your inserts into one atomic transaction.
What you should do (abstract) is:
BEGIN;
INSERT INTO Genres...;
INSERT INTO Actors...;
INSERT INTO othertable....;
and so on, for referenced columns/tables.
INSERT INTO Movies....;
COMMIT;
This way you should always have a consistent dataset.
you are probably going after foreign keys - use one ID from the base table to identify other relations in other table (use movieid in other table to identify what movie the other values belong to etc.)
Related
I am trying to ignore an INSERT query if the two values, Answer1Text and Answer2Text are already in the database, but still get the id (called AnswerID (Primary Key) from the row? How can I do this?
My code:
$stmt = $conn->prepare("INSERT INTO answers (Answer1Text, Answer2Text) VALUES (?, ?)");
$stmt->bind_param('ss', $text1, $text2);
$stmt->execute();
$answerid = $stmt->insert_id;
Table of importance:
answers: AnswerID(PK), Answer1Text, Answer2Text
I have tried looking at ON DUPLICATE KEY UPDATE but I neither understand how to use it, nor do I think it is the right choice.
The best approach is to query your database first using a SELECT query: does the data already exist? If so, pull out the ID and use that in place of the insert ID; if not, insert a new row. You're right: ON DUPLICATE KEY UPDATE is wrong for this.
I'm from a mssql background so it's difficult to get used to MySQL.
I have a table called users_settings. Within this table there are three columns; uid, name, value. Uid is a integer and refers to the user that owns that setting, the name is the name of the setting, the value, well, you guessed it, is the value.
What I'm trying to do is update these settings if it already exists, but if it doesn't, insert a new row.
My current query is as such (Note I'm using prepared statements):
IF EXISTS (SELECT * FROM users_settings WHERE name = ? AND uid = ?) THEN
UPDATE users_settings SET value = ? WHERE name = ? AND uid = ?;
ELSE
INSERT INTO users_settings (uid, name, value) VALUES (?, ?, ?);
END IF;
The issue I'm having is that when I attempt to prepare my statement, it returns false, which therefore suggests that the syntax is incorrect. After looking in to this, it looks like it's a SQL syntax error.
Would anybody be able to point me in the relative direction as to what may be occurring here, and where I may have got my syntax incorrect?
IN MySQL, if as a statement can only used in programming blocks -- stored procedures, functions, and triggers (this is not to be confused with if as a function, which can be used in almost any SQL statement).
You can do what you want in MySQL with a single statement, insert . . . on duplicate key update. For this to work, you need a unique index on name and uid:
create unique index users_settings_name_uid on users_settings(name, uid);
And then:
INSERT INTO users_settings (uid, name, value)
VALUES (?, ?, ?)
ON DUPLICATE KEY UPDATE value = VALUES(value);
There are two ways to fulfill your request in MySQL:
If you want to update an existing row or insert a new one if it does not exist then you should use INSERT ... ON DUPLICATE KEY UPDATE:
INSERT INTO users_settings (uid, `name`, `value`)
VALUES (?, ?, ?)
ON DUPLICATE KEY UPDATE `value` = VALUES(`value`);
This relies on an unique index that contains the columns uid and name. If it does not already exist, you can create it:
ALTER TABLE users_settings
ADD UNIQUE INDEX uid_name (uid, `name`);
You need it anyway because you want a single entry in the table for each user and setting name.
If you want to insert a row in the table and replace (discard) another one that already exists then you can use REPLACE:
REPLACE INTO users_settings (uid, `name`, `value`)
VALUES (?, ?, ?);
The syntax of REPLACE is similar with the one of INSERT (but it does not support ON DUPLICATE KEY UPDATE for obvious reasons). Internally it does a DELETE followed by an INSERT (it is just a shortcut). It discards the existing row (if any) and insert the new one. It also relies on the presence of the above mentioned index (that you have no matter how you update the values in the table).
For your situation both approaches have the same outcome because there is a single column (value) that is updated or replaced. In other situations only one of them is good.
Choose the one that you feel more appropriate for your workflow and your coding style.
INSERT INTO persona_opcion(nPerCodigo,nOpcCodigo,nPerOpcEstado)
SELECT '$codPer','$idOpc',1
FROM persona_opcion
WHERE NOT EXISTS(
SELECT nPerCodigo,nOpcCodigo,nPerOpcEstado
FROM persona_opcion
WHERE nOpcCodigo='$idOpc'
and nPerCodigo='$codPer'
)
LIMIT 1;
enter link description here
Is it possible to INSERT a row, and simultaneously set one of the fields to contain the inserted row's ID? (the "self" id)
I'm trying to avoid using multiple queries if possible (it would be 3 queries in total otherwhise)
Something like this (but probably not):
INSERT INTO thetable (email, phone, activationkey) VALUES ($email, $phone, CONCAT(THIS_NEW_ID, md5($activation) )
Why would you want to store duplicated data?
You could change your table structure and just run:
INSERT INTO thetable (email, phone, activation_suffix)
VALUES ($email, $phone, md5($activation))
And then you have all the data you need.
You can always concat when you query the table:
SELECT CONCAT(id, activation_suffix) activationkey
FROM thetable
WHERE ...
UPDATE
On second thoughts, do you really need the id as part of the activationkey?
I wouldn't want to give any user the id for their record in my table unless it is hashed/encrypted.
EDIT: I forgot to mention that my script should not input IF a row already exists in the new DB with the same column values, that is what I mean when I refer to "duplicate entries" despite there not being a common PK.
Here's my dilemma,
I'm working with MySQLi to migrate data from an old table into a new table which have different designs and I want my program to be able to run multiple times without multiplying previous entries. My initial approach was to do a verification query for each inserted element:
//foreach elt of old table:
$a = $old_table['a'];
$b = $old_table['b'];
$query = $db->query("SELECT `id` FROM `old_table`
WHERE `a` = '$b'
AND `b` LIKE '$b'")->fetch_assoc();
if ($query == null) {
//insert a row into the new table
}
The problem with this method is that the run-time was horrendous and I managed to considerably cut it down by using a database transaction:
$query = $db->prepare("INSERT INTO `new_table`
(`a`, `b`, `c`, `d`, `e`)
VALUES (?, ?, ?, ?, ?)");
$query->bind_param('isssi', $a, $b, $c, $d, $e);
$db->query("START TRANSACTION");
foreach ($old_table as $old_row) {
$a = $old_row['a'];
...
$e = $old_row['e'];
$query->execute();
}
$query->close();
$db->query("COMMIT");
The problem with this method is that it results in multiple entries if the program is run more then once. It's important to note that since both tables have different designs, there is no common Primary Key and therefore I don't think I can use DUPLICATE KEY.
Thoughts?
In fact, you already solved the problem, but for some reason stopped half-way.
The problem with this method is that the run-time was horrendous and I managed to considerably cut it down by using a database transaction
I wonder why didn't you include select into transaction as well.
Thoughts?
Just add select query you used to run in the first variant. That's all.
Well, I believe you don't have to use a script for this, a query would be enoguh:
SELECT
/* your complex columns, from and joins go here */
LEFT JOIN `new_table` n ON n.a = old_table.a AND n.b LIKE old_table.b
WHERE
n.a IS NULL AND n.b IS NULL AND
/* your WHERE and LIMIT go here */
This approach makes use of LEFT JOIN which, if there is no matching row in the right table, sets all columns to NULL (documented here).
I haven't completely understood that: there is no common KEY and therefore I can't do a DUPLICATE KEY. so I'm not sure that you will find this useful but you can choose if you use it...
Perhaps you'll find more simple if you just try INSERT IGNORE :
INSERT IGNORE INTO new_table (a,b,c,d)
SELECT a,b,c,d FROM old_table
IGNORE word will discard automatically any insert wich generates a primary o unique key conflict.
You also can directly insert from a select query. This can make things easier for this job
This question already has answers here:
MySQL Insert into multiple tables? (Database normalization?) [duplicate]
(7 answers)
Closed 9 years ago.
How do I insert from 1 form into 2 tables?
I want to submit into tables: gallery and cat.
This is the code I have for just submitting into gallery. How do I add info to the cat table simultaneously?
$sql = 'INSERT INTO gallery (image_url, image_name, image_description)
VALUES (?, ?, ?)';
$stmt = $conn->stmt_init();
if ($stmt->prepare($sql)) {
$stmt->bind_param('sss', $_FILES['upload']['name'], $_POST['image_name'],
$_POST['image_description']);
$OK = $stmt->execute();
}
What do I do with this:
$sql_2 = 'INSERT INTO cat (cat_name) VALUES (?)';
After the code you have above, just initialize a new statement, bind it, and execute. You can use the existing connection ($conn). The $sql and $stmt variables have already done their job by inserting into the first table, so just reset them for the second insert.
... your code as posted above, followed by:
$sql = 'INSERT INTO cat (cat_name) VALUES (?)';
$stmt = $conn->stmt_init();
if ($stmt->prepare($sql)) {
$stmt->bind_param('s', put your cat_name value here);
$OK = $stmt->execute();
}
The important thing to remember is that the second insert is just like the first: you have to initialize it, bind it, and execute it.
You should use transactions as demonstrated here.
No, you can't insert into multiple tables in one MySQL command. You can however use transactions.
BEGIN
INSERT INTO users (username, password)
VALUES('test', 'test')
INSERT INTO profiles (userid, bio, homepage)
VALUES(LAST_INSERT_ID(),'Hello world!', 'http://www.stackoverflow.com');
COMMIT;
Have a look at LAST_INSERT_ID to reuse autoincrement values.
Edit: you said "After all this time trying to figure it out, it still doesn't work. Can't I simply put the just generated ID in a $var and put that $var in all the MySQL commands?"
Let me elaborate: there are 3 possible ways here:
1/ Is the code you see above. This does it all in MySQL, and the LAST_INSERT_ID in the second statement will automatically be the value of the autoincrement-column that was inserted in the first statement.
Unfortunately, when the second statement itself inserts rows in a table with an auto-increment column, the LAST_INSERT_ID will be updated to that of table 2, and not table 1. If you still need that of table 1 afterwards, we will have to store it in a variable. This leads us to ways 2 and 3:
2/ Will stock the LAST_INSERT_ID in a MySQL variable:
A/ INSERT B/ SELECT LAST_INSERT_ID into #mysql_variable_here C/ INSERT INTO table2 (#mysql_variable_here D/ INSERT INTO table3 (#mysql_variable_here ...
3/ Will stock the LAST_INSERT_ID in a php variable (or any language that can connect to a database, of your choice):
A/ INSERT B/ Use your language to retrieve the LAST_INSERT_ID, either by executing that literal statement in MySQL, or using for example php's mysql_insert_id() which does that for you C/ INSERT ()