ON DUPLICATE KEY UPDATE-like behaviour on multiple columns - php

I have a table like this:
id | userid | commentid | value
-------------------------------
Each user is permitted to vote a comment once. The value can be between -1 and 1. Is there a easy way for a table model to achieve an change of this vote in one single query, without checking EXISTS() first? I already thought about a hash-column like this
MD5(CONCAT(userid, commentid))
but is there any better solution for this?

Just use an unique key on userid + commentid and you can use ON DUPLICATE KEY UPDATE.
INSERT INTO `yourTable` (
`userid`,
`commentid`,
`value`
) VALUES (
x,
y,
z
) ON DUPLICATE KEY UPDATE
`value` = z

Related

SQL Statement INSERT, on duplicate columns UPDATE

I have an SQL Table that contains a list of items that users can have linked to their profile. The SQL table looks something like this:
Item_Activity_ID Item_ID User_ID Status Date-Added
1 1 1 1 2015-06-08
2 2 2 1 2015-06-08
3 1 1 0 2015-06-09
The entry shows that someone with the user with id of 1 added item id 1 twice, and the only thing that was changed was the date and status. I want to make it so that when given an INSERT statement such as:
INSERT INTO items (Item_ID, User_ID, Status, Date_Added) VALUES ('$x', '$y', 1, CURDATE()) IF EXISTS SOME Item_ID = $x AND User_ID = $y UPDATE items SET Status = 1, Date_Added = CURDATE() WHERE Item_ID = $x AND User_ID = $y
Item_Activity_ID is an auto_incremented primary key index. How can I accomplish this in one query? Two users can have the same item, but where should never be repeat entries of the same user id and item id.
First, create a unique index for Item_ID, UserID combination,
Then, use the INSERT ... ON DUPLICATE KEY UPDATE statement:
INSERT INTO items (Item_ID, User_ID, Status, Date_Added)
VALUES ('$x', '$y', 1, CURDATE())
ON DUPLICATE KEY UPDATE Status = VALUES(Status), Date_Added = VALUES(Date_Added))
P.S. make sure to sanitize $x and $y to prevent SQL injections!
I would start by adding a unique key index:
ALTER TABLE items
ADD CONSTRAINT uc_UserItem UNIQUE (Item_ID,User_ID);
Then, you can just modify your insert query:
INSERT INTO items (Item_ID, User_ID, Status, Date_Added) VALUES ('$x', '$y', 1, CURDATE()) ON DUPLICATE KEY UPDATE Status=VALUES(1), Date_Added=VALUES(CURDATE());
Try to perform the update first supposing that the user and item already exist. Then check if this update affects any rows (using ##rowcount, in SQL Server).
If not then perform an insert.
Don't forget to put the above two statements in a transaction... ;)
Normal way would be to set a composite constraint at the db level. If you are using mysql and phpmyadmin you do this in the table structure view.
Check both fields (I guess 'user_id' and 'item_id') and click the 'unique' button.
After that is set you can just append
ON DUPLICATE KEY UPDATE status =1, date_added=CURDATE().
It will update the row that violated the constraint you created

Mysql query "WHERE 2 IN (`column`)" not working

I want to execute a query where I can find one ID in a list of ID.
table user
id_user | name | id_site
-------------------------
1 | james | 1, 2, 3
1 | brad | 1, 3
1 | suko | 4, 5
and my query (doesn't work)
SELECT * FROM `user` WHERE 3 IN (`id_site`)
This query work (but doesn't do the job)
SELECT * FROM `user` WHERE 3 IN (1, 2, 3, 4, 6)
That's not how IN works. I can't be bothered to explain why, just read the docs
Try this:
SELECT * FROM `user` WHERE FIND_IN_SET(3,`id_site`)
Note that this requires your data to be 1,2,3, 1,3 and 4,5 (ie no spaces). If this is not an option, try:
SELECT * FROM `user` WHERE FIND_IN_SET(3,REPLACE(`id_site`,' ',''))
Alternatively, consider restructuring your database. Namely:
CREATE TABLE `user_site_links` (
`id_user` INT UNSIGNED NOT NULL,
`id_site` INT UNSIGNED NOT NULL,
PRIMARY KEY (`user_id`,`site_id`)
);
INSERT INTO `user_site_links` VALUES
(1,1), (1,2), (1,3),
(2,1), (2,3),
(3,4), (3,5);
SELECT * FROM `user` JOIN `user_site_links` USING (`id_user`) WHERE `id_site` = 3;
Try this: FIND_IN_SET(str,strlist)
NO! For relation databases
Your table doesn't comfort first normal form ("each attribute contains only atomic values, and the value of each attribute contains only a single value from that domain") of a database and you:
use string field to contain numbers
store multiple values in one field
To work with field like this you would have to use FIND_IN_SET() or store data like ,1,2,3, (note colons or semicolons or other separator in the beginning and in the end) and use LIKE "%,7,%" to work in every case. This way it's not possible to use indexes[1][2].
Use relation table to do this:
CREATE TABLE user_on_sites(
user_id INT,
site_id INT,
PRIMARY KEY (user_id, site_id),
INDEX (user_id),
INDEX (site_id)
);
And join tables:
SELECT u.id, u.name, uos.site_id
FROM user_on_sites AS uos
INNER JOIN user AS u ON uos.user_id = user.id
WHERE uos.site_id = 3;
This way you can search efficiently using indexes.
The problem is that you are searching within several lists.
You need something more like:
SELECT * FROM `user` WHERE id_site LIKE '%3%';
However, that will also select 33, 333 and 345 so you want some more advanced text parsing.
The WHERE IN clause is useful to replace many OR conditions.
For exemple
SELECT * FROM `user` WHERE id IN (1,2,3,4)
is cleaner than
SELECT * FROM `user` WHERE id=1 OR id=2 OR id=3 OR id=4
You're just trying to use it in a wrong way.
Correct way :
WHERE `field` IN (list_item1, list_item2 [, list_itemX])

avoid duplicates in mysql relations table

I have a table that looks like this:
CREATE TABLE `relations` (
`idA` VARCHAR(20),
`idB` VARCHAR(20).
PRIMARY KEY (idA,idB)
)
TYPE=MyISAM;
and it basically just maps two ids together from another table that looks like this:
CREATE TABLE `scores` (
`id` VARCHAR(20) PRIMARY KEY,
`score` INT(10) UNSIGNED NOT NULL DEFAULT ‘0’,
`friendsids` VARCHAR(1000)
)
TYPE=MyISAM;
So - if I want to add something to the relations table, I query
INSERT IGNORE INTO relations VALUES ('$idA', '$idB')
So the problem - it sometimes creates entries that have the same info but swapped between idA and idB. For example, if one entry is idA = 1, idB = 2 - I dont' want an entry that looks like idA = 2, idB = 1;
I tried:
INSERT IGNORE INTO relations VALUES ('$idA', '$idB') WHERE NOT EXISTS (SELECT * WHERE idA IS '$idB' AND idB IS '$idA');
It gives me a syntax error which I somehow can't figure out:
Query failed: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'IGNORE INTO relations VALUES ('saubua', 'deppata') WHERE NOT EXISTS (SELECT * WH' at line 1
Is there a chance I'm totally on the wrong way with this? Is there a simpler way?
can you try
INSERT INTO relations
SELECT '$idA','$idB'
FROM dual
WHERE NOT EXISTS (SELECT idA
FROM relations
WHERE (idA='$idA' OR idB='$idB')
OR (idA='$idB' OR idB='$idA')
)
so - I took Akhils code and mofified it, so not it works:
INSERT IGNORE INTO relations SELECT '$idA','$idB' FROM dual WHERE NOT EXISTS (SELECT idA FROM relations WHERE (idA='$idB' AND idB='$idA') )
thanks everyone!! :)

Update MySQL table if non-primary key column is duplicate, insert otherwise?

I have a MySQL table with an autoincremented primary key, and a column for a person's name, and some data.
Is there a MySQL query where I can insert a new row if the name of the person is unique, but if it's a duplicate name, then I would update that row?
I.e.
ID, Name, Data
1 , Michael, x
2 , Stephen, y
3 , Christopher, z
If I were to add a "Michael" to the database with "qq" data, I would want the database to look like this:
ID, Name, Data
1 , Michael, qq
2 , Stephen, y
3 , Christopher, z
If I were to add "John" with "zz" data, the database would look like this:
ID, Name, Data
1 , Michael, x
2 , Stephen, y
3 , Christopher, z
4 , John, zz
I know of the command, ON DUPLICATE KEY UPDATE, but I only want to update if the name is the same, not if the primary key is the same.
Create a UNIQUE index on Name. ON DUPLICATE KEY UPDATE will then work correctly.
Create a stored procedure and simply call it in your query code
PROCEDURE:
DROP PROCEDURE IF EXISTS InsUpdTable $$
CREATE PROCEDURE InsUpdTable
(
IN _name VARCHAR(45),
IN _data VARCHAR(45)
)
BEGIN
if exists (select ID from mytable where name = _name)
Then
update mytable set data = _data where name=_name;
else
insert into mytable(name, data) values(_name, _data);
END IF;
END $$
DELIMITER ;
Query:
call InsUpdTable('John','zzz')

SELECT unique row from field 3 to field 4

Is there a way to delete duplicate records based on two fields?
I have a system where people can register for sport events. In the table:
event_registrations
• unique_id
• eventname
• id (person's id number)
• Name and Surname
One person can apply for many events - id may duplicate
an event may have multiple participants - eventname may duplicate:
--Johnsmith-- --Mountain Cycle--
--Johnsmith-- --Marathnon Walk--
--Linda-- --Mountain Cycle--
--Johnsmith-- --Mountain Cycle--
But a person may not register for a event they have already registered for:
--Johnsmith-- --Mountain Cycle--
--Johnsmith-- --Mountain Cycle--
They Select a event name through a form. Then the form data and their user details is stored in table event_registrations.
Any help would be appreciated
First delete any rows with duplicate (eventname, id) combinations.
Then add the UNIQUE constraint:
ALTER TABLE yourTable
ADD CONSTRAINT eventname_person_Unique
UNIQUE INDEX eventname_id_U
(eventname, id) ;
Your form that adds registrations should be adjusted accordingly to treat the error it will get from MySQL when a duplicate row is rejected.
A UNIQUE INDEX is the way to prevent this, as ypercube suggests. To identify/delete existing duplicates you could use this:
SELECT
eventname,
id -- You should consider using a less ambiguous name here
FROM
Event_Registrations ER1
WHERE
EXISTS (
SELECT *
FROM Event_Registrations ER2
WHERE
ER2.eventname = ER1.eventname AND
ER2.id = ER1.id AND
(ER2.registration_datetime < ER1.registration_datetime OR
(ER2.registration_datetime = ER1.registration_datetime AND
ER2.unique_id < ER1.unique_id
)
)
)
If you need to do some data tidy up before adding the unique constraint then you could use the following (one good reason why always having a unique id column is a good idea):
create table id_for_deletion (id int unsigned not null);
insert into id_for_deletion (id)
(
select a.delete_me_id
from (
select eventname,id,max(unique_id) as delete_me_id
from event_registrations
group by eventname,id
having count(*) > 1
) a);
delete from event_registrations where unique_id in (select id from id_for_deletion);
drop table id_for_deletion;

Categories