Using IF statements to avoid adding duplicates with an SQL query? - mysql

I'm trying to learn about databases and SQL, and this is an issue I'm having trouble with: how do I detect if a new entry is a duplicate, and if it is, not discard the new entry, but merge it with the old one?
An example makes it clearer. Let's say that I'm making a database of my video game collection. My columns are 'Title' (varchar) and then a boolean column for each platform I own the game on, since some games are on multiple platforms.
I buy World of Goo, and go to my database to say
INSERT INTO `collections`.`games` (`Title`,`Windows`) VALUES ('World of Goo','1');
Easy. But six months later, I buy it again on Android, because I really like that game and want to play it in bed. What I want to do now is write a query that says
IF (select * from `games` where title = 'World of Goo') {
UPDATE `games`
SET `Android` = '1'
WHERE `title` = 'World of Goo';
} ELSE {
INSERT INTO `collections`.`games` (`Title`,`Android`) VALUES ('World of Goo','1');
}
(I know the first line is wrong, I'd need to write "if query returns 1 or more results", but ignore that for now.)
Now... I know I could do this with a PHP script. But I don't want to use a more complex solution than is necessary -- is there a way do this in SQL alone?
(And this is an example of a problem, I know that in reality I'd remember that I owned a game and just write the query to update it.)

MySQL has implemented an UPSERT statement using INSERT ... ON DUPLICATE KEY UPDATE.
INSERT INTO collections.games (Title, Android)
VALUES ('World of Goo', '1')
ON DUPLICATE KEY UPDATE Android = '1'
but in order to work the statement above, you need to make column Title unique.
ALTER TABLE games ADD CONSTRAINT games_uq UNIQUE (Title)

I would use insert into ... on duplicate key
INSERT INTO games (Title,Android) VALUES ('World of Goo','1') ON DUPLICATE KEY UPDATE Android=1
Remember that your Title column has tu be UNIQUE. If it is not, let it be with:
CREATE UNIQUE INDEX unique_title
ON games (Title)
Anyways, i think your model is not the best, since if youu consider a new platform in the future, you will have to alter the table, and probaly update many records.
I would prefer a games table, a platforms table, and a game_rel_platform table where you put an entry for every gameid-platformid pair

Noticed by the tags you are using MySQL. My suggestion is to use INSERT INTO ... ON DUPLICATE KEY UPDATE and have title as primary key (declared as UNIQUE):
INSERT INTO `collections`.`games` (`Title`,`Windows`) VALUES ('World of Goo','1') ON DUPLICATE KEY UPDATE `Windows`=VALUES(`Windows`)

Related

MySQL cascading tables

(My website is built using PHP and MySQL.)
My DB structure for users is mainly composed of 2 tables: "doctors" and "clients".
However, in order to integrate a chat system, I need to create a third table, namely 'chat_users', which combines both doctors and clients: fields of 'chat_users' table are
userid (unique integer),
username,
type (0:client, 1:doctor),
refid (id of the user in the associated clients or doctors table)
But I do not want to insert/delete/update this table manually each time a client or doctor is inserted/updated/deleted. I heard about cascading table some time ago...
What would be the best way performance-wise to do so? How can I achieve it?
I'm not sure you'll consider this an "answer", but may I comment on your database architecture?
You will be much happier in the long run having the following tables:
user_account: (ua_id, ua_email, ua_username, ua_password, etc.)
doctor: (d_id, ua_id, etc.)
customer: (c_id, ua_id, etc.)
In other words, have your relation going the other way. Then if you would like to be able to delete a doctor or customer by simply deleting the user_account, you can add the following relational constraint:
ALTER TABLE `doctor`
ADD CONSTRAINT `doctor_fk_user_account` FOREIGN KEY (`ua_id`) REFERENCES `user_account` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE `customer`
ADD CONSTRAINT `customer_fk_user_account` FOREIGN KEY (`ua_id`) REFERENCES `user_account` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
What you need is an AFTER INSERT Trigger. This would allow you to create new users. In case if you want it to be updated on update and deleted on delete of the original record then you need those triggers as well.
CREATE TRIGGER `chat_users_insert` AFTER INSERT ON `doctors`
FOR EACH ROW BEGIN
INSERT INTO `chat_users` SET user_id= NEW.id;
END;
The above would insert a record and set the value of id. http://dev.mysql.com/doc/refman/5.0/en/trigger-syntax.html can give you exact syntax. Let me know if you need any specific clarifications.
I know, this is not exactly an answer to your question but what about using an old fashioned view instead? This would save you from storing redundant data altogether.
CREATE VIEW chat_users AS
SELECT drid uid, drid userid, drname username, 0 FROM doctors
UNION ALL
SELECT clid+100000, clid, clname, 1 FROM clients
This view will have unique uids only if you don't have more than 100000 doctors in your table (otherwise choose a higher offset). The advantage of this approach would be that there is no dependent table data to maintain.
"I do not want to insert/delete/update this table manually each time a client or doctor is inserted/updated/deleted."
Why are you fretting about this? Just do it. You have application requirements that mandate it, so unless you can figure out how to unify your client and doctor tables, you will need a third that relates to your chat function.
The difficulty of adding this in an application framework is almost zero, it's just the case of creating an additional record when a client or doctor is created, and removing it when their respective record is deleted.
The other answers here propose using views or triggers to obscure what's really happening. This is generally a bad idea, it means your application isn't in charge of its own data, basically handing over control of certain application logic functions to the database itself.
The best solution is often the most obvious, as that leads to fewer surprises in the future.

INSERT ON DUPLICATE KEY UPDATE as substitute for UPDATE

This question is somewhat about "best practices", but also a search for potential problems. I would like to be able to run an update on multiple fields and assign different values without running multiple queries and not using a super complex query. So, what I've done is created a table with a primary key and the "name" column as a unique key.
Now, when I want to update multiple columns with different values, I can run a query like this:
INSERT INTO my_table (name, description) VALUES ('name', 'mydescription'), ('name2', 'description2') ON DUPLICATE KEY UPDATE description = VALUES(description)
Is this a bad idea? Is there a better way to do this? Are the standards police going to come arrest me?
Edit: I did just notice one potential issue with this, being a race condition. If one user removes a row while another user is editing it and they save the information, the edit will recreate the row. (Which could be used as a feature or a bug.)
Further to my comment above (linking to a question where another poster advises of the performance impact from using INSERT ... ON DUPLICATE KEY UPDATE where the records are known to exist), one could use the multiple-table UPDATE syntax with a table materialised from constants using UNION:
UPDATE my_table JOIN (
SELECT 'name' AS name, 'mydescription' AS description
UNION ALL
SELECT 'name2', 'description2'
) t USING (name) SET my_table.description = t.description

Update a row in mysql and drop the row if it creates a duplicate as defined by the unique key

I've got a MySQL table that has a lot of entries. Its got a unique key defined as (state, source) so there are no duplicates for that combination of columns. However now I am realizing that much of the state data is not entered consistently. For example in some rows it is entered as "CA" and others it might be spelled out as "California."
I'd like to update all the entries that say "California" to be "CA" and if it creates a conflict in the unique key, drop the row. How can I do that?
You may be better off dumping your data and using an external tool like Google Refine to clean it up. Look at using foreign keys in the future to avoid these issues.
I don't think you can do this in one SQL statement. And if you have foreign key relationships from other tables to the one you are trying to clean-up then you definitely do not want to do this in one step (even if you could).
CREATE TABLE state_mappings (
`old` VARCHAR(64) NOT NULL,
`new` VARCHAR(64) NOT NULL
);
INSERT INTO state_mappings VALUES ('California', 'CA'), ...;
INSERT IGNORE INTO MyTable (state, source)
SELECT sm.new, s.source from states s JOIN state_mappings sm
ON s.state = sm.old;
// Update tables with foreign keys here
DELETE FROm MyTable WHERE state IN (SELECT distinct old FROM state_mappings);
DROP TABLE state_mappings;
I'm no SQL pro, so these statements can probably be optimized, but you get the gist.

mysql inserting with foreign key

i do have a form field which includes values which will be put in different tables in mysql.
they are all connected with foreign keys.
how do i put these values to different tables.
pseudo tables:
users_table:
userId|userlogin
user_info:
info_id|userId|name|surname
user_contact:
contact_id|userId|phone|email
form includes:
userlogin
name
surname
phone
email
in my research, i found out that i can use mysql_insert_id to link the FKs, but i wonder if that can cause problems if there is high load in the website (diff. requests sent at the same time).
i also found out that i can set triggers to create new fk values:
CREATE TRIGGER ins_kimlik AFTER INSERT ON hastalar
for each row
insert into hasta_kimlik set idhasta = new.idhasta
but i don't know how to add data to them. i can use
UPDATE table SET (name, surname) VALUES ('John', 'Brown') WHERE info_id = LAST_INSERT_ID();
but it doesn't feel the native way.
what is the best practise?
i found out that i can use mysql_insert_id to link the FKs, but i wonder if that can cause
problems if there is high load in the website (diff. requests sent at the same time).
mysql_insert_id returns the last auto-increment value generated by the database connection currently in use.
It doesn't matter what other processes do on other connections. It is safe. You'll get the right value.
but it doesn't feel the native way.
nope. The right way is :
INSERT user
get id
INSERT user_info
If the tables are connected by foreign keys shouldn't you just start with the basic table (users_table here) and then add in either user_info table and then in user_contact table, or the other way around. As long as you have filled in the table that has the primary key of the fk's in the other tables, then you can add easily.
INSERT SQL command:
INSERT INTO table_name (column1, column2, column3,...) VALUES (value1,
value2, value3,...)
Is that what you were asking?

mysql replace into alternative

i'm currently using a replace into statement, I have a unique field which will cause it to UPDATE rather than INSERT if it finds a duplicate...
Problem is if it finds a duplicate i can't get to update on a few columns, it just wipes the lot.
Is there a similar "one statement" method where I can just UPDATE what I want?
I've found merge into but don't undertsnad the first bit about merge into table using table
You're going to want to use the INSERT...ON DUPLICATE KEY UPDATE syntax.
http://dev.mysql.com/doc/refman/5.1/en/insert-on-duplicate.html
Here's an example that will try to create a record with an id, birthday, and name. If a record with the id field exists, it will do the update specified. The table has lots of other fields like email address, zip code, etc. I want to leave those fields alone if I update. (REPLACE INTO would lose any of that data if I didn't include it in the REPLACE INTO statement.)
INSERT INTO user (userid,birthday,first_name,last_name)
VALUES (1234,'1980-03-07','Joe','Smith')
ON DUPLICATE KEY UPDATE
birthday = '1980-03-07',
first_name = 'Joe',
last_name = 'Smith';