How do I insert data while updating? - php

when updating a database having 4 tables, if a particular table hasn't got the data which is updating..then how can I insert the same to that table??
my database is based on SEO so inserting web information to all 4 tables. these tables indexed by domain name. so if four tables got domain name which is updating then it will update all table otherwise it wont. but I want add that domain and info to table which does not have the same info.
right now I am using normal update query
mysql_query("UPDATE LOW_PRIORITY dscrpn SET descr='$descr',title='$title' WHERE web='".mysql_real_escape_string($urweb)."'");

You can't (as far as I know) write one single statement that will either update the existing row, or insert a new one if it doesn't exist.
What you can do is send an update, then check the number of rows affected by the update (mysql_affected_rows); if it had zero rows affected, then do an insert. You could do this all in MySQL, but I'd probably just do it in the PHP code.

see http://dev.mysql.com/doc/refman/5.1/en/insert-on-duplicate.html
self-contained example (using PDO):
<?php
$pdo = new PDO('mysql:host=localhost;dbname=test', 'localonly', 'localonly');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
setup($pdo);
$stmt = $pdo->prepare('
INSERT INTO
so_dscrpn
(web,descr,title)
VALUES
(?,?,?)
ON DUPLICATE KEY UPDATE
descr=VALUES(descr),
title=VALUES(title)
');
$data = array(
array('uri1', 'desc1', 'title1'),
array('uri2', 'desc2', 'title2'),
array('uri1', 'desc3', 'title3')
);
foreach($data as $row ) {
$stmt->execute($row);
}
foreach( $pdo->query('SELECT * FROM so_dscrpn', PDO::FETCH_ASSOC) as $row ) {
echo join(', ', $row), "\n";
}
function setup($pdo) {
$pdo->exec('
CREATE TEMPORARY TABLE so_dscrpn (
web varchar(48) NOT NULL,
descr varchar(48),
title varchar(48),
unique key(web)
)
');
}
prints
uri1, desc3, title3
uri2, desc2, title2

Related

Moving records to a new table updating original id accordingly

I need to create a new table with certain data from another table but update the original table with the ID of the newly inserted record from the new table. Like so:
NEW_TABLE
----------------
id
-- other data --
ORIGINAL_TABLE
----------------
id
new_table_id
-- other data --
However, the added records to new_table will be grouped to get rid of duplicates. So, it won't be a 1-to-1 insert. The query needs to update matching records, not just the copied record.
Can I do this in one query? I've tried doing a separate UPDATE on original_table but it's not working.
Any suggestions?
You are going to be doing 3 seperate queries as I see it.
$db = new PDO("...");
$stmt = $db->prepare("SELECT * FROM table");
$stmt->execute();
$results = $stmt->fetchAll();just iterate o
foreach ($results as $result) {
$stmt = "INSERT INTO new_table (...) VALUES (...)";
$stmt = $pdo->prepare($stmt);
$data = $stmt->execute();
$insert_id = $pdo->lastInsertId();
// Update first table
$stmt = "UPDATE table SET id=:last WHERE id=:id";
$stmt = $pdo->prepare($stmt);
$data = $stmt->execute(array('last' => $insert_id, 'id' => $result['id']));
}
The above is a global example of your workflow.
You can use temporary tables or create a view for NEW_TABLE.
Temporary Tables
You can use the TEMPORARY keyword when creating a table. A TEMPORARY table is visible only to the current session, and is dropped automatically when the session is closed. This means that two different sessions can use the same temporary table name without conflicting with each other or with an existing non-TEMPORARY table of the same name. (The existing table is hidden until the temporary table is dropped.) To create temporary tables, you must have the CREATE TEMPORARY TABLES privilege.
--Temporary Table
create temporary table NEW_TABLE as (select * from ORIGINAL_TABLE group by id);
Views
Views (including updatable views) are available in MySQL Server 5.0. Views are stored queries that when invoked produce a result set. A view acts as a virtual table. Views are available in binary releases from 5.0.1 and up.
--View
create view NEW_TABLE as select * from ORIGINAL_TABLE group by id;
The view will always be updated with the values in ORIGINAL_TABLE and you will not have to worry about having duplicate information in your database.
If you do not want to use the view, I believe you can only perform an insert on one table at a time unless you have some sort of view that would allow you to do both, but you probably want to do it as two steps in a transaction
First you will have to tell the database that you want to start a transaction. Then you will perform your operations and check to see if they were successful. You can get the id of last inserted row (this assumes you have an auto_increment field) to use in the second statement. If both statement seem to work fine, you can commit the changes, or if not, rollback the changes.
Example:
//Assume it will be okay
$success = true;
//Start the transaction (assuming you have a database handle)
$dbh->beginTransaction();
//First Query
$stmt = "Insert into ....";
$sth = $dbh->prepare($stmt);
//See if it works
if (!$sth->execute())
$success = false;
$last_id = $dbh->lastInsertId();
//Second Query
$stmt = "Insert into .... (:ID ....)";
$sth = $dbh->prepare($stmt);
$sth->bindValue(":ID", $last_id);
//See if it works
if (!$sth->execute())
$success = false;
//If all is good, commit, otherwise, rollback
if ($success)
$dbh->commit();
else
$dbh->rollBack();

select insert into not working

I am trying to insert a number of ids into a new table. The list of ids is taken from another table.
My Code:
$stmt = $con->prepare('DROP TABLE tblname;
CREATE TABLE tblname (
id BIGINT
);
INSERT INTO tblname (id)
SELECT tablename2.colname
FROM tablename2
WHERE (col1 = "value" AND col2 = "value")');
$stmt->execute();
I create and dump the table because its part of an update script.
(Is there a better way to do that than dump/create?)
The script needs the current list of ids and I am trying to get create a table with those ids. What happens is, whenever I run the code (using putty) it returns "0" and the table remains empty.
What did I do wrong?
Any general help/advice concerning php/mysql welcome too!
First, make sure PDO is set to throw exceptions if a query fails:
$con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
Then, perhaps catch the exception (or let the exception halt the application) and see what is wrong.
I do believe your insert query is, erm, off:
INSERT INTO tblname (id)
SELECT tblname2.colname
FROM tablename2
WHERE col1 = "val"
Just seems ambiguous, and messy, even more: it seems unsafe. However, try this -equally messy- query:
INSERT INTO tblname (id) VALUES (
SELECT colname
FROM tblname2
WHERE col1 = "val"
);
Last but not least, make sure you're running PHP version 5.3+, because prior to that version, PDO did not support multiple queries.
My suggestion, though, is not to use multiple queries for the INSERT query. Instead, I'd use a transaction and separate the select and insert query. I'd also add a safety-net to the DROP TABLE and CREATE TABLE queries, too:
try
{
$con->beginTransaction();//DROP & CREATE:
if ($con->exec('DROP TABLE IF EXISTS tblname') === false)
{//query wasn't executed
$con->rollback();
exit($con->errInfo());//error
}
if ($con->exec('CREATE TABLE IF NOT EXISTS tblname(...);') === false)
{
$con->rollback();
exit($con->errInfo());
}
$con->commit();//alter tables.
$con->beginTransaction();//INSERT TRANSACTION
$stmt = $con->prepare('INSERT INTO tblname (id) VALUES (:id)');
$bind = array(
':id' => null
);
$select = $con->prepare(
'SELECT colname FROM tblname2 WHERE col1 = :val1 AND col2 = :val2'
);
$select->execute(
array(
':val1' => 'value1',
':val2' => 'value2'
)
);
while ($row = $select->fetch(PDO::FETCH_ASSOC))
{
$bind[':id'] = $row['colname'];
$stmt->execute($bind);//inserts row
$stmt->closeCursor();//optional
}
$con->commit();//save changes to db
}
catch (PDOException $e)
{
//rollback transaction
$con->rollback();
exit($e->getMessage());//show what went wrong, and exit.
}
You are missing a keyword here to INSERT the value into the table, which is VALUES.
The correct syntax will be
INSERT INTO tblname (id) VALUES
SELECT tablename2.colname
FROM tablename2
WHERE (col1 = 'value' AND col2 = 'value')
The values should be into SINGLE QUOTES, tried and test myself and these 2 work for me every time.!

PHP MYSQL insert Data into 2 tables

Hey All I currently have a problem with my insert php file
I have a form.. where the user types in details.. for my form.. event name, event date and location
basically the bit that is working
is
well first of all i would like to add an entry into 2 tables: events and results
it's having no problems adding the entry into events
but it doesnt add the same entry into "results"
the events table had the following columns: Event ID, Event Name, Event Date and Location
The Results table has: Event ID, Member ID, Event Name, Score and Place
The Event ID is auto increment
so it auto assigns an ID to it
and its applied to both tables
the auto increment in Event ID
the bit thats working is
inserting entry into the events table
but because the events table and results table both have "Event Name
I want this php to fully insert details for the event table
BUT also at the same time, just insert the eventname into the results table
but the EventID in events has to be the same generated number as EventID in results..
Below is my code: All help really appreciated!!!
<?
$pdo = new PDO('mysql:host=localhost;dbname=clubresults', 'root', '12345678');
#Set Error Mode to ERRMODE_EXCEPTION.
$pdo->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$query = $pdo->query('SELECT EventID, EventName, EventDate, Location from events');
$rowset = array();
if ($query) {
while ($row = $query->fetch(PDO::FETCH_ASSOC)) {
// Build array of rows
$rowset[] = $row;
}
// Output header first
$headrow = $rowset[0];
print("<table border=\"1\">\n<tr>\n");
// Use $rowset[0] to write the table heading
foreach ($headrow as $col => $val) {
printf("<th>%s</th>\n", $col);
}
print("</tr>");
// Then output table rows.
// Outer loop iterates over row
foreach ($rowset as $row) {
print("<tr>");
// Inner loop iterates over columns using $col => $val
foreach ($row as $col => $val) {
// We don't know your column names, but substitute the first column (the ID) for FIRSTCOL here
printf("<td>%s</td>\n", $row['EventID'],$val);
}
print("</tr>");
}
}
print("</table>");
?>
</form>
</div>
<?
$pdo = new PDO('mysql:host=localhost;dbname=clubresults', 'root', '12345678');
#Set Error Mode to ERRMODE_EXCEPTION.
$pdo->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt=$pdo->prepare('INSERT INTO events (EventName, EventDate, Location)
VALUES (:EventName, :EventDate, :Location)');
$stmt->bindValue(':EventName', $_POST['EventName']);
$stmt->bindValue(':EventDate', $_POST['EventDate']);
$stmt->bindValue(':Location', $_POST['Location']);
$stmt->execute();
?>
<?
$int_event_id = $_GET["EventID"];
if((int)$int_event_id)
{
$stmt=$pdo->prepare('INSERT INTO results (EventName, EventID)
VALUES (:EventResultsName, $int_event_id)');
$stmt->bindValue(':EventName', $_POST['EventResultsName']);
$stmt->execute();
}
?>
If the inserts are always taking place in sequence, I'd use $pdo->lastInsertId() (see: http://www.php.net/manual/en/pdo.lastinsertid.php)
So, I think this line is wrong:
$int_event_id = $_GET["EventID"];
I'd write it like this:
<?
$stmt = $pdo->prepare('
INSERT INTO results (EventName, EventID)
VALUES (:EventResultsName, :EventId)
');
$stmt->bindValue(':EventName', $_POST['EventResultsName']);
$stmt->bindValue(':EventId', $pdo->lastInsertId());
$stmt->execute();
?>
Note that this assumes the insert into results occurs immediately after the insert into events.
You could've done the same thing without a bind variable using MySQL's native last_insert_id() function, like this:
<?
$stmt = $pdo->prepare('
INSERT INTO results (EventName, EventID)
VALUES (:EventResultsName, last_insert_id())
');
$stmt->bindValue(':EventName', $_POST['EventResultsName']);
$stmt->execute();
?>
However, this is less portable than the previous example. However, pdo's lastInsertId() isn't exactly RDBMS agnostic either (see docs) so you'd have to fix this piece of code anyway if you're thinking of targeting another RDBMS
Assuming that the Event entity represents an event and the Result entity represents an outcome of an event for a particular member. So there is a 1->n relationship between Event -> Result
Problem
This means that sometimes you will get a request where both Event and Result data needs to be inserted, while at others there will only be Result data. In this latter case you will be able to get EventId from the request _GET[EventId].
In the first case, you will not have this EventId set and since you are executing the second insert based on the check if((int)$int_event_id), the Result will never be inserted.
Solution
The first thing you should do is check if _GET["EventId"] isset. If it isn't then you need to both insert an Event and Result. In this case the EventId will be fetched from the last inserted statement as follows:
$pdo->lastInsertId()
While if the `_GET["EventId"] isset, you should only do the second insertion as you are doing.

Getting the final value to this MySQL query

I've got my database set up with three tables - code, tags, and code_tags for tagging posts.
This will be the SQL query processed when a post is submitted. Each tag is sliced up by PHP and individually inserted using these queries.
INSERT IGNORE INTO tags (tag) VALUES ('$tags[1]');
SELECT tags.id FROM tags WHERE tag = '$tags[1]' ORDER BY id DESC LIMIT 1;
INSERT INTO code_tags (code_id, tag_id) VALUES ($codeid, WHAT_GOES_HERE?)
The WHAT_GOES_HERE? value at the end is what I need to know. It needs to be the ID of the tag that the second query fetched. How can I put that ID into the third query?
I hope I explained that correctly. I'll rephrase if necessary.
Edit: Thanks for your help so far but I'm still struggling a bit in regards to what was pointed out - if it's already there I can't get the inserted ID...?
If you use INSERT IGNORE and a new record is ignored (because of a unique key violation) mysql_insert_id() and LAST_INSERT_ID() don't have a meaningful value.
But you can use INSERT ... ON DUPLICATE KEY UPDATE and LAST_INSERT_ID(expr) to set the data you expect LAST_INSERT_ID() to return in case of a doublet.
Step-by-step:
Let's assume you have a table tags like
CREATE TABLE tags (
id int auto_increment,
tag varchar(32),
dummy int NOT NULL DEFAULT 0, /* for demo purposes only */
primary key(id),
unique key(tag)
)
Inserting a tag twice results in a duplicate key violation because of unique key(tag). That's probably the reason why you've used INSERT IGNORE. In that case MySQL ignores the violation but the new record is ignored as well. The problem is that you want the id of the record having tag='xyz' regardless of whether it has been newly created or it was already in the database. But right now mysql_insert_id()/LAST_INSERT_ID() can oly provide the id of a new record, not an ignored one.
With INSERT ...ON DUPLICATE you can react on such duplicate key violations. If the new record can be inserted (no violation) it behaves like a "normal" INSERT. But in case of a duplicate key violation the part after ON DUPLICATE KEY is executed like an UPDATE statement for the record with that particular index value already existing in the table. E.g. (with an empty table tags)
INSERT INTO tags (tag) VALUES ('tag A') ON DUPLICATE KEY UPDATE dummy=dummy+1
This will simply insert the record as if there was no ON DUPLICATE ... clause. id gets the next auto-increment value, dummy the default value of 0 and tag='tag A'. Let's assume the newly create auto_increment value was 1. The resulting record stored in MySQL is (id=1, tag='tag A', dummy=0) and LAST_INSERT_ID() will return 1 right after this query. So far so good.
Now if you insert the same record again with the same query a violation occurs because of the first record (id=1, 'tag=tag A', dummy=0). For this already exisitng record the UPDATE statement after ON DUPLICATE KEY is executed, i.e. the record becomes (id=1, tag='tag A', dummy=1). But since no new record has been created there was also no new auto_increment value and LAST_INSERT_ID() becomes meaningless. So still the same problem as with INSERT IGNORE.
But there is a "special" construct that allows you to set the value LAST_INSERT_ID() is supposed to return after the ON DUPLICATE KEY UPDATE statement has been executed.
id=LAST_INSERT_ID(id)
Looks strange but it really only sets the value LAST_INSERT_ID() will return.
If you use the statement
INSERT INTO
tags
(tag)
VALUES
('xyz')
ON DUPLICATE KEY UPDATE
id=LAST_INSERT_ID(id)
LAST_INSERT_ID() will always return the id of the record having tag='xyz' no matter if it was added by the INSERT part or "updated" by the ON DUPLICATE KEY part.
I.e. if your next query is
INSERT INTO
code_tags
(code_id, tag_id)
VALUES
(4711, LAST_INSERT_ID())
the tags.id for the tag 'xyz' is used.
The self-contained example script uses PDO and prepared statements. It should do more or less what you want to achieve.
$pdo = new PDO("mysql:host=localhost;dbname=test", 'localonly', 'localonly');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// set up temporary table and demo data
$pdo->exec('CREATE TEMPORARY TABLE tmpTags (id int auto_increment, tag varchar(32), primary key(id), unique key(tag))');
$pdo->exec('CREATE TEMPORARY TABLE tmpCode_tags (code_id int, tag_id int)');
$pdo->exec("INSERT INTO tmpTags (tag) VALUES ('tagA'), ('tagB')");
// prepare the statements
// set id=LAST_INSERT_ID(id), so LAST_INSERT_ID() gets a value even if the record is "ignored"
$stmtTags = $pdo->prepare('
INSERT INTO
tmpTags
(tag)
VALUES
(:tag)
ON DUPLICATE KEY UPDATE
id=LAST_INSERT_ID(id)
');
$stmtTags->bindParam(':tag', $tag);
$stmtCodeTags = $pdo->prepare('INSERT INTO tmpCode_tags (code_id, tag_id) VALUES (:codeid, LAST_INSERT_ID())');
$stmtCodeTags->bindParam(':codeid', $codeid);
// and some new records we want to insert
$testdata = array(
array('codeid'=>1, 'tags'=>'tagA tagC'), // tagA is already in the table "tags", tagC is a "new" tag
array('codeid'=>2, 'tags'=>'tagC tagD tagE') // tagC will already be inserted, tagD and atgE are "new"
);
// process (test)data
foreach($testdata as $data) {
// the parameter :codeid of $stmtCodeTags is bound to $codeid; assign it the "current" value
$codeid = $data['codeid'];
// split the tags
$tags = explode(' ', $data['tags']);
foreach($tags as $tag) {
// the parameter :tag is bound to $tag
// nothing more to do than to execute the statement
$stmtTags->execute();
// the parameter :codeid is bound to $codeid which was set to $codeid=$data['codeid']
// again nothing more to do than to execute the statement
$stmtCodeTags->execute();
}
}
unset($stmtTags);
unset($stmtCodeTags);
// let's see what we've got
$query = '
SELECT
ct.code_id, t.tag
FROM
tmpCode_tags as ct
JOIN
tmpTags as t
ON
ct.tag_id=t.id
';
foreach( $pdo->query($query, PDO::FETCH_NUM) as $row ) {
echo join(', ', $row), "\n";
}
prints
1, tagA
1, tagC
2, tagC
2, tagD
2, tagE
edit2: In case the PDO-part of the script and the prepared statements are intimidating, here's the same thing using the old php-mysql module. But I urge you to use parametrized prepared statements. Doesn't have to be PDO but I happen to like it. E.g. the mysqli module provides prepared statements as well, the old mysql module doesn't.
$mysql = mysql_connect('localhost', 'localonly', 'localonly') or die(mysql_error());
mysql_select_db('test', $mysql) or die(mysql_error());
// set up temporary table and demo data
mysql_query('CREATE TEMPORARY TABLE tmpTags (id int auto_increment, tag varchar(32), primary key(id), unique key(tag))', $mysql) or die(mysql_error());
mysql_query('CREATE TEMPORARY TABLE tmpCode_tags (code_id int, tag_id int)', $mysql) or die(mysql_error());
mysql_query("INSERT INTO tmpTags (tag) VALUES ('tagA'), ('tagB')", $mysql) or die(mysql_error());
// and some new records we want to insert
$testdata = array(
array('codeid'=>1, 'tags'=>'tagA tagC'), // tagA is already in the table "tags", tagC is a "new" tag
array('codeid'=>2, 'tags'=>'tagC tagD tagE') // tagC will already be inserted, tagD and atgE are "new"
);
// "prepare" the statements.
// This is nothing like the server-side prepared statements mysqli and pdo offer.
// we have to insert the parameters into the query string, i.e. the parameters must
// be escaped so that they cannot mess up the statement.
// see mysql_real_escape_string() for string literals within the sql statement.
$qsTags = "
INSERT INTO
tmpTags
(tag)
VALUES
('%s')
ON DUPLICATE KEY UPDATE
id=LAST_INSERT_ID(id)
";
$qsCodeTags = "
INSERT INTO
tmpCode_tags
(code_id, tag_id)
VALUES
('%s', LAST_INSERT_ID())
";
foreach($testdata as $data) {
// in this example codeid is a simple number
// let's treat it as a string literal in the statement anyway
$codeid = mysql_real_escape_string($data['codeid'], $mysql);
$tags = explode(' ', $data['tags']);
foreach($tags as $tag) {
// now $tag is certainly a string parameter
$tag = mysql_real_escape_string($tag, $mysql);
$query = sprintf($qsTags, $tag);
mysql_query($query, $mysql) or die(mysql_error());
$query = sprintf($qsCodeTags, $codeid);
mysql_query($query, $mysql) or die(mysql_error());
}
}
// let's see what we've got
$query = '
SELECT
ct.code_id, t.tag
FROM
tmpCode_tags as ct
JOIN
tmpTags as t
ON
ct.tag_id=t.id
';
$result = mysql_query($query, $mysql) or die(mysql_error());
while ( false!==($row=mysql_fetch_row($result)) ) {
echo join(', ', $row), "\n";
}
If I understand what you're attempting to achieve correctly, the second query is un-necessary - use mysql_insert_id to obtain the ID of previously inserted row, which is I presume what you need for "WHAT_GOES_HERE".

MySql and inserting data in a link table, how to?

What is the trick in MySql (Version 4) to write from PHP thtree Inserts and to be sure that we can link tables properly.
[Table1]-[LinkTable]-[Table2]
In the PHP code I create an Insert that add 1 row in table 1 and an other Insert that add an other row in table 2. I get both rows PK (primary key) with two SELECTs that return me the last row of each tables. This way I get the PK (auto-increment) from Table 1 and Table 2 and I insert it on the link table.
The problem is that is not atomic and now that I get a lot of transaction something it doesn't link properly.
How can I make a link between 2 tables in MySQL 4 that preserve data? I cannot use any stored procedure.
Well, if you have the ability to use InnoDB tables (instead of MyISAM) then you can use transactions. Here's some rough code
<?php
// Start a transaction
mysql_query( 'begin' );
// Execute the queries
if ( mysql_query( "insert into table_one (col1, col2) values('hello','world')" ) )
{
$pk1 = mysql_insert_id();
if ( mysql_query( "insert into table_two (col1, col2) values('foo', 'bar')" ) )
{
$pk2 = mysql_insert_id();
$success = mysql_query( "insert into link_table (fk1, fk2) values($pk1, $pk2)" );
}
}
// Complete the transaction
if ( $success )
{
mysql_query( 'commit' );
} else {
mysql_query( 'rollback' );
}
If you can't use InnoDB tables then I suggest looking into the Unit Of Work pattern.
your problem is here:
SELECTs that return me the last row of each tables.
if you did something like this: SELECT MAX(id) FROM table you can get a wrong id for your transaction.
This is a common many-to-many relationship
as BaileyP say you should use the function mysql_insert_id().
at http://ar2.php.net/mysql_insert_id
you can read
Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
so dont care if other process insert a row in the same table. you always will get the last id for your current connection.

Categories