MariaDB INSERT INTO... SELECT... ON DUPLICATE KEY UPDATE affecting 0 rows - php

I've created the following table in MariaDB
Table creation
CREATE TABLE `email_templates_pending` (
`template_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`template_name` varchar(100) NOT NULL,
`template_data` text,
`modify_type` varchar(16) NOT NULL,
`modify_by` varchar(50) NOT NULL,
`modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`template_id`),
UNIQUE KEY `template_name` (`template_name`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
And inserted a row in that table with the template_id = 1. I wanted to update the row using the following INSERT INTO...SELECT...ON DUPLICATE KEY UPDATE statement below
SQL Statement
INSERT INTO email_templates_pending
(template_id, template_name, template_data, modify_by, modify_type)
SELECT template_id, template_name, template_data, 'test#test.com', 'Deleted'
FROM email_templates WHERE template_id= '1'
ON DUPLICATE KEY UPDATE modify_type='Deleted'
However, when I run the statement it returns successful but with 0 rows affected. I have another similar table with different column names that works as intended. I've ensured that the template_id is the primary key, so I'm not sure what else the issue could be?

You have 1 row in email_templates_pending, but no rows in email_templates.
That may be the reason why 0 row affected. No row is in the source table.
INSERT INTO email_templates_pending ...
SELECT ... FROM email_templates
If you just want to update for id = 1, you can use this:
INSERT INTO email_templates_pending (template_id, template_name, template_data, modify_by, modify_type)
SELECT template_id, template_name, template_data, 'test#test.com', 'Deleted' FROM email_templates_pending
ON DUPLICATE KEY UPDATE modify_type='Deleted';
If you just need to do UPDATE, may be a direct UPDATE statement can also be used.
UPDATE email_templates_pending SET modify_type='Deleted' WHERE template_id= '1';

If id#1 existed and was already marked as "Deleted", you would correctly get "0 rows affected".
To prove that this is not the case, let's see the output from
SELECT template_id, template_name, template_data, modify_by, modify_type
FROM email_templates WHERE template_id= '1' ;

Related

Retrieving last inserted ID for InnoDB composite key table

I have the following table:
CREATE TABLE `my_table` (
composite_pk1 INT NOT NULL ,
composite_pk2 INT NOT NULL ,
data VARCHAR(255) NOT NULL ,
primary key (composite_pk1, composite_pk2)
) ENGINE=InnoDB;
For a given composite_pk1, I wish composite_pk2 to act as an autoincrement primary key. I don't wish to lock the table, and as such plan on using a trigger such as the following:
DELIMITER $$
CREATE TRIGGER my_trigger BEFORE INSERT ON my_table
FOR EACH ROW BEGIN
SET NEW.composite_pk2 = (
SELECT IFNULL(MAX(composite_pk2), 0) + 1
FROM issue_log
WHERE composite_pk1 = NEW.composite_pk1
);
END $$
I can now insert a record:
$stmt=$myDB->prepare('INSERT INTO my_table(composite_pk1, data) VALUES (?,?)');
$stmt->execute([123,'hello']);
How do I get the last inserted composite_pk2? PDO::lastInsertId only works with native autoincrement tables (i.e. not the trigger approach). I "could" later do a SELECT query to get the max value, however, there is no guarantee that another record has snuck in.
You can make composite_pk2 an unique key with auto_increment:
CREATE TABLE `my_table` (
composite_pk1 INT NOT NULL ,
composite_pk2 INT NOT NULL unique auto_increment,
data VARCHAR(255) NOT NULL ,
primary key (composite_pk1, composite_pk2)
) ENGINE=InnoDB;
Now last_insert_id() will return the recently created id for composite_pk2.

MySQLi does not return error code

In my MySQL database I have a table "table1" with unique constraint set on column "name" - I want to prevent duplicate names.
If there's already name 'John' in table this code:
$db=new mysqli(...);
$sql="INSERT INTO table1 SET id=10,name='John'";
if(!$db->query($sql))
{
if($db->errno==1062)
{
throw new InsertNonUniqueException(...);
}
else
{
throw new InsertException(...);
}
}
should throw InsertNonUniqueException() (my own exception). Instead, it throws InsertException().
Execution of query returns false and execution enters the if() loop. Also $db->row_affected is -1 but problem is that $db->errno is always O (it should be 1062)!!! So I can't detect that my insert error was caused by violating unique key constraint on name column!
I don't know why mysqli does not return 1062 code when unique key constraint violation occurs!
I can't leave a comment, thus going to ask you here.
Please provide the result of SHOW CREATE TABLE table1;
I can't reproduce your problem using your code and next table:
CREATE TABLE `table1` (
`name` varchar(11) COLLATE utf8_unicode_ci NOT NULL,
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
Am I the only one around here that thinks you have an error in your SQL syntax?.. There is no room for SET in INSERT INTO, because you can only use SET in UPDATE statements (assuming you habe MySQL in version 5.5 or below).
INSERT INTO syntax is like the following (as described in the docs):
INSERT [LOW_PRIORITY | HIGH_PRIORITY] [IGNORE]
[INTO] tbl_name [(col_name,...)]
SELECT ...
[ ON DUPLICATE KEY UPDATE col_name=expr, ... ]
OR
INSERT INTO tbl_temp2 (fld_id)
SELECT tbl_temp1.fld_order_id
FROM tbl_temp1 WHERE tbl_temp1.fld_order_id > 100;
Try it like this:
<?php
$sql="INSERT INTO table1 (id, name) VALUES ('10', 'John')";
...
step 1
make sure that the table has a unique key
SHOW CREATE TABLE table1
expected result
CREATE TABLE `table1` (
`id` INT(11) default NULL,
`name` varchar(11) COLLATE utf8_general_ci NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci
if there is UNIQUE KEY name (name) we have a unique key
step 2
try to change your code
$db = new mysqli(...);
// first insert
if( !$db->query("INSERT INTO table1 (id, name) VALUES (10, 'John')") ) {
throw new Exception($db->error);
}
// second insert (for me raise: Duplicate entry 'John' for key 'name')
if( !$db->query("INSERT INTO table1 (id, name) VALUES (11, 'John')") ) {
throw new Exception($db->error);
}
Please, try these two steps
Side note: if you have name and id as duplicates, only the first duplicate encountered will be returned in the message.
The only issue i have with your code is that:
having setup your table and columns.
I setup a unique index on the table. I did .. stuff on a two column table that ensure it works.
You missed the 'new'
keyword when you 'throw exceptions'.
this is the only error with your posted code that i could find.
i.e: throw new Exception('Division by zero.'); // example taken from PHP manual.

Deleting a row in certain conditions with mysql

I want to automatically delete rows when the table (shown below) gets a new insert, if certain conditions are met.
When:
There are rows referring to the same 'field' with the same 'user_id'
Their 'field', 'display' and 'search' columns are the same
Simply, when the rows would become duplicates (except the 'group_id' column) the non null 'group_id' should be deleted, otherwise a row should be updated or inserted.
Is there a way to set this up in mysql (in spirit of "ON DUPLICATE do stuff" combined with unique keys etc.), or do I have to explicitly check for it in php (with multiple queries)?
Additional info:
There should always be a row with NULL 'group_id' for every possible 'field' (there's a limited set, defined elsewhere). On the other hand there might not be one with a non null 'group_id'.
CREATE TABLE `Views` (
`user_id` SMALLINT(5) UNSIGNED NOT NULL,
`db` ENUM('db_a','db_b') NOT NULL COLLATE 'utf8_swedish_ci',
`field` VARCHAR(40) NOT NULL COLLATE 'utf8_swedish_ci',
`display` TINYINT(1) UNSIGNED NOT NULL,
`search` TINYINT(1) UNSIGNED NOT NULL,
`group_id` SMALLINT(6) UNSIGNED NULL DEFAULT NULL,
UNIQUE INDEX `user_id` (`field`, `db`, `user_id`),
INDEX `Views_ibfk_1` (`user_id`),
INDEX `group_id` (`group_id`),
CONSTRAINT `Views_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `User` (`id`) ON
UPDATE CASCADE ON DELETE CASCADE
)
COLLATE='utf8_swedish_ci'
ENGINE=InnoDB;
I think you need to revise your logic. It makes no sense to Insert a row only to delete another row. Why not just update the Group_ID field in the duplicate row to what is being inserted? Below is a rough idea of how I would go about it.
N.b. I haven't done much work with MySQL and cannot get the below to run on SQLFiddle, but based on the MySQL docs I can't work out why. Perhaps someone more versed in MySQL can correct me?
SET #User_ID = 1;
SET #db = 'db_a';
SET #Field = 'Field';
SET #Display = 1;
SET #Search = 1;
SET #Group_ID = 1;
IF EXISTS
( SELECT 1
FROM Views
WHERE User_ID = #User_ID
AND DB = #DB
AND Field = #Field
AND Group_ID IS NOT NULL
)
THEN
UPDATE Views
SET Group_ID = #Group_ID,
Display = #Display,
Search = #Search
WHERE User_ID = #User_ID
AND DB = #DB
AND Field = #Field
AND Group_ID IS NOT NULL
ELSE
INSERT INTO Views (User_ID, DB, Field, Display, Search, Group_ID)
VALUES (#User_ID, #DB, #Field, #Display, #Search, #Group_ID)
END IF;
Alternatively (and my preferred solution), add a Timestamp field to your table and create a view as follows:
SELECT v.User_ID, v.DB, v.Field, v.Display, v.Search, v.Group_ID
FROM Views v
INNER JOIN
( SELECT User_ID, DB, Field, MAX(CreatedDate) AS CreatedDate
FROM Views
WHERE Group_ID IS NOT NULL
GROUP BY User_ID, DB, Field
) MaxView
ON MaxView.User_ID = v.User_ID
AND MaxView.DB = v.DB
AND MaxView.Field = v.Field
AND MaxView.CreatedDate = v.CreatedDate
WHERE v.Group_ID IS NOT NULL
UNION ALL
SELECT v.User_ID, v.DB, v.Field, v.Display, v.Search, v.Group_ID
FROM Views v
WHERE v.Group_ID IS NULL
This would allow you to track changes to your data properly, without compromising the need to be able to view unique records.
delete group_id from Views where group_id != 'NUll'
Your question is not very good to understand, so I'm not sure this is what you want:
DELETE FROM Views WHERE # delete from the table views
group_id IS NOT NULL AND # first condition delete only rows with not null group_id
(SELECT count(*) as tot FROM Views GROUP BY group_id) = 1 # second condition count the difference in group id
If that's not what you want, please update your question with more details...

what's wrong with this SQL statement causing column count doesn't match value count at row 1?

Mysql table (migration_terms) fields are as follows
oldterm count newterm seed
I used the following create table statment.
CREATE TABLE `migration_terms`
(
`oldterm` varchar(255) DEFAULT NULL,
`count` smallint(6) DEFAULT '0',
`newterm` varchar(255) DEFAULT NULL,
`seed` int(11) NOT NULL AUTO_INCREMENT, PRIMARY KEY (`seed`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
And It works, no problems there.
but then when I used the following insert into statement to populate it;
"INSERT INTO migration_terms
SELECT looseterm as oldterm,
COUNT(seed) AS count
FROM looseterms
GROUP BY looseterm
ORDER BY count DESC "
I get this error;
Column count doesn't match value count at row 1
I cannot figure out why?
If you need the table structure of the looseterms table, it was created by the following create table statement.
CREATE TABLE looseterms
(
`seed` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`looseterm` varchar(255)
)
You need to specify the columns if your select statement has fewer columns than the table
"INSERT INTO migration_terms
(oldterm,
count)
SELECT looseterm AS oldterm,
Count(seed) AS count
FROM looseterms
GROUP BY looseterm
ORDER BY count DESC "
From MySql docs on Insert Syntax
If you do not specify a list of column names for INSERT ... VALUES or
INSERT ... SELECT, values for every column in the table must be
provided by the VALUES list or the SELECT statement. If you do not
know the order of the columns in the table, use DESCRIBE tbl_name to
find out.
Your insert is adding 2 columns of data, whereas your table's definition has 4 columns

PHP: use id to another column

Ok, so i have a normal query that inserts to the database.
mysql_query("INSERT INTO users_pm_in (uID, msg) VALUES ('$uID', '$msg')");
Now this table has also a column called "id" with auto_increment & primary key.
When it inserts it auto makes number for the column in the row. Now I want this number, and put it in column dialog, in the same row. So the inserted row have the same number/id in "id" and "dialog". How can i do that?
Not sure if this can be done in one query (or why you even want to do this), but you can use this:
mysql_query("INSERT INTO users_pm_in (uID, msg) VALUES ('$uID', '$msg')");
mysql_query("UPDATE users_pm_in SET dialog = id WHERE id = '".mysql_insert_id()."');
Be sure to escape the variables properly also.
I think it would be easier to remove the autoincrement and add the id+dialog value yourself.
Check out mysql_insert_id()
You can do this, altough it's not very efficient...
Supose you have this table:
CREATE TABLE `test` (
`id` INT(10) NOT NULL AUTO_INCREMENT,
`a` INT(10) NULL DEFAULT '0',
`b` INT(10) NULL DEFAULT '0',
PRIMARY KEY (`id`)
)
ENGINE=MyISAM
ROW_FORMAT=DEFAULT
You can perform the following query:
INSERT INTO test (a, b) SElECT IFNULL((MAX(id) +1),1), 200 FROM test;
Notice that "200" is some random value that will be inserted on "b" column.

Categories