Why in MYSQL by executing this SQL query 2 rows will add to table? Is this query executes two times!?;
INSERT INTO user(`usr_name`, `email`, `name`, `reg_date`, `role_id`)
(
SELECT "editor1",
"editor1#example.com",
"editor1",
"2005-12-20",
2
FROM `user`
WHERE (("admin", 3) IN (
SELECT usr_name, role_id
FROM `user`
)
AND NOT EXISTS (
SELECT usr_name, email
FROM `user`
WHERE usr_name = "editor1" OR email = "editor1#example.com"
))
)
result is here!
Apparently, two rows in user match the WHERE conditions.
You are not using the user table in the first FROM. So how about this instead:
INSERT INTO user(`usr_name`, `email`, `name`, `reg_date`, `role_id`)
SELECT t.*
FROM (SELECT 'editor1' as user_name, 'editor1#example.com as email,
'editor1' as name, '2005-12-20 as reg_date, 2 as role_id
) t
WHERE ('admin', 3) IN (SELECT usr_name, role_id
FROM `user`
) AND
NOT EXISTS (SELECT usr_name, email
FROM `user` u
WHERE u.usr_name = t.usr_name OR u.email = t.email
)
Or, better yet, but unique indexes on the fields that you don't want duplicated in the table:
create unique index idx_user_username on user(usr_name);
create unique index idx_user_email on usr(email);
Let the database protect the table. It is there to help you.
Related
I have a table but it has no unique ID or primary key.
It has 3 columns in total.
name
user_id
role_id
ben
1
2
ben
1
2
sam
1
3
I'd like to remove one entry with the name Ben.
So output would look like this
name
user_id
role_id
ben
1
2
sam
1
3
Most of the examples shows deleting duplicate entries with ID or primary key. However how would I retain one entry whilest removing the other ones?
Using the following query I was able to get duplicated rows
SELECT name, user_id, role_id, count(*) FROM some_table
GROUP BY name, user_id, role_id
HAVING count(*) > 1
To clarify, I am looking to delete these rows.
Prefer not creating a new table.
If you don't have to worry about other users accessing the table -
CREATE TABLE `new_table` AS
SELECT DISTINCT `name`, `user_id`, `role_id`
FROM `old_table`;
RENAME TABLE
`old_table` TO `backup`,
`new_table` TO `old_table`;
Or you could use your duplicates query to output lots of single row delete queries -
SELECT
`name`,
`user_id`,
`role_id`,
COUNT(*),
CONCAT('DELETE FROM some_table WHERE name=\'', `name`, '\' AND user_id=\'', `user_id`, '\' AND role_id=\'', `role_id`, '\' LIMIT 1;') AS `delete_stmt`
FROM `some_table`
GROUP BY `name`, `user_id`, `role_id`
HAVING COUNT(*) > 1;
Or you could temporarily add a SERIAL column and then remove it after the delete -
ALTER TABLE `some_table` ADD COLUMN `temp_id` SERIAL;
DELETE `t1`.*
FROM `some_table` `t1`
LEFT JOIN (
SELECT MIN(`temp_id`) `min_temp_id`
FROM `some_table`
GROUP BY `name`, `user_id`, `role_id`
) `t2` ON `t1`.`temp_id` = `t2`.`min_temp_id`
WHERE `t2`.`min_temp_id` IS NULL;
ALTER TABLE `some_table` DROP COLUMN `temp_id`;
Note that you are not saving anything by not having a primary key; mysql (at least with innodb) requires a primary key and will create a hidden one if you do not have one. So I would first add a primary key:
alter table some_table add id serial primary key;
Then you can easily remove duplicates with:
delete a from some_table a join some_table b on a.name=b.name and a.user_id=b.user_id and a.role_id=b.role_id and b.id < a.id;
I would take the duplicate records and put them into another table.
SELECT
name,
user_id,
role_id
INTO some_new_table
FROM some_table
GROUP BY name, user_id, role_id
HAVING count(*) > 1
Then you can delete those records from your source table
DELETE a
FROM some_table a
INNER JOIN some_new_table b
ON a.name = b.name
AND a.user_id = b.user_id
AND a.role_id = b.role_id
Finally you can then insert the deduped records back into your table.
INSERT INTO some_table
SELECT
name,
user_id,
role_id
FROM some_new_table
If the volume of dupes is very large you could also just create a new table with the deduped data. Truncate \ Drop the old table and then Insert \ Rename from the new table.
I have tables users (id, email), permissions (id, description) and users_permissions (user_id, permission_id, created) with many to many relation.
I need to select user with some email and assign to him all permissions from table permissions, which he does not have.
Now I am trying to assign at least all permissions, but I am getting error
Subquery returns more than 1 row
My query:
insert into `users_permissions` (`user_id`, `permission_id`, `created`)
select
(select `id` from `users` where `email` = 'user-abuser#gmail.com') as `user_id`,
(select `id` from `permissions`) as `permission_id`,
now() as `created`;
If a subquery (inside SELECT) returns more than one row, MySQL does not like it.
Another way to achieve your requirement is using CROSS JOIN between Derived Tables (subquery in the FROM clause):
INSERT INTO `users_permissions` (`user_id`, `permission_id`, `created`)
SELECT
u.id,
p.id,
NOW()
FROM
users AS u
CROSS JOIN permissions AS p
WHERE u.email = 'user-abuser#gmail.com'
I am looking to get the latest date of a select statement inside a select statement. I am using Hibernate, so there are limitations to normal MySQL such as not being able to have the select statement in the from area or inside MAX.
Here is a test structure:
CREATE TABLE User (
username varchar(20) NOT NULL PRIMARY KEY,
locationId int(10) NOT NULL
);
CREATE TABLE UserRecords (
id int(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
username varchar(20) NOT NULL,
recordDate datetime NOT NULL
);
INSERT INTO User VALUES ('test',1);
INSERT INTO User VALUES ('test2',2);
INSERT INTO User VALUES ('test3',1);
INSERT INTO UserRecords VALUES (null,'test','2018-02-10 14:29:40');
INSERT INTO UserRecords VALUES (null,'test2','2018-03-11 12:21:10');
INSERT INTO UserRecords VALUES (null,'test3','2018-05-18 11:11:15');
INSERT INTO UserRecords VALUES (null,'test','2018-06-20 16:58:50');
This is what I am after and works regularly, but doesn't work in Hibernate:
SELECT
u.locationId,
MAX(
SELECT
MAX(ur.recordDate)
FROM
UserRecords
WHERE
ur.username=u.username
)
FROM
User u
GROUP BY
u.locationId
The closest I can get is by just listing the max dates of each user and then have to parse them after.
SELECT
u.locationId,
GROUP_CONCAT(
CONCAT('''',
SELECT
MAX(ur.recordDate)
FROM
UserRecords
WHERE
ur.username=u.username
, '''')
)
FROM
User u
GROUP BY
u.locationId
This is really stripped down, but hopefully you get the idea.
Looks like you're trying to get the max record date per location id which can be achieved joining nest subQueries
location ID's max record date
SELECT
u.locationId,
Max(urRecordDate.maxRecordDate)
FROM User u
INNER JOIN
(SELECT
ur.username,
MAX(ur.recordDate) AS maxRecordDate
FROM UserRecords ur
GROUP BY ur.username) AS urRecordDate
ON u.username = urRecordDate.username
GROUP BY u.locationId
Users max record date and locationId
SELECT
u.locationId,
urRecordDate.maxRecordDate
FROM User u
INNER JOIN
(SELECT
ur.username,
MAX(ur.recordDate) AS maxRecordDate
FROM UserRecords ur
GROUP BY ur.username) AS urRecordDate
ON u.username = urRecordDate.username
using native SQL queries in hibernate
Another approach:
select u.locationId, ur.recordDate
FROM User u
JOIN UserRecords ur on (ur.username = u.username)
ORDER BY ur.recordDate desc
LIMIT 1;
im trying to create a sql query, that will detect (possible) duplicate customers in my database:
I have two tables:
Customer with the columns: cid, firstname, lastname, zip. Note that cid is the unique customer id and primary key for this table.
IgnoreForDuplicateCustomer with the columns: cid1, cid2. Both columns are foreign keys, which references to Customer(cid). This table is used to say, that the customer with cid1 is not the same as the customer with the cid2.
So for example, if i have
a Customer entry with cid = 1, firstname="foo", lastname="anonymous" and zip="11231"
and another Customer entry with cid=2, firstname="foo", lastname="anonymous" and zip="11231".
So my sql query should search for customers, that have the same firstname, lastname and zip and the detect that customer with cid = 1 is the same as customer with cid = 2.
However, it should be possible to say, that customer cid = 1 and cid=2 are not the same, by storing a new entry in the IgnoreForDuplicateCustomer table by setting cid1 = 1 and cid2 = 2.
So detecting the duplicate customers work well with this sql query script:
SELECT cid, firstname, lastname, zip, COUNT(*) AS NumOccurrences
FROM Customer
GROUP BY fistname, lastname,zip
HAVING ( COUNT(*) > 1 )
My problem is, that i am not able, to integrate the IgnoreForDuplicateCustomer table, to that
like in my previous example the customer with cid = 1 and cid=2 will not be marked / queried as the same, since there is an entry/rule in the IgnoreForDuplicateCustomer table.
So i tried to extend my previous query by adding a where clause:
SELECT cid, firstname, lastname, COUNT(*) AS NumOccurrences
FROM Customer
WHERE cid NOT IN (
SELECT cid1 FROM IgnoreForDuplicateCustomer WHERE cid2=cid
UNION
SELECT cid2 FROM IgnoreForDuplicateCustomer WHERE cid1=cid
)
GROUP BY firstname, lastname, zip
HAVING ( COUNT(*) > 1 )
Unfortunately this additional WHERE clause has absolutely no impact on my result.
Any suggestions?
Here you are:
Select a.*
From (
select c1.cid 'CID1', c2.cid 'CID2'
from Customer c1
join Customer c2 on c1.firstname=c2.firstname
and c1.lastname=c2.lastname and c1.zip=c2.zip
and c1.cid < c2.cid) a
Left Join (
Select cid1 'CID1', cid2 'CID2'
From ignoreforduplicatecustomer one
Union
Select cid2 'CID1', cid1 'CID2'
From ignoreforduplicatecustomer two) b on a.cid1 = b.cid1 and a.cid2 = b.cid2
where b.cid1 is null
This will get you the IDs of duplicate records from customer table, which are not in table ignoreforduplicatecustomer.
Tested with:
CREATE TABLE IF NOT EXISTS `customer` (
`CID` int(11) NOT NULL AUTO_INCREMENT,
`Firstname` varchar(50) NOT NULL,
`Lastname` varchar(50) NOT NULL,
`ZIP` varchar(10) NOT NULL,
PRIMARY KEY (`CID`))
ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=100 ;
INSERT INTO `customer` (`CID`, `Firstname`, `Lastname`, `ZIP`) VALUES
(1, 'John', 'Smith', '1234'),
(2, 'John', 'Smith', '1234'),
(3, 'John', 'Smith', '1234'),
(4, 'Jane', 'Doe', '1234');
And:
CREATE TABLE IF NOT EXISTS `ignoreforduplicatecustomer` (
`CID1` int(11) NOT NULL,
`CID2` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `ignoreforduplicatecustomer` (`CID1`, `CID2`) VALUES
(1, 2);
Results for my test setup are:
CID1 CID2
1 3
2 3
Edit as per TPete's comment (dind't try it):
SELECT
C1.cid, C1.firstname, C1.lastname
FROM
Customer C1,
Customer C2
WHERE
C1.cid < C2.cid AND
C1.firstname = C2.firstname AND
C1.lastname = C2.lastname AND
C1.zip = C2.zip AND
CAST(C1.cid AS VARCHAR)+' ' +CAST(C2.cid AS VARCHAR) <>
(SELECT CAST(cid1 AS VARCHAR)+' '+CAST(cid2 AS VARCHAR) FROM IgnoreForDuplicateCustomer I WHERE I.cid1 = C1.cid AND I.cid2 = C2.cid);
Initially I thought that IgnoreForDuplicateCustomer was a field in the customer table.
crazy but I think it works :)
first I join the customer tables with itself on the names to get the duplicates
then I exclud the keys on the IgnoreForDuplicateCustomer table (the union is because the first query returns cid1, cid2 and cid2,cid1
the result will be duplicated but I think you can get the info you need
select c1.cid, c2.cid
from Customer c1
join Customer c2 on c1.firstname=c2.firstname
and c1.lastname=c2.lastname and c1.zip=c2.zip
and c1.cid!=c2.cid
except
(
select cid1,cid2 from IgnoreForDuplicateCustomer
UNION
select cid2,cid1 from IgnoreForDuplicateCustomer
)
second shot:
select firstname,lastname,zip from Customer
group by firstname,lastname,zip
having (count(*)>1)
except
select c1.firstname, c1.lastname, c1.zip
from Customer c1 join IgnoreForDuplicateCustomer IG on c1.cid=ig.cid1 join Customer c2 on ig.cid2=c2.cid
third:
select firstname,lastname,zip from (
select firstname,lastname,zip from Customer
group by firstname,lastname,zip
having (count(*)>1)
) X
where firstname not in (
select c1.firstname
from Customer c1 join IgnoreForDuplicateCustomer IG on c1.cid=ig.cid1 join Customer c2 on ig.cid2=c2.cid
)
I'm trying to get an id from a companies table where the id is not yet in the crawlLog table. Then I need to insert that companyId into the crawlLog table.
I need to do this in one call so that parallel crawlers don't pull the same url after some other crawler has selected a url, but hasn't inserted it into the crawl log yet. I don't want to lock tables because of other problems that generates.
I get this error from both queries below:
You can't specify target table 'crawlLog' for update in FROM clause
Here are two queries i've tried to do the same thing.
INSERT INTO crawlLog (companyId, timeStartCrawling)
VALUES
(
(
SELECT companies.id FROM companies
LEFT OUTER JOIN crawlLog
ON companies.id = crawlLog.companyId
WHERE crawlLog.companyId IS NULL
LIMIT 1
),
now()
)
I've also tried this, but get the same error:
INSERT INTO crawlLog (companyId, timeStartCrawling)
VALUES
(
(
SELECT id
FROM companies
WHERE id NOT IN
(
SELECT companyId
FROM crawlLog
)
LIMIT 1
),
now()
)
Why use a Subselect? INSERT INTO ... SELECT exists:
INSERT INTO crawlLog (companyId, timeStartCrawling)
SELECT companies.id, NOW()
FROM companies
LEFT OUTER JOIN crawlLog
ON companies.id = crawlLog.companyId
WHERE crawlLog.companyId IS NULL
LIMIT 1
And that way it should not complain about using a table both in the INSERT and SELECT part
You can't update rows which you are querying. There is a way to force MySQL to use a temporary table implicitly:
INSERT INTO crawlLog (companyId, timeStartCrawling)
VALUES
(
SELECT id, when FROM
(
SELECT companies.id AS id, now() AS when FROM companies
LEFT OUTER JOIN crawlLog
ON companies.id = crawlLog.companyId
WHERE crawlLog.companyId IS NULL
LIMIT 1
)
)
This works and seems like the simplest solution:
Using the simpler of the two statements in my question, I created an alias for the inner crawlLog table as suggested by #Tocco in the comments, and then removed the necessary encapsulation in VALUES().
INSERT INTO crawlLog (companyId, timeStartCrawling)
SELECT id, now()
FROM companies
WHERE id NOT IN
(
SELECT companyId
FROM crawlLog AS crawlLogAlias
)
LIMIT 1
Do the select into a temp table, then insert selecting from the temp table. You can't insert into a table and select from it in the same statement, so use a temp table and two statements.