ON DUPLICATE KEY: multi-column unique constraint - mysql

Right now I have:
INSERT INTO mytable (a,b,c,d) VALUES(1,2,3,4)
ON DUPLICATE KEY UPDATE c=VALUES(c),d=VALUES(d)
which works if a or b are UNIQUE keys...
But now I want to UPDATE only when another row with the pair (a,b) doesn't exist in the table (otherwise skip insertion).
Basically (a,b) shoud be UNIQUE, not (a) or (b), but both connected.
For example these rows would be valid
ID (auto-inc) | a | b | c | d
0 | 5 | 1 | 343 |466
1 | 5 | 2 | 363 |466
2 | 5 | 3 | 343 |496
3 | 7 | 1 | 343 |496
Because there's 5,1, 5,2, 5.3, 7.1 etc.
But row #2 here should be considered duplicate of row #1, so row #1 should be updated:
ID (auto-inc) | a | b | c | d
0 | 5 | 1 | 343 |466
1 | 5 | 1 | 363 |466
2 | 5 | 3 | 343 |496
3 | 7 | 1 | 343 |496
Is this possible?

make UNIQUE KEY to (a,b) not to b
ALTER TABLE tblname ADD UNIQUE (a,b)

CREATE UNIQUE INDEX index_unique_on_a_and_b ON mytable (a,b)

It's worth noting that if you try to add the ADD UNIQUE(a,b) while there is data in the table, you'll likely get a duplicate key error. Empty the table, add the unique index, and it'll work.

Related

SQL ON DUPLICATE KEY with 2 unique keys?

I have a MySQL table that goes like so:
+----+--------+-------+-------------+
| id | userID | month | lastUpdated |
+----+--------+-------+-------------+
| 1 | 5 | 1 | 2017-03-27 |
| 2 | 5 | 2 | 2017-03-22 |
| 3 | 5 | 3 | 2017-03-21 |
| 4 | 9 | 1 | 2017-03-27 |
| 5 | 9 | 2 | 2017-03-22 |
| 6 | 9 | 3 | 2017-03-21 |
+----+--------+-------+-------------+
I want to make an INSERT statement to this table but use ON DUPLICATE KEY UPDATE like this:
INSERT INTO users_last_updated
(userID, month, lastUpdated)
VALUES
(:userID, :month, :lastUpdated)
ON DUPLICATE KEY UPDATE lastUpdated = :lastUpdated
The thing is, a userID can show up multiple times and a month value can show up multiple times BUT, the uniqueness of each row is a combination of userID & month (e.g: userID = 1 & month = 3 can only appear once).
Is there a way to make a combination of 2 columns to be unique?
Thanks :)
If the unique key is userid/month, then use both of those for a unique index or constraint:
create index unq_users_last_updated_2 on users_last_updated(userId, month);
I would add that it seems strange to have month without a year.
The on duplicate key uses any and all available unique indexes, including primary keys (but not limited only to primary keys).

Update table data using foreign key

I have two tables structure like below
Table1
Serial | Src | Albumid(primarykey)
________|__________________|________
1 | /root/wewe.jpg | 20
2 | /root/wewe.jpg | 21
3 | /root/wewe.jpg | 21
4 | /root/wewe.jpg | 23
5 | /root/wewe.jpg | 18
Table2
Albumid | Albumname | AlbumCover //albumid is secondary key ref. to first table
________|__________________|________
20 | AAA | null
21 | bbb | null
31 | vcc | null
42 | ddd | null
18 | eee | null
I followed this POST two update my Albumcover in Table2 using Serial no. of first table..
create proc AddCover #Serial int
as
Begin
update Table1 set albumcover='somthing' where table1.serial = #Serial
end
Can i do like this using foregin key constraint??
You'll need to do the update on Table2. To tell it to have a condition based on values from table1, check this post for examples:
MySQL - UPDATE query based on SELECT Query

Access 2010 update query based on a conversion table

Is there any way that I can, in a single statemente, update the contents of a table based on a conversion table?
For example, if I have the following table called MyStuff
Key | Values
----+-------
1 | Apples
2 | Oranges
3 | Bananas
And supose I have the following ConversionTable
Old Key | New Key
--------+--------
1 | 101
2 | 202
3 | 303
What I'm looking for is an update SQL statement that, based on ConversionTable, would produce the following changes in MyStuff
Key | Values
----+-------
101 | Apples
202 | Oranges
303 | Bananas
Assuming A.[Key] is not an autonumber, this should work:
Update MyStuff A
INNER JOIN Conversion B
on A.Key = B.[Old Key]
Set A.[Key] = B.[New Key]

Is it possible to share a set between two tables in MySQL?

I am currently in the process of designing a database.
I have a table of 20,000+ records, which has a set in it (4 values). I also am making another table (100+ records) which will have an enum over the same set (1 value from the same set)
Example of current:
tbl1 tbl2
ID | Letters | Stuff ID | Letter | Stuff
---------------------- ---------------------
0 | A,B,C,D | ... 0 | D | ...
1 | A,B,C,D | 1 | C |
2 | C,D | 2 | A |
3 | B,C,D | 3 | D |
...
Is there a way to make sure that the sets are the same, and can I compare the enum and the set?
I also might need to add more options to the set as our data changes. Would a separate table for that set be necessary, and then an association table for that?
Example of what I just said:
tbl1 tbl2
ID | Stuff ID | LetterID | Stuff
------------ ------------------------
0 | ... 0 | 3 | ...
1 | 1 | 2 |
2 | 2 | 0 |
3 | 3 | 3 |
...
tblLetters tblLetters1 (Association table)
ID | Letter tbl1Id | letterId
------------ ------------------
0 | A 0 | 0
1 | B 0 | 1
2 | C 0 | 2
3 | D 0 | 3
...? ...
My only major concern with this is whether the size of the association table would be too big (most of the rows in tbl1 will have all 4 elements of the set).
Thank you! Sorry if I didn't explain my problem very well. I'm very green when it comes to SQL.
Your second solution seems fine, comma separated values in 1 column should normally be avoided. You might not need an ID, and I'd drop the ENUM type for the column, and use the actual type / column definition of the letter in tblLetters:
tbl1 tbl2
ID | Stuff ID | Letter | Stuff
------------ ------------------------
0 | ... 0 | D | ...
1 | 1 | C |
2 | 2 | A |
3 | 3 | D |
tblLetters tblLetters1 (Association table)
Letter tbl1Id | letter
------------ ------------------
A 0 | A
B 0 | B
C 0 | C
D 0 | D
Possibly add a FOREIGN KEY constraint to tblLetters1.letter & tbl2.letter to enforce an existing letter from tblLetters.
And 80K rows in total is not many by any standard, it should be fine (use the proper indexes though)
I'm going to take a stab at your question....
So from what I understand, you just want to make sure the tables have the "options" or "variables" in the enum and set fields.
What you can do is:
Show create table tbl1;
What you should see is
Create table tbl1
(id int unsigned,
stuff set('A','B','C','D'),
.....)
Show create table tbl2;
Create table tbl2
(id int unsigned,
stuff enum('A','B','C','D'),
.....)
All you would need to to, technically, is make sure both tables have the same variables. You can do this with a script or just be aware of it when you do an ALTER TABLE.

Update a sorting index column to move items

If I have the following table & data to allow us to use the sort_index for sorting:
CREATE TABLE `foo` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`bar_id` INT(11) DEFAULT NULL,
`sort_index` INT(11) DEFAULT NULL,
PRIMARY KEY (`id`)
);
INSERT INTO `foo` (`bar_id`, `sort_index`) VALUES
(1,1),(1,2),(1,3),(1,4),
(2,1),(2,2),(2,3),(2,4),(2,5);
I want to be able to do the following in the most efficient manner:
Move a foo entry to a given position (scoped by the bar_id)
Ensure that the sort_index is always 1 indexed and has no gaps
You should be able to move items to the beginning and end of the list and rule #2 should still be applied
It should be done entirely in queries and as few as possible (as the sets could be very large and looping over them doing individual UPDATEs is not ideal)
To clarify what I'm trying to do, lets assume the table was empty so we have the following data:
id | bar_id | sort_index
1 | 1 | 1
2 | 1 | 2
3 | 1 | 3
4 | 1 | 4
5 | 2 | 1
6 | 2 | 2
7 | 2 | 3
8 | 2 | 4
9 | 2 | 5
Then if we were to do the following moves
Foo 1 to sort_index 3
Foo 7 to sort_index 1
Foo 5 to sort_index 5
We should get the following data:
id | bar_id | sort_index
1 | 1 | 3
2 | 1 | 1
3 | 1 | 2
4 | 1 | 4
5 | 2 | 5
6 | 2 | 2
7 | 2 | 1
8 | 2 | 3
9 | 2 | 4
And SELECT * FROM foo ORDER BY bar_id, sort_index; gives us:
id | bar_id | sort_index
2 | 1 | 1
3 | 1 | 2
1 | 1 | 3
4 | 1 | 4
7 | 2 | 1
6 | 2 | 2
8 | 2 | 3
9 | 2 | 4
5 | 2 | 5
You should be able to do this in a single query: something along the lines of UPDATE foo SET sort_index = sort_index + 1 WHERE bar_id == b AND sort_index < s1 AND sort_index >= s2, where b is the bar_id of the row to be moved, s1 is the current sort_index of that row, and s2 is the the sort_index you want to move it to. Then, you'd just change the sort_index of the row.
You'd probably want to do the two queries inside a transaction. Also, it might speed things up if you created an index on the sort_index using something like CREATE INDEX foo_index ON foo (sort_index).
(By the way, here I'm assuming that you don't want duplicate sort_index values within a given bar_id, and that the relative order of rows should never be changed except explicitly. If you don't need this, the solution is even simpler.)