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'
Related
I am trying to limit returned results of users to results that are "recent" but where users have a parent, I also need to return the parent.
CREATE TABLE `users` (
`id` int(0) NOT NULL,
`parent_id` int(0) NULL,
`name` varchar(255) NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `times` (
`id` int(11) NOT NULL,
`time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
);
INSERT INTO `users`(`id`, `parent_id`, `name`) VALUES (1, NULL, 'Alan');
INSERT INTO `users`(`id`, `parent_id`, `name`) VALUES (2, 1, 'John');
INSERT INTO `users`(`id`, `parent_id`, `name`) VALUES (3, NULL, 'Jerry');
INSERT INTO `users`(`id`, `parent_id`, `name`) VALUES (4, NULL, 'Bill');
INSERT INTO `users`(`id`, `parent_id`, `name`) VALUES (5, 1, 'Carl');
INSERT INTO `times`(`id`, `time`) VALUES (2, '2019-01-01 14:40:38');
INSERT INTO `times`(`id`, `time`) VALUES (4, '2019-01-01 14:40:38');
http://sqlfiddle.com/#!9/91db19
In this case I would want to return Alan, John and Bill, but not Jerry because Jerry doesn't have a record in the times table, nor is he a parent of someone with a record. I am on the fence about what to do with Carl, I don't mind getting the results for him, but I don't need them.
I am filtering tens of thousands of users with hundreds of thousands of times records, so performance is important. In general I have about 3000 unique id's coming from times that could be either an id, or a parent_id.
The above is a stripped down example of what I am trying to do, the full one includes more joins and case statements, but in general the above example should be what we work with, but here is a sample of the query I am using (full query is nearly 100 lines):
SELECT id AS reference_id,
CASE WHEN (id != parent_id)
THEN
parent_id
ELSE null END AS parent_id,
parent_id AS family_id,
Rtrim(last_name) AS last_name,
Rtrim(first_name) AS first_name,
Rtrim(email) AS email,
missedappt AS appointment_missed,
appttotal AS appointment_total,
To_char(birth_date, 'YYYY-MM-DD 00:00:00') AS birthday,
To_char(first_visit_date, 'YYYY-MM-DD 00:00:00') AS first_visit,
billing_0_30
FROM users AS p
RIGHT JOIN(
SELECT p.id,
s.parentid,
Count(p.id) AS appttotal,
missedappt,
billing0to30 AS billing_0_30
FROM times AS p
JOIN (SELECT missedappt, parent_id, id
FROM users) AS s
ON p.id = s.id
LEFT JOIN (SELECT parent_id, billing0to30
FROM aging) AS aging
ON aging.parent_id = p.id
WHERE p.apptdate > To_char(Timestampadd(sql_tsi_year, -1, Now()), 'YYYY-MM-DD')
GROUP BY p.id,
s.parent_id,
missedappt,
billing0to30
) AS recent ON recent.patid = p.patient_id
This example is for a Faircom C-Tree database, but I also need to implement a similar solution in Sybase, MySql, and Pervasive, so just trying to understand what I should do for best performance.
Essentially what I need to do is somehow get the RIGHT JOIN to also include the users parent.
NOTES:
based on your fiddle config I'm assuming you're using MySQL 5.6 and thus don't have support for Common Table Expressions (CTE)
I'm assuming each name (child or parent) is to be presented as separate records in the final result set
We want to limit the number of times we have to join the times and users tables (a CTE would make this a bit easier to code/read).
The main query (times -> users(u1) -> users(u2)) will give us child and parent names in separate columns so we'll use a 2-row dynamic table plus a case statement to to pivot the columns into their own rows (NOTE: I don't work with MySQL and didn't have time to research if there's a pivot capability in MySQL 5.6)
-- we'll let 'distinct' filter out any duplicates (eg, 2 'children' have same 'parent')
select distinct
final.name
from
-- cartesian product of 'allnames' and 'pass' will give us
-- duplicate lines of id/parent_id/child_name/parent_name so
-- we'll use a 'case' statement to determine which name to display
(select case when pass.pass_no = 1
then allnames.child_name
else allnames.parent_name
end as name
from
-- times join users left join users; gives us pairs of
-- child_name/parent_name or child_name/NULL
(select u1.id,u1.parent_id,u1.name as child_name,u2.name as parent_name
from times t
join users u1
on u1.id = t.id
left
join users u2
on u2.id = u1.parent_id) allnames
join
-- poor man's pivot code:
-- 2-row dynamic table; no join clause w/ allnames will give us a
-- cartesian product; the 'case' statement will determine which
-- name (child vs parent) to display
(select 1 as pass_no
union
select 2) pass
) final
-- eliminate 'NULL' as a name in our final result set
where final.name is not NULL
order by 1
Result set:
name
==============
Alan
Bill
John
MySQL fiddle
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.
I have a table of users:
UserID | User
1 Martin
2 Lilian
3 Oliver
Now I have another table:
dataID | UserID | key | value
Now what I need to do is:
Select certain users from the user-table and insert several recrods in the data-table:
I need to combine these two querys:
INSERT INTO `data` (`UserID`, `key`, `value`)
VALUES (HERE_ID_OF_USER, 'someKey', 10),
VALUES (HERE_ID_OF_USER, 'otherKey', 20)
SELECT `UserID` FROM `users` WHERE ...
Not sure I fully understand what you want to do, but I'll assume you want this:
INSERT INTO data (UserID,key,value) SELECT UserID,'somekey',10 FROM users WHERE ...
INSERT INTO data (UserID,key,value) SELECT UserID,'otherkey',20 FROM users WHERE ...
If that's not what you want, you'll need to be a bit more explicit...
Update
If you already have the data you want to insert for each user in a table, you can use:
INSERT INTO data (UserID,key,value)
SELECT u.UserID,dd.key,dd.value FROM users u,default_data dd WHERE ...
If you don't (and don't want to store it in a table), you can use;
INSERT INTO data (UserID,key,value)
SELECT UserID,'some key',10 FROM users WHERE ...
UNION ALL
SELECT UserID,'other key',20 FROM users WHERE ...
or (to avoid the repetition of the WHERE clause):
INSERT INTO data (UserId,key,value)
SELECT u.UserID,dd.key,dd.value
FROM users u,
(
SELECT 'some key' AS key,10 AS data
UNION ALL
SELECT 'other key',20
) dd
WHERE ...
There are probably more ways to do it.
Build your select query to fetch data from 2 table based on User ID join. Then insert the result set into the desired table.
INSERT INTO DATA (SELECT A.USER_ID, B.KEY, B.VALUE FROM USERS A, SECOND_TABLE B WHERE A.USER_ID = B.USER_ID AND A.USER_ID IN (.....))
Structure of my tables:
posts (id, name, user_id, about, time)
comments (id, post_id, user_id, text, time)
users_votes (id, user, post_id, time)
users_favs ( id, user_id, post_id, time)
How can I combine these four queries (not with UNION):
SELECT `id`, `name`, `user_id`, `time` FROM `posts` WHERE `user_id` = 1
SELECT `post_id`, `user_id`, `text`, `time` FROM `comments` WHERE `user_id` = 1
SELECT `user`, `post_id`, `time` FROM `users_votes` WHERE `user` = 1
SELECT `user_id`, `post_id`, `time` FROM `users_favs` WHERE `user_id` = 1
Should I use JOINs?
What would the SQL query for this be?
You don't want to join these together.
The kind of JOIN you'd use to retrieve this would end up doing a cross-product of all the rows it finds. This means that if you had 4 posts, 2 comments, 3 votes, and 6 favorites you'd get 4*2*3*6 rows in your results instead of 4+2+3+6 when doing separate queries.
The only time you'd want to JOIN is when the two things are intrinsically related. That is, you want to retrieve the posts associated with a favorite, a vote, or a comment.
Based on your example, there's no such commonality in these things.
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.