SQL ON DUPLICATE KEY with 2 unique keys? - mysql

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).

Related

Insert If duplicate not found in table else update

I wanted to ignore or update duplicate when I am going to insert values. I know about on duplicate key but i can't figure out the solution with that. Here is sample table example.
| ID | roll | sub | mark |
| ---- |----------| ------|------|
| 1 | 100 | 11 | 15 |
| 2 | 101 | 11 | 16 |
| 3 | 102 | 11 | 17 |
| 4 | 100 | 12 | 10 |
| 5 | 101 | 12 | 11 |
| 6 | 102 | 12 | 12 |
Here the id is primary key but I wanted to insert to check if roll & sub already exist then update otherwise insert new row. I've tried with the following code but that's insert duplicate row but it should update row 6 in following table.
CREATE INDEX mycompo_index on student(roll,sub);
insert into student(roll, mark, sub)
values (102, 22, 12)
on duplicate key update mark = values(mark);
If the combination of roll and sub should be unique, you should define such a key in your table:
ALTER TABLE student ADD CONSTRAINT student_uq UNIQUE(roll, sub)
Note that if you do this, you don't have to explicitly create the index you're creating, the constraint will create on for you. Once you have this is place, you can use the on duplicate key syntax you were trying to use:
INSERT INTO student(roll, mark, sub)
VALUES (102, 22, 12)
ON DUPLICATE KEY UPDATE mark = VALUES(mark)

How to prevent race condition with INSERT?

How would i go about preventing race conditions when INSERTing a row into a table that contains no unique index. For example say my table is....
key | slot | label
------------------
1 | 1 | some
1 | 2 | some
2 | 1 | some
2 | 2 | some
... is the only way to prevent such race conditions to create a composite unique field such as "key:slot" e.g.
id | key | slot | label
------------------------
1:1 | 1 | 1 | some
1:2 | 1 | 2 | some
2:1 | 2 | 1 | some
2:2 | 2 | 2 | some
...or is there a more efficient way that has escaped me? What about if i was to check for duplicate rows after the insert has been performed and roll the transaction back if there are any found?
actually you can do it without the key:slot column. You can define a unique compound key on the table. eg,
ALTER TABLE tableName ADD CONTRAINT tb_uq UNIQUE (`key`, slot)
How about using LOCK TABLES syntax to prevent race conditions when inserting?

Query to select newly added records only

As I am new to mysql, let me clear this doubt. how to write a query to find/select the latest added records only?
Example:
Consider a Table, which is daily added certain amount of records. Now the table contain 1000 records. And the total 1000 records are taken out for some performance. After sometimes table is added 100 records. Now I would like take the remain 100 only from the 1100 to do some operation. How to do it?
(For example only, I have given the numbers, But originally I don't know the last updated count and the newly added)
Here My table contain three columns Sno, time, data. where Sno is indexed as primary key.
Sample table:
| sno | time | data |
| 1 | 2012-02-27 12:44:07 | 100 |
| 2 | 2012-02-27 12:44:07 | 120 |
| 3 | 2012-02-27 12:44:07 | 140 |
| 4 | 2012-02-27 12:44:07 | 160 |
| 5 | 2012-02-27 12:44:07 | 180 |
| 6 | 2012-02-27 12:44:07 | 160 |
| 7 | 2012-02-28 13:00:35 | 100 |
| 8 | 2012-03-02 15:23:25 | 160 |
Add TIMESTAMP field with 'ON UPDATE CURRENT_TIMESTAMP' option, and you will be able to find last added or last edited records.
Automatic Initialization and Updating for TIMESTAMP.
Create table as below
Create table sample
(id int auto_increment primary key,
time timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
data nvarchar(100)
);
then query as
select * from sample order by time desc limit 1

MySQL - Increment Column Value or Insert Data if not Exists

I have users visiting the site. The user can do a bunch of different actions. I'd like a counter to count the amount of times the user does that action. The problem is, it's per day, it starts over each day.
So the model has, Id, User, Action, Times, Date
I'd like to use this, but I can't because Action is not a key, and cannot be. None of the other fields can be a key either.
insert into useractions (user, action, times) values (2, 3, 1)
on duplicate key update times = times + 1;
You left the data column out of your insert example, but you mentioned it several times so I'll assume that exists. Also, I'm assuming it's an actual date (not a timestamp or datetime).
If you add a unique index on (user,action,date) then your query will work.
Here's the DDL:
alter table useractions
add unique index unique_idx (user,action,date);
And your DML (adding the date column):
insert into useractions (user, action, times, date) values (2, 3, 1, current_date())
on duplicate key update times = times + 1;
Do you absolutely want to calculate the counter value at the time of inserting the action? It may be simpler to simply keep track of the users and actions with timestamps, like so:
+--------+----------+---------------------+
| UserID | ActionID | Time |
+--------+----------+---------------------+
| 1 | 1 | 2012-01-19 14:47:03 |
| 1 | 2 | 2012-01-19 14:48:12 |
| 1 | 3 | 2012-01-19 14:48:15 |
| 2 | 1 | 2012-01-19 14:49:33 |
| 2 | 1 | 2012-01-18 14:49:42 |
And then calculate the daily tallies with a query:
SELECT UserID,
ActionID,
DATE(Time) AS Date,
COUNT(*) AS n
FROM actions
GROUP BY UserID,ActionID,Date
ORDER BY Date,UserID,ActionID;
+--------+----------+------------+---+
| UserID | ActionID | Date | n |
+--------+----------+------------+---+
| 1 | 2 | 2012-01-17 | 2 |
| 1 | 3 | 2012-01-17 | 2 |
| 3 | 2 | 2012-01-17 | 6 |
| 1 | 1 | 2012-01-18 | 1 |
| 1 | 2 | 2012-01-18 | 1 |
| 1 | 3 | 2012-01-18 | 4 |
You can use a unique key on a combination of columns. That way you can make that combination (user, action, date) unique and your query should then work.
That really is the easiest solution. You do need rights to alter the table though.

ON DUPLICATE KEY: multi-column unique constraint

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.