I have one table in mysql named 'UserFriends' where I am updating my websites user's friends details.
here is the schema of the table (UserFriends)
id int,
Userid int,
friendid int,
createdate timespan
now, I want to create unique index on userid & friendid. that i have created unique index well. so, right now i am not able to insert same userid and friendid as duplicate. but if i am inserting same value in opposite field it accept without generating error.
example :
insert into userfriends ( userid, friendid )
select 1, 2 --- insert perfect
insert into userfriends ( userid, friendid )
select 1, 2 --- show error because unique index comes in a picture
now i am inserting
insert into userfriends ( userid, friendid )
select 2, 1 --- records insert here (i don't want this)
How do i prevent this?
First things first: for your inserts, don't use 'select' to generate the values, as it's liable to become confusing. Instead do it this way:
INSERT INTO userfriends (userid, friendid) VALUES (1, 2)
That said, the only way to have a unique qualifier that works backwards too is to do it programmatically. If you want to prevent x,y from being accepted, run SELECT * FROM userfriends WHERE userid = y AND friendid = x and if you get a result, reject the insert before it happens. If you don't, then insert into the table as normal, and your unique key will deny it from there on.
Related
I would like to write a SQL statement that inserts a new row into the database if there isn't already a row for it. The unique identifier of a row is the id and url. Let's say the table schema looks like this:
LinkClicks: (id, url, clicks)
So now let's say I've got a row with a parameterized SQL insert. I'm attempting to do something like this:
INSERT (id, url, clicks)
INTO LinkClicks Values(#id, #url, #clicks)
WHERE #url NOT IN
(SELECT url FROM LinkClicks WHERE id=#id);
I think you want something like this:
INSERT INTO LinkClicks(id, url, clicks)
SELECT id, url, clicks
FROM (SELECT #id as id, #url as url, #clicks as clicks) t
WHERE t.url NOT IN (SELECT url FROM LinkClicks WHERE id = #id);
You can add a unique index on the id and url columns:
ALTER TABLE LinkClicks ADD UNIQUE u_idx (id, url);
With this constraint in place, attempts to insert a record whose id and url combination of values already appears will fail at the database level.
This might be preferable to the query you are attempting, because it guarantees that MySQL will reject a duplicate attempt to insert. A query could also be used to this effect, but later on perhaps someone else using your code base might forget this.
In fact you should take Tim's advice and put the unique index on the table but in doing so you need a fail safe way of ensuring that you don't attempt to put duplicates (id and url) into the table (otherwise loads of red-ink messages). This way seems ok:
DROP TABLE LINKCLICKS
DROP TABLE LINKCLICKS1
CREATE TABLE LINKCLICKS
(
[ID] INT,
[URL] CHAR(11),
CLICKS BIGINT
)
GO
INSERT INTO LINKCLICKS VALUES (1001,'www.abc.com',40000)
INSERT INTO LINKCLICKS VALUES (1002,'www.def.com',40000)
INSERT INTO LINKCLICKS VALUES (1003,'www.ghi.com',40000)
GO
CREATE TABLE LINKCLICKS1
(
[ID] INT,
[URL] CHAR(11),
CLICKS BIGINT
)
GO
INSERT INTO LINKCLICKS1 VALUES (1001,'www.abc.com',40000)
INSERT INTO LINKCLICKS1 VALUES (1003,'www.def.com',40000)
INSERT INTO LINKCLICKS1 VALUES (1004,'www.ghi.com',40000)
GO
WITH CTE1 AS
(
SELECT *,'d' AS [Source] FROM LINKCLICKS
UNION ALL
SELECT *,'s' AS [Source] FROM LINKCLICKS1
)
,
CTE2 AS
(
SELECT ID,[URL] FROM CTE1 GROUP BY ID,[URL] HAVING COUNT(ID) =1 AND COUNT([URL]) =1
)
INSERT INTO LINKCLICKS
SELECT ID,[URL],CLICKS
FROM CTE1
WHERE [Source] <> 'd'
AND
(
ID IN (SELECT ID FROM CTE2) AND [URL] IN (SELECT [URL] FROM CTE2)
)
SELECT * FROM LINKCLICKS ORDER BY [ID],URL
GO
The INSERT statement only inserts those rows where the ID and URL combined are not the same as rows already in the destination table. It quite happily inserts rows where either the IDs are the same but the URLs are different or where the IDs are different but the URLs are the same.
My only reservation is the question of 'dupes' in the source table (in this case LINKCLICKS1). If there are duplicates in the source table, none of them will be inserted into the destination table. This will defeat the object of the query.
The answer is, if you have duplicates, or any risk of duplication in the source table, then you should apply 'de-dupe code' to the source table before you run this.
If you need any de-dupe code, put a comment below.
My database structure is:
items (id, item_name)
user (id, username)
inventory(id, user_id FK, item_id FK, amount)
I have a select query which returns rows with (item_id, amount)
I just want to add values from that select query into that inventory table.
So what i need in pseudo code is:
FUNCTION give_items(IN in_user_id)
FOR EACH ROW FROM THAT SELECT:
IF ROW EXISTS with user_id = in_user_id AND item_id = SELECT.item_id THEN INSERT ROW
ELSE UPDATE ROW amount = amount + SELECT.amount
it is like INSERT INTO SELECT ... ON DUPLICATE KEY, but i need to respect user_id and item_id not PRIMARY KEY
#EDIT
This is what i got now:
INSERT INTO `items_inventory` (`user_id`,`item_id`,`amount`)
SELECT mu.user_id, mi.item_id, SUM(mu.amount*mi.income) as amount_sum
FROM `mining_machines_users` AS mu
LEFT JOIN `mining_machines_income` AS mi ON mu.machine_id=mi.machine_id
WHERE mu.user_id=in_user_id
GROUP BY mi.item_id
ON mu.user_id=in_user_id AND mi.item_id=VALUES(`item_id`) //NEED FIX
`amount`=`amount`+VALUES(`amount`)
Sorry for my bad English, i hope that you will help me :)
Is the id property necessary? otherwise its maybe easier if you make a combined primary key of user_id and item_id
I have an issue where I'm filtering a table by a bunch of different values. There's about 30 different filters on this table and since I'm still a novice with MySQL I have it done in a stored procedure executing multiple DELETE queries from a temporary table to filter. This example is only going to show the filter that I'm having issues from, which is a DELETE FROM table WHERE value IN () query.
Here's a test Schisma:
CREATE TABLE accounts (
user_id INT(11) NOT NULL AUTO_INCREMENT,
name VARCHAR(40) NOT NULL,
PRIMARY KEY(user_id)
);
CREATE TABLE blocked (
user_id INT(11) NOT NULL,
other_id INT(11) NOT NULL,
);
INSERT INTO accounts (name) VALUES ('Chris'), ('Andy');
INSERT INTO blocked (user_id, other_id) VALUES (1, 2);
The queries create two tables: the accounts table containing two rows, and the blocked table containing one row where user_id 1 has user_id 2 blocked.
Here's the query that's causing us some problem (Please note that the queries are actually more complex than displayed, but the DELETE query is 100% the same, and the issue persists through the test example provided):
BEGIN
#user_in input is a int(11) value bassed in the CALL FUNCTION(ID).
CREATE TEMPORARY TABLE IF NOT EXISTS filtered AS (SELECT * FROM accounts);
DELETE FROM filtered WHERE user_id IN (SELECT other_id FROM blocked WHERE blocked.user_id = user_in);
SELECT * FROM filtered;
END
This query should delete the row with the user_id field of 2, as in the blocked table the only row is (1, 2).
Running the SELECT query directly providing the user_id returns the other_id of 2.
SELECT other_id FROM blocked WHERE blocked.other_id = 2;
However, the stored procedure returns both rows, instead of just one. Why?
NOTE: The above query is to show what is returned when the query SELECT other_id FROM blocked WHERE blocked.user_id = user_in, another example would be SELECT other_id FROM blocked WHERE blocked.user_id = 1 granted user_in is set to 1. Both of these queries will return a set of (2) which would make the delete query look like DELETE FROM filtered WHERE user_id IN (2). This is not working, for whatever reason.
To get a simple select of that users use next query
SELECT * FROM accounts WHERE accounts.user_id NOT IN (SELECT distinct blocked.other_id from blocked)
To do it with one single select without deleting rows from temporary table use next query:
BEGIN
CREATE TEMPORARY TABLE IF NOT EXISTS filtered AS (SELECT * FROM accounts WHERE accounts.user_id NOT IN (SELECT distinct blocked.other_id from blocked));
SELECT * from filtered;
END
No need for select all in temporary table first and then delete specific rows.
Hope it helps
EDIT:
I'v read the question and still a bit confused about you problem. But i checked this solution and it works perfectly so i don't understand what is problem with this. In your procedure you have
DELETE FROM filtered WHERE user_id IN (SELECT other_id FROM blocked WHERE blocked.user_id = user_in);
and after that you say that
SELECT other_id FROM blocked WHERE blocked.other_id = 2;
And i can say that blocked.other_id and blocked.user_id are two different columns.
No disrespect but amateur mistake to mix up columns. :)
The problem here is with this statement:
DELETE FROM filtered WHERE user_id IN (SELECT other_id FROM blocked WHERE blocked.other_id = user_id);
Try changing it to this:
DELETE FROM filtered WHERE user_id
IN (SELECT other_id FROM blocked);
Reason being that the blocked table has both a other_id and a user_id column. So where you are attempting to join out to the filtered table you are in fact comparing the other_id and user_id columns in the blocked table only. Which are not equal. So no delete happens.
I have a table that looks like this:
Number | Name
--------+--------
123 | Robert
This is what I want to do:
If the Number is already in the database, don't insert a new record.
If the Number is not in the databse, but the name is, create a new name and insert it. So for example, if I have a record that contains 123 for Number and Bob for Name, I don't want to insert it, but if I get a record that contains 456 for Number and Robert for name, I would insert 456 and Robert1. I was going to check for duplicates individually like:
SELECT * FROM Person where Number = 123;
//If number is not found
SELECT * FROM Person where Name = 'Robert';
//If name is found, add a number to it.
Is there a way I can combine the two statements?
There are actually two problems in your question. The first problem is to make Number column unique and the second one is to increment the column Name by appending a number if it already exists.
FIRST PART
Since the number is UNIQUE, enforce a UNIQUE constraint on the column. It could be a PRIMARY KEY or a UNIQUE KEY.
If the column has no KEY and you want to make it PRIMARY, here is the ALTER statement:
ALTER TABLE TableName ADD CONSTRAINT tb_pk PRIMARY KEY (Number)
SQLFiddle Demo
but if you only want it to be UNIQUE and not a primary key,
ALTER TABLE TableName ADD CONSTRAINT tb_uq UNIQUE (Number)
SQLFiddle Demo
SECOND PART
You can actually do it without using join.
INSERT INTO TableName(Number, Name)
SELECT 124 AS Number,
CONCAT('Robert', COALESCE(MAX(CAST(REPLACE(Name, 'Robert', '0') AS UNSIGNED)) + 1,'')) AS Name
FROM TableName
WHERE Name LIKE 'Robert%'
SQLFiddle Demo
SQLFiddle Demo (added more example)
SQLFiddle Demo (throws exception due to uniqueness)
Some details:
when the value supplied on column Number already exists, it will throw an error since the column is unique. I have read a comment from a deleted posts saying: "..Number is not unique, but if it does exist, I don't want to enter a record." -- it does not make any sense if you don't want to add uniqueness on the column. How will you know if the number already exists or not? Doing a little check for the existence of Number feels like a little overhead for me. So my best recommendation is to enforce uniqueness.
SELECT * FROM Person WHERE Number = 123 OR Name = 'Robert'
I haven't worked with SQL for some time, so this may be wrong ;)
Edit:
$number = 123;
$name = 'Robert';
$query = mysql_query("SELECT * FROM Person WHERE Number = $number OR Name = '$name' ");
if (mysql_num_rows($query) == 0 ) {
//-> Add your record, it's unused
} else if (mysql_result($query, 0, 'number') == $number && mysql_result($query, 0, 'name' == $name)) {
//combination of number and name already exists -> modify name and add record
} else {
echo "Number is used by another name";
}
Use this query, for insert the row [123, 'Robert']. if you want insert other values, change 123 & Robert values in below query:
insert into Person (Number,Name)
select 123, IF(mn.MaxNumber is NULL,'Robert',concat('Robert',mn.MaxNumber+1))
from (SELECT 'foo') foo
left JOIN (select max(CONVERT(SUBSTR(Name,LENGTH('Robert')+1),UNSIGNED)) `MaxNumber`
from person where name rlike '^Robert[0-9]*$') mn on 1=1
where Not Exists (select * from Person where Number=123)
NOTE: if Robert exists in the table, above query inserts Robert1. if Robert1 exists, it inserts Robert2, and so on .
make both number and name unique.
ALTER TABLE `person` ADD UNIQUE (`number` ,`name`);
You can now do a insert with ON DUPLICATE
INSERT INTO `person` (`number`, `name`, `id`) VALUES ('322', 'robert', 'NULL') ON DUPLICATE KEY UPDATE `id`='NULL';
For appending a number after name i would suggest using autoincrement column instead.
insert into Person (Number,Name)
select 123, IF(mn.MaxNumber is NULL,'Robert',concat('Robert',mn.MaxNumber+1))
from (SELECT 'foo') foo
left JOIN (select max(CONVERT(SUBSTR(Name,LENGTH('Robert')+1),UNSIGNED)) `MaxNumber`
from person where name rlike '^Robert[0-9]*$') mn on true
where Not Exists (select * from Person where Number=123)
How can I use a single query for inserting table when a column value is not found.
eg/ i want to insert new user only when this username not found
what i doing now is issue 1 query to check for existing,
then another query if no existing found. total 2 query
INSERT INTO friends (memberID) SELECT 1 WHERE NOT EXISTS (SELECT memberID FROM friends WHERE memberID = 1)
You just need to add FROM DUAL
INSERT INTO friends
(memberid)
SELECT 1
FROM dual
WHERE NOT EXISTS (SELECT memberid
FROM friends
WHERE memberid = 1)
sql fiddle
How about this:
INSERT INTO YourTable (UserName)
SELECT x
FROM (SELECT 'New User Name' AS x) a
WHERE x NOT IN(SELECT UserName
FROM YourTable)
Since you only want one row with a given value you should enforce that with a UNIQUE constraint on the table, for example:
ALTER TABLE friends ADD UNIQUE (memberID);
After you do that, you can simply add the IGNORE keyword to your insert statements and it won't report an error and it won't insert a duplicate row if it already exists.
INSERT IGNORE INTO friends(memberID) VALUES(1);