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
Related
I'm building a db to hold friendships between users of my app.
The server I use to communicate with the MySQL instance is written using Node.js (Express).
My table 'friendships' consists mainly of two INTs which correspond (foreign keys) to user ids.
I want to avoid bidirectional duplicates ( 1,2 vs. 2,1) so I need to write a query which does the following:
INSERT INTO friendships f (id_1, id_2) VALUES (?, ?) IF (SELECT * FROM friendships s WHERE s.id_1=? AND s.id_2=?) IS NULL ;
Obviously this one doesn't really work. And of course I would have the last two question marks have opposite values compared to the first ones, and a UNIQUE key on the ids (id_1, id_2).
The usual answer for these kind of questions is "just order your ids by size to avoid duplicates" and it's a good answer. But in my case, I want to keep record of who sent the friend request (and who approved), without using any extra variables (and extra queries).
Also, I don't want to use code for this, in order to avoid "concurrent" problems.
Thanks!
In MySQL, you can do this with a trigger that does the check. Some other databases have functional indexes, indexes on computed columns, or check constraints that help implement this functionality.
If you want to do the check in the insert, you can do:
INSERT INTO friendships(id_1, id_2)
select new1, new2
from (select ? as new1, ? as new2) t
where not exists (select 1
from friendships f
where f.id_1 = new2 and f.id_2 = new1
);
You should also have a unique index on id_1 and id_2:
create unique index idx_friendsships_id1_id2 on (id_1, id_2);
EDIT:
The basic query is:
INSERT INTO friendships(id_1, id_2)
select ?, ?
from dual
where not exists (select 1
from friendships f
where f.id_1 = ? and f.id_2 = ?
);
But you have to get the arguments in the right order, so the earlier method is less prone to error.
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`)
Following on from this: SQL INSERT from SELECT and the correct answer marked there.
I will need to be able to also check whether the row already exists, also by using the username. So would I delete and then insert or is there a better way?
And if it is delete, how do I say DELETE FROM table WHERE UserID = do the username select here
Thanks
If delete then you can use:
DELETE a FROM Avatar a LEFT JOIN User u ON a.UserID=u.UserID WHERE u.UserName='theusername'
Try REPLACE INTO instead of INSERT INTO. If the UserID is specified and is the primary key for the table, this will overwrite the row whose UserID matches what you insert.
To answer your sub-question, it would be DELETE FROM table WHERE UserID IN (SELECT UserID ...)
Side note: StackOverflow is really not an appropriate venue for learning basic SQL. If you read up first, your questions will be better and the answers correspondingly more useful.
Coming from the other question where you're doing an "insert from select", I assume you want to not insert the row that already have entries for the keys you're attempting to insert. I also assume that it's giving you some error like "duplicate key found".
With those assumptions in mine, the fix is simple: add the IGNORE keyword after INSERT, so you're query looks something like this:
INSERT IGNORE... //rest of query
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';
I'm using MySQL 4.1. Some tables have duplicates entries that go against the constraints.
When I try to group rows, MySQL doesn't recognise the rows as being similar.
Example:
Table A has a column "Name" with the Unique proprety.
The table contains one row with the name 'Hach?' and one row with the same name but a square at the end instead of the '?' (which I can't reproduce in this textfield)
A "Group by" on these 2 rows return 2 separate rows
This cause several problems including the fact that I can't export and reimport the database. On reimporting an error mentions that a Insert has failed because it violates a constraint.
In theory I could try to import, wait for the first error, fix the import script and the original DB, and repeat. In pratice, that would take forever.
Is there a way to list all the anomalies or force the database to recheck constraints (and list all the values/rows that go against them) ?
I can supply the .MYD file if it can be helpful.
To list all the anomalies:
SELECT name, count(*) FROM TableA GROUP BY name HAVING count(*) > 1;
There are a few ways to tackle deleting the dups and your path will depend heavily on the number of dups you have.
See this SO question for ways of removing those from your table.
Here is the solution I provided there:
-- Setup for example
create table people (fname varchar(10), lname varchar(10));
insert into people values ('Bob', 'Newhart');
insert into people values ('Bob', 'Newhart');
insert into people values ('Bill', 'Cosby');
insert into people values ('Jim', 'Gaffigan');
insert into people values ('Jim', 'Gaffigan');
insert into people values ('Adam', 'Sandler');
-- Show table with duplicates
select * from people;
-- Create table with one version of each duplicate record
create table dups as
select distinct fname, lname, count(*)
from people group by fname, lname
having count(*) > 1;
-- Delete all matching duplicate records
delete people from people inner join dups
on people.fname = dups.fname AND
people.lname = dups.lname;
-- Insert single record of each dup back into table
insert into people select fname, lname from dups;
-- Show Fixed table
select * from people;
Create a new table, select all rows and group by the unique key (in the example column name) and insert in the new table.
To find out what is that character, do the following query:
SELECT HEX(Name) FROM TableName WHERE Name LIKE 'Hach%'
You will se the ascii code of that 'square'.
If that character is 'x', you could update like this:(but if that column is Unique you will have some errors)
UPDATE TableName SET Name=TRIM(TRAILING 'x' FROM Name);
I'll assume this is a MySQL 4.1 random bug. Somes values are just changing on their own for no particular reason even if they violates some MySQL constraints. MySQL is simply ignoring those violations.
To solve my problem, I will write a prog that tries to resinsert every line of data in the same table (to be precise : another table with the same caracteristics) and log every instance of failures.
I will leave the incident open for a while in case someone gets the same problem and someone else finds a more practical solution.