I'm having an issue with MySQL Queries, there are a couple queries that seem to be overlapping each other. Basically, I have some PHP code (being called via a REST API) using MySQL with PDO that inserts a value into a table if it doesn't already exist. If it exists, then it updates the record instead. Pretty simple stuff, and it seems to be working 99% of the time. But on certain occasions the same record gets inserted twice (instead of being updated the second time).
I looked into the MySQL log and found that on rare occasions the script is somehow being run twice at the same time (causing overlapping queries). Notice how the process IDs 95587 and 95588 seem to be overlapping.
95587 Connect #localhost on ****
95588 Connect #localhost on ****
95587 Query SET NAMES utf8
95588 Query SET NAMES utf8
95588 Query SELECT * FROM `my_table` WHERE `data` = '89158268' LIMIT 1
95587 Query SELECT * FROM `my_table` WHERE `data` = '89158268' LIMIT 1
95588 Query INSERT INTO `my_table` (`data`, `date_created`, `date_updated`) VALUES ('89158268', '2011-08-21 17:11:10 ', '2011-08-21 17:11:10 ')
95587 Query INSERT INTO `my_table` (`data`, `date_created`, `date_updated`) VALUES ('89158268', '2011-08-21 17:11:10 ', '2011-08-21 17:11:10 ')
95588 Quit
95587 Query SELECT * FROM `another_table`
95587 ... more queries
This is the order of the code:
SELECT * FROM my_table WHERE data = '89158268' LIMIT 1
if record found, then update my_table
else if record not found, insert new record into my_table and then continue other queries
Can anyone think of a reason why these 2 mysql processes seem to be overlapping each other? Any help is greatly appreciated. Thank you.
Try locking the table before the select:
LOCK TABLE my_table WRITE;
SELECT * FROM `my_table` WHERE `data` = '89158268' LIMIT 1;
INSERT INTO `my_table` (`data`, `date_created`, `date_updated`);
UNLOCK TABLES;
This will avoid any other MySQL session from using the my_table table, and avoid this race condition.
Related
i have a bunch of tables in one database. i am using three for testing and have created the following sqls
SELECT DISTINCT bb_users.user_nickname FROM bb_users
JOIN bucket_list ON bb_users.username = bucket_list.author
WHERE bucket_list.status=(:s) ORDER BY bucket_list.author ASC LIMIT 0, 30
This returns the user_nickname from the bb_users table and works as expected
I then use the following to do so on a table named music
SELECT DISTINCT bb_users.user_nickname FROM bb_users
JOIN music ON bb_users.username = music.author
WHERE music.status=(:s) ORDER BY music.author ASC LIMIT 0, 30
i run the sql query with the prepared statement:
//$sql ->the sql statements from above
$prep=$conn->prepare($sql);
$exec=$prep->execute(array(":s"=>"active")); var_dump($exec);
while($fetch=$prep->fetch(PDO::FETCH_ASSOC)){
//do stuff
}
the music tables outputs nothing and using php PDO the execute method returns false
both of these lines of code are dynamically created so i am very confused as to how one works and the other does not
any help is greatly appreciated
Changed the collation code from utf8_unicode_ci to utf8_general_ci
I am attempting to use a prepared statement in combination with a cross table update. I have prepared a sample script that is representative of our larger database. This first section does what I want without a prepared statement, but I am hoping to avoid copy/pasting this for every column of my data.
SET SESSION group_concat_max_len = 1000000000;
drop table if exists update_test;
create table update_test(
time_index decimal(12,4),
a varchar(20),
b varchar(20),
c varchar(20));
insert into update_test(time_index) values(20150101.0000),(20150101.0015),(20150101.0030);
drop table if exists energy_values;
create table energy_values(
time_stamp decimal(12,4),
site_id varchar(5),
energy int);
insert into energy_values
values(20150101.0000,'a',100),(20150101.0000,'b',200),(20150101.0000,'c',300),
(20150101.0015,'a',400),(20150101.0015,'b',500),(20150101.0015,'c',600),
(20150101.0030,'a',700),(20150101.0030,'b',800),(20150101.0030,'c',900);
drop table if exists update_test_sites;
create table update_Test_sites(
sites varchar(5));
insert into update_test_sites values
('a'),('b'),('c');
update update_test, energy_values, update_test_sites
set update_test.a=energy_values.energy
where update_test.time_index = energy_values.time_stamp
and energy_values.site_id ='a';
update update_test, energy_values, update_test_sites
set update_test.b=energy_values.energy
where update_test.time_index = energy_values.time_stamp
and energy_values.site_id ='b';
update update_test, energy_values, update_test_sites
set update_test.c=energy_values.energy
where update_test.time_index = energy_values.time_stamp
and energy_values.site_id ='c';
select * from update_test;
Which is why I have attempted something like this as a replacement for the update functions. However, I often get a syntax error report. Can anyone identify where I am going wrong? It would be much appreciated!
SELECT
concat(
'update update_test, energy_values, update_test_sites
set update_test.',sites,'=energy_values.energy
where update_test.time_index = energy_values.time_stamp
and energy_values.site_id = ',sites,';
select * from update_test;')
from update_test_sites
where sites = 'a'
INTO #sql;
PREPARE stmt FROM #sql;
EXECUTE stmt;
I've never seen "SELECT INTO" work that way. In my experience, it is used like so:
SELECT [field_list] INTO [variable_list]
FROM [some_table]
[etc...]
I don't think it can be used to store a resultset like it appears you are attempting.
With some tweaking and doing this in a stored procedure, you could use a cursor to iterate over the results to prepare and execute each generated statement individually.
I have tested this query in phpmyadmin & it returns exactly what I'm looking for...it duplicates row1 & updates the title to DUPLICATE.
$sql = "CREATE TEMPORARY TABLE tmp
SELECT `unit_id`,
`title`,
`status_id`,
`category_id`,
`tags`,
`access_id`
FROM unit_genData
WHERE `unit_id`='1';
ALTER TABLE tmp
DROP COLUMN `unit_id`;
UPDATE tmp
SET `title` = 'DUPLICATE';
INSERT INTO unit_genData
SELECT 0,tmp.*
FROM tmp;
DROP TABLE tmp;";
Then I go and add it to a php page, and...
You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'ALTER TABLE tmp DROP COLUMNunit_id; UPDATE tmp ' at line 10
Why am I getting this error?
use mysqli_multi_query() for executing multiple queries.
$sql = "CREATE TEMPORARY TABLE tmp
SELECT `unit_id`,
`title`,
`status_id`,
`category_id`,
`tags`,
`access_id`
FROM unit_genData
WHERE `unit_id`='1';
ALTER TABLE tmp
DROP COLUMN `unit_id`;
UPDATE tmp
SET `title` = 'DUPLICATE';
INSERT INTO unit_genData
SELECT 0,tmp.*
FROM tmp;
DROP TABLE tmp;";
$mysqli->multi_query($sql);
By default, PHP disables multiple statements in a single query.
Either run the two statements separately, or you can use mysql_multi_query.
http://php.net/manual/en/mysqli.quickstart.multiple-statement.php
(Question doesn't specify which MySQL interface is used.)
Note that if your code is subject to SQL Injection vulnerabilities, then enabling multiple statements per query can throw open the door to a whole boatload of nefariousness... ala Little Bobby Tables http://xkcd.com/327/.
If you're for some reason using mysql functions pass CLIENT_MULTI_STATEMENTS as mysql_connect's 5th parameter to allow multiple statements in your queries.
More info here.
$res = $db->query("SELECT COUNT(*) as cnt FROM table")->fetchAll(PDO::FETCH_ASSOC);
if ( $res[0]['cnt'] == 0 ) {
$db->query("
INSERT INTO table (col)
VALUES
('value1'),
('value2'),
('value3'),
....
");
}
Suppose 2 users requested this code same time,
So, for first user, count will return 0 and INSERT query will executed, but there is possible that while first insert executed, for second user, count return also 0 ? (and in this case second insert query will also executed, what I don't need).
If this is possible, how to prevent this? Using Transactions will help in such cases ?
So, you want to insert only if there are no records in the table. Transactions won't help you.
You can use a WRITE LOCK:
Only the session that holds the lock can access the table. No other session can access it until the lock is released.
https://dev.mysql.com/doc/refman/5.0/en/lock-tables.html
Use the following method:
mysql> LOCK TABLES t1 WRITE;
mysql> SELECT COUNT(*) FROM t1;
mysql> INSERT INTO t1 (col)
VALUES
('value1'),
('value2'),
('value3');
mysql> UNLOCK TABLES;
If another session tries to execute the command LOCK TABLES t1 WRITE;, it will wait until the first session finishes. So COUNT will always return the correct value.
i have any sql command for create backup from table's fields and i'm using this bewlow command in phpmyadmin:
SQL:
CREATE TEMPORARY TABLE tmptable_1 SELECT * FROM sale_khomsi;
UPDATE tmptable_1 SET id= NULL , faal= 1;
INSERT INTO sale_khomsi SELECT * FROM tmptable_1;
DROP TEMPORARY TABLE IF EXISTS tmptable_1;
this work correctly but after runing this command into php code such az:
PHP:
$reslut=mysql_query("CREATE TEMPORARY TABLE tmptable_1 SELECT * FROM sale_khomsi;
UPDATE tmptable_1 SET id= NULL , faal= 1;
INSERT INTO sale_khomsi SELECT * FROM tmptable_1;
DROP TEMPORARY TABLE IF EXISTS tmptable_1;");
not working .
result of MYSQL into PHPMYADMIN:
CREATE TEMPORARY TABLE tmptable_1 SELECT * FROM sale_khomsi;# 4 rows affected.
UPDATE tmptable_1 SET id= NULL , faal= 1;# 4 rows affected.
INSERT INTO sale_khomsi SELECT * FROM tmptable_1;# 4 rows affected.
DROP TEMPORARY TABLE IF EXISTS tmptable_1;# MySQL returned an empty result set (i.e. zero rows).
There is no option for executing multi-query in mysql. But if you go for mysqli it is there.
mysqli_multi_query()
But if want to use mysql only mean you can go for procedure for these kind of things.
phpMyAdmin uses ; as a delimiter between calls to the database. Therefore entering that string in phpMyAdmin will cause 4 queries to run, not one massive query as you have. You need to break it down.
$result = mysql_query("CREATE TEMPORARY TABLE tmptable_1 SELECT * FROM sale_khomsi");
$result = mysql_query("UPDATE tmptable_1 SET id= NULL , faal= 1");
$result = mysql_query("INSERT INTO sale_khomsi SELECT * FROM tmptable_1");
$result = mysql_query("DROP TEMPORARY TABLE IF EXISTS tmptable_1");
That should give you the desired result. Of course you need to check the outcome of result to make sure the query was successful.