PHP: Inserting using sqlsrv pdo using triggers - php

I'm using sqlsrv pdo driver (used both 5.2 and 5.3 versions) and I'm seeing what I believe is unintended behavior. Hopefully I'm doing something wrong.
I've created 2 tables in sql express 2008. One (Locations) is a table of primary keys shared with other tables. The other table (Rooms) has 2 (or more) columns...a key column linked as a foreign key to the first table and one (or more) data columns. So it looks like:
Locations
LocationID int
1
2
3
4
5
Rooms
LocationID int, Roomname varchar(50)
2, 'living'
4, 'dining'
2,4 both link back to the Locations table.
Then I create a unique constraint on the Roomname column:
ALTER TABLE Rooms ADD CONSTRAINT UniqueRoom UNIQUE (Roomname)
Next, I created an INSTEAD OF INSERT trigger on the Rooms table:
CREATE TRIGGER [dbo].[Rooms_INSERT]
on [dbo].Rooms
INSTEAD OF INSERT
as
BEGIN
INSERT INTO dbo.Locations DEFAULT VALUES
INSERT INTO dbo.Rooms select ##IDENTITY, Roomname from inserted
END
Now I can insert data into the rooms table by:
INSERT INTO Rooms VALUES (null, 'roomname')
I use the sqlsrv PDO object to insert data through PHP:
$db = new PDO("sqlsrv:server=$serverName;Database=db", "", "");
$sql=("insert into Rooms values (null,?)");
$dbobj = $db->prepare($sql);
if($dbobj->execute(array("dining")))
print "<p>insert successful</p>";
else
print "<p>insert unsuccessful</p>";
Starting with the table listing above, I get the following results:
Modify insert statement to "insert into Rooms values (?)" - fails as expected because it expects two args, "insert unsuccessful."
Revert insert statement to normal, attempt to insert array("bedroom") - successful as expected, "insert successful."
Re-attempt insert of array("bedroom") - db insert fails, no entries are made in either table, but attempt is reported successful "insert successful."
Why is the 3rd attempt reported as successful when there were no lasting entries made to the database? Or, more specifically, how can I modify either my PHP or my SQL to properly detect when an insert like this fails due to a violation of a unique constraint?

Related

Ignore duplicate rows

I am importing some data from a csv file into MySQL and trying to ignore duplicate rows.
mysql_query("INSERT IGNORE INTO products (parent_product_url, child_product_url, swatch) VALUES ('".$row[0]."', '".$row[1]."', '".$row[2]."')");
My csv file.
polo.htm,red.htm,red.jpg
polo.htm,green.htm,green.jpg
round-neck.htm,green.htm,green.jpg
Now if I run below csv file it should ignore first three rows as they already exists in the table. It should insert only fourth row.
polo.htm,red.htm,red.jpg
polo.htm,green.htm,green.jpg
round-neck.htm,green.htm,green.jpg
v-neck.htm,red.htm,red.jpg
I prefer on duplicate key update because insert ignore ignores all errors, not just duplication errors.
Regardless of which you use, your problem is probably the lack of unique constraint/index.
You don't specify what you mean by "duplicate". Assuming you mean all the columns:
create unique index unq_products_3 on products(parent_product_url, child_product_url, swatch);
Note: there is a maximum length to the keys used for indexes, depending on the storage engine. If your columns are too long, you may need to think about other approaches.
Records are inserted again when you re-execute insert statements because the inserts are not violating any unique or primary key index. Therefore MySQL doesn't have anything to ignore.
create table products (
parent_product_url varchar(100),
child_product_url varchar(100),
swatch varchar(100)
);
-- this will enter both records
insert ignore into products values ('polo.htm', 'red.htm', 'red.jpg');
insert ignore into products values ('polo.htm', 'green.htm', 'green.jpg');
-- this will enter both records **AGAIN**
insert ignore into products values ('polo.htm', 'red.htm', 'red.jpg');
insert ignore into products values ('polo.htm', 'green.htm', 'green.jpg');
Now let's add uniqueness to parent_product_url and try again:
truncate table products;
create unique index uk_products_parent_product_url on products(parent_product_url);
insert ignore into products values ('polo.htm', 'red.htm', 'red.jpg');
insert ignore into products values ('polo.htm', 'green.htm', 'green.jpg');
This will enter only the first record. 2nd record will be ignored and a warning will be thrown. No error will be thrown.
If you desire to have a combination of the 3 columns to be unique, then you would do this (This is what Gordon Linoff has mentioned also...I am just adding more context):
alter table products drop key uk_products_parent_product_url;
create unique index uk_products_parenturl_childurl_swatch on
products(parent_product_url, child_product_url, swatch);
insert ignore into products values ('polo.htm', 'red.htm', 'red.jpg');
insert ignore into products values ('polo.htm', 'green.htm', 'green.jpg');
Now you will see only two records inserted even when you re-execute the same 2 insert statements many times.
From https://dev.mysql.com/doc/refman/5.5/en/insert.html
If you use the IGNORE keyword, errors that occur while executing the
INSERT statement are ignored. For example, without IGNORE, a row that
duplicates an existing UNIQUE index or PRIMARY KEY value in the table
causes a duplicate-key error and the statement is aborted. With
IGNORE, the row is discarded and no error occurs. Ignored errors may
generate warnings instead, although duplicate-key errors do not.
I got it solved with the help of this Answer -> Insert query check if record exists - If not, Insert it
Below is my updated query
mysql_query("INSERT INTO products (parent_product_url, child_product_url, swatch)
SELECT * FROM (SELECT '".$row[0]."', '".$row[1]."', '".$row[2]."') AS tmp
WHERE NOT EXISTS (
SELECT * FROM products WHERE parent_product_url='".$row[0]."' AND child_product_url='".$row[1]."' AND swatch='".$row[2]."'
);");

Get array of Last Inserted Ids mysql php

I have a query like
Insert into tbl(str)values('a'),('b'),('c')
if i had a single insert then by using mysqli_insert_id($con) i could get last id inserted but how get all ids inserted in this multiple insert query?
This behavior of last_insert_id() is documented in the MySQL docs:
The currently executing statement does not affect the value of
LAST_INSERT_ID(). Suppose that you generate an AUTO_INCREMENT value
with one statement, and then refer to LAST_INSERT_ID() in a
multiple-row INSERT statement that inserts rows into a table with its
own AUTO_INCREMENT column. The value of LAST_INSERT_ID() will remain
stable in the second statement; its value for the second and later
rows is not affected by the earlier row insertions. (However, if you
mix references to LAST_INSERT_ID() and LAST_INSERT_ID(expr), the
effect is undefined.)
IF you really need it you can test it using foreach with array_push
<?php
$InsetQueryArray = array(
"Insert into tbl(str) values('a')",
"Insert into tbl(str) values ('b')",
"Insert into tbl(str) values('c')"
);
$allLasIncrementIds = array();
foreach ($InsetQueryArray as $value) {
//execute it mysql
//Then use array_push
array_push($allLastIncrementIds, mysqli_insert_id($con));
}
?>
If it is just for a few rows, you could switch to sending individual inserts with last_insert_id(). Otherwise it will slow down your application notably. You could make a marker for those bulk inserts, which gets set to a number identifying this bulk insert at the bulk insert itself and you can fetch those ids later on:
insert into tbl (stuff, tmp_marker) values ("hi",1715),("ho",1715),("hu",1715);
select group_concat(id) from tbl where tmp_marker = 1715;
update tbl set tmp_marker=0 where tmp_marker=1715;
If those bulk inserts have a meaning, you could also make a table import_tbl with user and time and filename or whatever and keep the 1715 as reference to that import.
EDIT: After discussion, I would go to an import-table
CREATE TABLE import (id int(11) not null auto_increment primary key, user_id int(11), tmstmp timestamp);
When an import starts, insert that:
INSERT INTO import set user_id = $userId;
in php:
$importId = last_insert_id();
$stmt = "insert into tbl (str, import_id) values ('a',$import_id), ('b', $import_id),('c',$importId);
Then you can do whatever you want with the id of your recently imported rows.
I have not made research if a multi-row-insert is guaranteed to lead to a consecutive row of IDs, as in a combination of last_insert_id() and num_rows is presupposed. And if that stays so, even when MySQL increases parallelization. So I would see it as dangerous to depend on it.

PHP Code for fetching one row in MSSQL in each registration process

Here is my query part of my registration PHP form.
columns account,password,email and age could be inserted by registration page user and they work well but, the column account_id needs to be increased by 1 automatically with each registration process.
Table name is Account not account and column name is account_id.
$query = "INSERT Account( account,password,email,pk_,type_ ) VALUES('$username','$converted_password','$email',1,'$age')";
$query_total = mssql_query("SELECT COUNT(account_id) FROM Account");
$results_check = mssql_query($query_check);
$results_total = mssql_fetch_row($query_total);
$result_total = $results_total['0'];
It gives me a NULL value for the (account_id) column and INSERT fails.
Perform the following query on your database: (Mysql based query!! not Mssql!!)
ALTER TABLE `Account` CHANGE `account_id` `account_id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY;
this wil result in an autoincrement.
If this fails you'll probably already own duplicates, you'd have to solve this first. There are many ways for this, though it mostly depends upon connections with other tables.
After that for each insert into account do not include the account_id.
use the mysql query:
select LAST_INSERT_ID();
to retrieve the last inserted id.
within PHP you can use http://nl1.php.net/mysql_insert_id though i'd highly advice you to start looking into http://www.php.net/PDO or http://www.php.net/mysqli with prepared statements.
Because as far as i've understood in the next version of PHP the basic Mysql functions will become deprecated. And prepared statements are better/safer. (If properly used)
Set account_id to auto increment in MySQL and just don't shoot anything to the MySQL database for the field account_id. MySQL will automatically create a new ID.
Read something about auto increment:
http://dev.mysql.com/doc/refman/5.0/en/example-auto-increment.html
*edit:
Also change
$query = "INSERT Account( account,password,email,pk_,type_ ) VALUES('$username','$converted_password','$email',1,'$age')";
to
$query = "INSERT INTO Account(account,password,email,pk_,type_) VALUES('$username','$converted_password','$email',1,'$age')";

Avoid entering duplicate entries based on date, without using select statement

I am running a insert statement to insert data, but I want to check for any duplicate entries based on date and then do an entry.
All I want is if today a user enters product_name='x', 'x' is unique so that no one can enter product name x again today. But of course the next day they can.
I do not want to run a select before the insert to do the checking. Is there an alternative?
You can either use
1. Insert into... on duplicate update
2. insert.. ignore
This post will answer your question
"INSERT IGNORE" vs "INSERT ... ON DUPLICATE KEY UPDATE"
You can use the mysql insert into... on duplicate update syntax which will basically enter in a new row if one isn't there, or if the new row would have caused a key constraint to kick in, then it can be used to update instead.
Lets say you have the following table:
MyTable
ID | Name
1 | Fluffeh
2 | Bobby
3 | Tables
And ID is set as the primary key in the database (meaning it CANNOT have two rows with the same value in it) you would normally try to insert like this:
insert into myTable
values (1, 'Fluffster');
But this would generate an error as there is already a row with ID of 1 in it.
By using the insert on duplicate update the query now looks like this:
insert into myTable
values (1, 'Fluffster')
on duplicate key update Name='Fluffster';
Now, rather than returning an error, it updates the row with the new name instead.
Edit: You can add a unique index across two columns with the following syntax:
ALTER TABLE myTable
ADD UNIQUE INDEX (ID, `name`);
This will now let you use the syntax above to insert rows while having the same ID as other rows, but only if the name is different - or in your case, add the constraint on the varchar and date fields.
Lastly, please do add this sort of information into your question to start with, would have saved everyone a bit of time :)

How to Update if the data exist else insert the new data (multiple rows)

I need to create a insert and update statement, when today date is not in the database it will insert else it will update the QTY (from excel [this part I have done]) get from today.
But, there have a lots of row need to be insert and update.
1) it will check for the last 4 days in database, if there doesn't include today, it will just insert the data for today and update the last 3 days data. in the other hand, if there contain today it will just update.
P.S: I had try to use INSERT... ON DUPLICATE KEY UPDATE but it only 1 row affected.
If else statement , when i used this it only insert one row of data then the rest it just doing update.
Can give me some advise or example.
suppose you bulk copy your data from excel to a temporary table tbl and your actual table is tbl1 then do something like this
begin transaction;
if not exists(select * from tbl(updlock holdlock) where...)
begin
insert into tbl1...
else
begin
update tbl1...
end
commit;
What language are you using to do this? I have done something similar in Ruby before. I would make the column (Date in your case) unique at the database level then simply try inserting each record. When I get an exception thrown because the Date is not unique I would then proceed to update the QTY.
I found this article on mysql which says it supports multiple insert.
http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html
INSERT INTO table (a,b,c) VALUES (1,2,3),(4,5,6)
ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b);
That statement is identical to the following two statements:
INSERT INTO table (a,b,c) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE c=3;
INSERT INTO table (a,b,c) VALUES (4,5,6)
ON DUPLICATE KEY UPDATE c=9;
So if we want to edit straight, we could do something like this.
INSERT INTO table (uniquekey,data) VALUES (1,2),(4,5)
ON DUPLICATE KEY UPDATE data=VALUES(data);

Categories