I have a table in a mySQL database we'll call 'tbl' where the fields are:
id, userID, favorite, emailID
The id is auto incremental. The userID stores an integer. The favorite is either yes or no. The emailID stores an integer.
I am programing in PHP and would like to have one query used to query the database that checks if the userID and emailID combination exists. If it does then update the favorite field with a yes or no value that comes from a form that is passed into the query dynamically. If it does not exist then insert the combination into the database.
Therefore if I had:
<?php
$userID = 34;
$emailID = 395;
$favorite = "yes"; // could be yes or no.
I don't believe the query below is correct but gets the idea of what I am trying to do:
IF NOT EXISTS
(SELECT userID, favorite, emailID
FROM tbl
WHERE ((userID = '$userID')and(emailID = '$emailID'))
INSERT INTO tbl (userID, favorite, emailID) VALUES ('$userID', '$favorite', '$emailID')
ELSE UPDATE tbl
SET favorite = '$favorite'
WHERE ((userID = '$userID')and(emailID = '$emailID'))
(please know that I know that entering values into a query like this is a security risk, this is only to help explain my question.)
What is the best way to write the query? Can it be written with one query?
It can't be written as a single query, because the primary key isn't the two columns you're looking for - it's the id column only.
If userid and emailid are unique pairs, they should be the primary key for the table -- not the id value. ORMs typically prefer that a primary key be single column for making query construction easier but the approach suffers on performance. Anything requiring more than one columns is generally referred to as a "composite" - primary key, unique constraint/index, foreign key, etc.
The ANSI means of doing this would be a MERGE statement, but MySQL doesn't support the syntax because it already has the ON DUPLICATE KEY UPDATE and REPLACE INTO to provide the same functionality.
I would drop the redundant id column and define the primary key as the combination of (userID, emailID), which means you have exactly one record for each different combination of the two fields, which seems to be what you want. Then you can use the REPLACE INTO command, which is equivalent to an INSERT but deletes any existing record if the primary key matches.
you can do it really simply in a single call - but sprocs are bad aren't they ??
drop procedure if exists insert_update_tbl;
delimiter #
create procedure insert_update_tbl
(
in p_userID int unsigned,
in p_emailID int unsigned,
out p_id int unsigned
)
proc_main:begin
set p_id = 0;
if exists (select 1 from tbl where userID = p_userID and emailID = p_emailID) then
update tbl set favorite = 1 where userID = p_userID and emailID = p_emailID;
leave proc_main;
end if;
insert into tbl (userID, emailID, favorite) values (p_userID, p_emailID, 0);
set p_id = last_insert_id();
end proc_main #
delimiter ;
Related
I have a SQL query as follows-
"INSERT INTO users(id, rank) SELECT v.user, v.vote FROM votes v WHERE
v.assertion = '$ID' ON DUPLICATE KEY UPDATE
rank = ( CASE WHEN v.vote = '1' THEN rank+50 WHEN v.vote = '-1'
THEN rank-200 WHEN v.vote = '3' THEN rank+100 ELSE rank END)"
applied on a database with a table users with and id and rank field, and a votes table with a user and vote field. I have to update the rank of the users in the users table based on their vote.
I really like this kind of query, but I've noticed a problem: every time I execute this from my PHP script the query adds a row to the users table completely empty (with only an ID, which is A_I, and a rank of 1, when usually there would be other field as well). I can't really wrap my head around why this happens.
Any help/idea?
Your table does not have a primary key first provide a primary key to id
run this sql query
alter table user add primary key (id)
and than try it will work
There are two possible reasons :
The id column is not the primary key, and probably you table doesn't have a primary key at all.
Create a primary key like this :
alter table user add primary key (id)
If you insert an value of 0 in an auto increment column, a new id is generated. An auto incremented column must not contain the value 0.
There is also a more general problem with your approach : in fact you only insert the user id and the rank, other compulsory fields in the table (username) are missing. The insert part does not seem to be valid for this reason. If you use an insert on duplicate key update, you must make sure that the result is correct which ever of insert and update is executed.
I'm trying to create a keyword system for my site but I have hit a bump in the road.
The database table has five rows DATE | USERID | AMOUNT | KEYWORD | ID
When inserting a row into the database it needs to check if the keyword already exists for that userid, if so the amount increments, if not it's inserted with a default value of 1.
So far I have:
$stmt= $conn->prepare("INSERT INTO `keywords` (keyword, userId, id) VALUES(?,?,?) ON DUPLICATE KEY UPDATE `amount` = `amount` + 1");
How do I check if there is a duplicate of the userid value and increment or insert the keyword based on the result?
Your query is:
INSERT INTO `keywords`(keyword, userId, id)
VALUES(?,?,?)
ON DUPLICATE KEY UPDATE `amount` = `amount` + 1;
That looks like what you want to do, with possibly one small change:
INSERT INTO `keywords`(keyword, userId, id)
VALUES(?,?,?)
ON DUPLICATE KEY UPDATE `amount` = coalesce(`amount`, 0) + 1;
(This is not needed if amount has a default value other than NULL.)
The other thing you need is a constraint that says that keyword is unique. So, add a unique index:
create unique index idx_keywords_keyword on keywords(keyword)
I have already asked this but did not get what I am looking for, I have many tables with column id set to auto-increment and primary key.
Table1
id, title, condition...
Table2
id, title, condition...
And I have a search box where I search for products and my query is like
$stmt = $mysqli->prepare("select * from table1 where title Like ? or id = ? ");
$stmt->bind_param('ss', $searchterm, $searchterm");
$stmt->execute();
$stmt = $mysqli->prepare("select * from table2 where title Like ? or id = ? ");
$stmt->bind_param('ss', $searchterm, $searchterm");
$stmt->execute();
So if I search for "1" then I will get all the products with id or title 1 but what I want is to have a unique identfier for each row. How can I accomplish this? As I am working with products, there should be a unique id for each product.
Or could it be possible to have a alphabet in front of the id, something like T1, T2, T3?
If you were using the Oracle table server, you'd use a database entity called a SEQUENCE to get a data-base-wide unique id value for your tables.
It's easy enough to emulate SEQUENCE entities in MySQL, though, even if it's a little clunky and not quite as space-efficient.
First, create yourself a table like this.
CREATE TABLE sequence (
sequence_id BIGINT NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`sequence_id`)
)
Then, create your other tables, the ones which will use these unique sequence_id values. Here's an example:
CREATE TABLE gadget (
sequence_id BIGINT NOT NULL,
gname VARCHAR(20) DEFAULT NULL,
gvalue VARCHAR(20) DEFAULT NULL,
PRIMARY KEY (sequence_id)
)
Here's another:
CREATE TABLE widget (
sequence_id BIGINT NOT NULL,
wname VARCHAR(20) DEFAULT NULL,
wvalue VARCHAR(20) DEFAULT NULL,
PRIMARY KEY (sequence_id)
)
Then, whenever you insert a row into your other tables, do it like this:
INSERT INTO sequence () VALUES() ;
INSERT INTO widget (sequence_id, wname, wvalue)
VALUES (LAST_INSERT_ID(), 'whatever', 'you_want');
or, for the other example table,
INSERT INTO sequence () VALUES() ;
INSERT INTO gadget (sequence_id, gname, gvalue)
VALUES (LAST_INSERT_ID(), 'another', 'gadget');
The trick is this: when you insert each (seemingly empty) row into the sequence table it updates the auto-increment sequence_id field. Then, when you use the LAST_INSERT_ID() function it retrieves the most-recently inserted value of that sequence_id.
Be very careful to keep your two INSERT INTO statements consecutive, or this will stop working properly.
This works even if many different clients of the database server are doing the inserts, for two reasons;
auto-increment is thread-safe. No two sequence numbers can be the same.
LAST_INSERT_ID() values are maintained one for each distinct connection to the MySQL database. Different programs (or different threads in a multi-threaded web application) each have their own connections, so they each have their own values of LAST_INSERT_ID().
By the way, I used bigint data for the sequence, but int will work just as well.
This question already has answers here:
MySQL 'UPDATE ON DUPLICATE KEY' without a unique column?
(3 answers)
Closed 10 months ago.
I'm trying to create more robust MySQL Queries and learn in the process. Currently I'm having a hard time trying to grasp the ON DUPLICATE KEY syntax and possible uses.
I have an INSERT Query that I want to INSERT only if there is no record with the same ID and name, otherwise UPDATE. ID and name are not UNIQUE but ID is indexed.ID isn't UNIQUE because it references another record from another table and I want to have multiple records in this table that reference that one specific record from the other table.
How can I use ON DUPLICATE KEY to INSERT only if there is no record with that ID and name already set else UPDATE that record?
I can easily achieve this with a couple of QUERIES and then have PHP do the IF ELSE part, but I want to know how to LIMIT the amount of QUERIES I send to MySQL.
UPDATE: Note you need to use IF EXISTS instead of IS NULL as indicated in the original answer.
Code to create stored procedure to encapsulate all logic and check if Flavours exist:
DELIMITER //
DROP PROCEDURE `GetFlavour`//
CREATE PROCEDURE `GetFlavour`(`FlavourID` INT, `FlavourName` VARCHAR(20))
BEGIN
IF EXISTS (SELECT * FROM Flavours WHERE ID = FlavourID) THEN
UPDATE Flavours SET ID = FlavourID;
ELSE
INSERT INTO Flavours (ID, Name) VALUES (FlavourID, FlavourName);
END IF;
END //
DELIMITER ;
ORIGINAL:
You could use this code. It will check for the existence of a particular record, and if the recordset is NULL, then it will go through and insert the new record for you.
IF (SELECT * FROM `TableName` WHERE `ID` = 2342 AND `Name` = 'abc') IS NULL THEN
INSERT INTO `TableName` (`ID`, `Name`) VALUES ('2342', 'abc');
ELSE UPDATE `TableName` SET `Name` = 'xyz' WHERE `ID` = '2342';
END IF;
I'm a little rusty on my MySQL syntax, but that code should at least get you most of the way there, rather than using ON DUPLICATE KEY.
id and name are not unique but id is
indexed. id isn't unique
How can I use ON DUPLICATE KEY to
INSERT only if there is no record with
that id and name already set else
UPDATE that record?
You can't. ON DUPLICATE KEY UPDATE needs a unique or primary key to determine which row to update. You are better off having PHP do the IF ELSE part.
edit:
If the combination of name and id IS supposed to be unique, you can create a multi-column UNIQUE index. From there you can use ON DUPLICATE KEY UPDATE.
Why not just use a stored procedure, then you can embed all the logic there are plus you have a reusable piece of code (e.g. the stored proc) that you can use in other applications. Finally, this only requires one round trip to the server to call the stored proc.
I asked this question a little earlier today but am not sure as to how clear I was.
I have a MySQL column filled with ordered numbers 1-56. These numbers were generated by my PHP script, not by auto_increment.
What I'd like to do is make this column auto_incrementing after the PHP script sets the proper numbers. The PHP script works hand in hand with a jQuery interface that allows me to reorder a list of items using jQuery's UI plugin.
Once I decide what order I'd like the entries in, I'd like for the column to be set to auto increment, such that if i were to insert a new entry, it would recognize the highest number already existing in the column and set its own id number to be one higher than what's already existing.
Does anyone have any suggestions on how to approach this scenario?
I'd suggest creating the table with your auto_increment already in place. You can specify a value for the auto_inc column, and mysql will use it, and still the next insert to specify a NULL or 0 value for the auto_inc column will magically get $highest + 1 assigned to it.
example:
mysql> create table foobar (i int auto_increment primary key);
mysql> insert into foobar values (10),(25);
mysql> insert into foobar values (null);
mysql> select * from foobar;
# returns 10,25,26
You can switch it to MySQL's auto_increment implementation, but it'll take 3 queries to do it:
a) ALTER TABLE to add the auto_increment to the field in question
b) SELECT MAX(id) + 1 to find out what you need to set the ID to
c) ALTER TABLE table AUTO_INCREMENT =result from (b)
MySQL considers altering the AUTO_INCREMENT value a table-level action, so you can't do it in (a), and it doesn't allow you to do MAX(id) in (c), so 3 queries.
You can change that with a query, issued through php, using the mysql console interface or (easiest) using phpmyadmin.
ALTER TABLE table_name CHANGE old_column_name new_column_name column_definition;
ALTER TABLE table_name AUTO_INCREMENT = highest_current_index + 1
column_definiton:
old_column_definition AUTO_INCREMENT
More info:
http://dev.mysql.com/doc/refman/5.1/en/alter-table.html
http://dev.mysql.com/doc/refman/5.1/en/create-table.html
EDIT
Always use mysql_insert_id or the appropiate function of your abstraction layer to get the last created id, as LAST_INSERT_ID may lead to wrong results.
No, stop it. This isn't the point of auto_increment. If you aren't going to make them ordered by the id then don't make them auto_increment, just add a column onto the end of the table for ordering and enjoy the added flexibility it gives you. It seems like you're trying to pack two different sets of information into one column and it's really only going to bite you in the ass despite all the well-meaning people in this thread telling you how to go about shooting yourself in the foot.
In MySQL you can set a custom value for an auto_increment field. MySQL will then use the highest auto_increment column value for new rows, essentially MAX(id)+1. This means you can effectively reserve a range of IDs for custom use. For instance:
CREATE TABLE mytable (
id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
col1 VARCHAR(256)
);
ALTER TABLE mytable AUTO_INCREMENT = 5001;
In this schema all ids < 5001 are reserved for use by your system. So, your PHP script can auto-generate values:
for ($i=1; $i<=56; $i++)
mysql_query("INSERT INTO mytable SET id = $i, col1= 'whatevers'");
New entries will use the non-reserved range by not specifying id or setting it to null:
INSERT INTO mytable SET id = NULL, col1 = 'whatevers2';
-- The id of the new row will be 5001
Reserving a range like this is key - in case you need more than 56 special/system rows in the future.
ALTER TABLE <table name> <column name> NOT NULL AUTO_INCREMENT
More info:
AUTO_INCREMENT Handling in InnoDB
Server SQL Modes