delete from multiple tables - mysql

I have a table of users
user id
3 ,Frank
4 ,Steve
5 ,Joe
and a table of roles where column 1 is id of users in above table:
1, billing
3, Admin
2, Admin
4, user
5, billing
as you can see users 1 and 2 has been orphaned, and there is no user with id of 2. how can i delete this entry from the roles table? there could be dozens of entries in roles that dont have a user.
will this work?
DELETE from roles,users where roles.userId!==users.userId

You don't need to delete from users if the user is not there. So you can just do:
delete r from roles r
where not exists (select 1 from users u where u.userId = r.userId);
As mentioned in a comment, if you declared the roles.userId value to be a foreign key reference, then the database would not let this happen. After the data is fixed, you can do:
alter table roles add constraint foreign key (userId) references user(userId);

DELETE FROM roles WHERE id NOT IN (SELECT id FROM users)

Q: Will this work?
A: Close, but no cigar. No, the statement in the question won't perform the specified operation.
First, write a query to identify the orphaned rows. An anti-join pattern is a common approach (though there are several other workable approaches):
SELECT r.*
FROM `roles` r
LEFT
JOIN `user` u
ON u.id = r.user_id
WHERE u.id IS NULL
This uses an "outer" join, to return all rows from roles, along with matching rows from user. If no matching row is found in user, the columns from the user table will be returned as NULL. Given the equality comparison in the join predicate (ON clause), we know that all matching rows will have a non-NULL value for u.id. The "trick" is the predicate in the WHERE clause that excludes all rows that found a match, so we are left with rows in roles that don't have a matching row in users
(If you want to examine the rows that will be deleted and/or create a "backup" of those rows, this query gives you that.)
Next, convert the SELECT statement into a DELETE statement. For this particular statement, it's very straightforward with MySQL. The only change required is to replace the SELECT keyword with the DELETE keyword:
DELETE r.*
FROM `roles` r
LEFT
JOIN `user` u
ON u.id = r.user_id
WHERE u.id IS NULL
This is an example of just one of several workable approaches.

Related

MySQL selecting tables where user exists

Let's say I have a mySQL table called "user" that contains a userid and a password or something. Then I have other tables for different purposes, such as a table for their favorite food, which would contain their userid and favorite food.
Is there a way to query for tables that contains a specified userid and checks whether they have an entry in that table?
So if I have three tables called favorite_food, favorite_drink, and favorite_candy, but userid only had values in favorite_food and favorite_drink, I want the query to return favorite_food and favorite_drink.
I'm a beginner and I couldn't quite grasp the concept of linking. Userid is the primary key of user and the other tables reference it?
Read more about UNION. I think that this is what you need.
SELECT * from favorite_food where `userid` = 50
UNION
SELECT * from favorite_drink where `userid` = 50
UNION
SELECT * from favorite_candy where `userid` = 50
In SQL "linking" is represented by JOINs. Since (as you indicated) some users will not have 1 or 2 favorite objects we will need to use LEFT OUTER JOINs. Then we will have NULLs in the output in place of missing favorites.
In your case:
SELECT u.userid, f.food_name, d.drink_name, c.candy_name
FROM user u
LEFT JOIN favorite_food f ON u.userid=f.userid
LEFT JOIN favorite_drink d ON u.userid=d.userid
LEFT JOIN favorite_candy c ON u.userid=c.userid
ORDER BY u.user.id
This will give you favorites for all users in your user table.

Left join and show the non-foreign key

I have two tables that I'm joining, Users and Addresses.
The Users table has a unique (but not primary) key called userID.
The Addresses table has a primary key called userID.
I'm trying to left-join the tables so that I get all the info from the Users table regardless if it has a match in the Addresses table, so I used
select * from users u left join addresses a on a.userID = u.userID
This works, but it only shows userID's if there is a match. How do I get the userID regardless if there's a match or not?
Edit: The following image shows what my current query (left join) returns:
Now it's clear what the problem is.
There's nothing "wrong" with the query, except that it is returning multiple expressions (columns) with the same alias. And one of them is overriding the other. (Not in terms of the resultset being returned, all the columns and rows are there; the issue is with how your PHP script is processing the resultset. If you are referencing the values from the row by "association" (using the column name), only one of the column values gets referenced.
So, one way to fix that, is to qualify the columns in the query:
SELECT u.userID
, u.blah
, a.userID AS a_userID
, a.blah AS a_blah
FROM users u
LEFT
JOIN addresses a
ON ...
such that all column aliases are unique. Then all of those column names will be available by "association" in your PHP.
Using the query you provided should give a result in the u.userID column regardless of if it had a corresponding address. The a.userID column would only have data in the rows that were joined with an address.
You could change to:
select u.*, a.address from users u left join addresses a on a.userID = u.userID
Which would select only the relevant parts of the address and keep the whole u.

Amount of users in a group where one particular user is present mysql

I'm trying to optimize this query but I can't seem to get it. Any help is more than welcomed. This is inside a stored procedure, so the 1 is replaced by an IN parameter.
Table userGroupRelation
userGroupID BIGINT --> foreign key to userGroupID in groups table
userID BIGINT --> foreign key to the users table
Table folders
ownerID BIGINT --> foreign key to the users table
postersGroup BIGINT --> foreign key to userGroupID in groups table
other stuff ...
SELECT folders.*,COUNT(userGroupRelation.userID) AS users
FROM folders
LEFT JOIN userGroupRelation
ON folders.postingGroupID = userGroupRelation.userGroupID
WHERE folders.ownerID = 1
OR userGroupRelation.userGroupID IN (
SELECT userGroupID FROM userGroupRelation WHERE userID = 1)
GROUP BY folders.folderID;
I basically want to get all the folders a user can see (either because he owns it or because he is in a group of posters for that folder)
I suggest two approaches. One is similar to your own, but removing the subquery and correcting the COUNT clause. I should mention it is unclear from your question whether the COUNT ... AS users result is of importance here, since you only specified you want to get all folders for a user.
SELECT
folders.*,
COUNT(DISTINCT IFNULL(userGroupRelation.userID, folders.ownerID)) AS users
FROM
folders
LEFT JOIN userGroupRelation ON folders.postingGroupID = userGroupRelation.userGroupID
WHERE
folders.ownerID = 1
OR userGroupRelation.userID = 1
GROUP BY
folders.folderID
;
This is somewhat better than your own solution because the subquery is eliminated, but it still leaves with this OR condition which is difficult to optimize -- it works on two different tables.
Edit: this next solution neither answers the question nor does it work at all :(
A completely different approach is to start with the users table (assuming you have one, of course):
SELECT
folders.*
FROM
users
LEFT JOIN folders ON (folders.ownerID = users.id)
LEFT JOIN userGroupRelation ON (userGroupRelation.userId = users.id)
LEFT JOIN folders ON (folders.postingGroupID = userGroupRelation.userGroupID)
WHERE
users.id = 1
GROUP BY
folders.folderID
;
This one is far better optimized if you have proper indexes on users.id and all joining columns. However:
It will not tell you the amount of (other) users per folder
SELECT filders.* .... GROUP BY filders.folderId is not strictly valid SQL -- though it will work if you're not using strict sql_mode. From you own query I can see you're not, so I won't dwell into that.

Delete operation in SQL with a Join in it

I have two tables, user and ban. User table has two columns id and type. Ban table also has two columns userID and bannedUserID. I want to delete records in ban table where bannedUserID has type of 'Admin'.
I've come up with such query but I am not sure if it is correct or not.
DELETE FROM ban WHERE ban.bannedUserID IN (SELECT id FROM user WHERE type = 'Admin')
Is it correct? Do I have to add/remove anything?
If you're not sure whether the DELETE is correct, you can test it by converting to an equivalent SELECT:
SELECT b.bannedUserID
FROM ban b JOIN user u ON b.bannedUserID = u.id
WHERE u.type = 'Admin';
Are the id's returned by that query the ones you want to DELETE?
Then try it in a transaction (if you use a storage engine that supports transactions, i.e. not MyISAM), so you can roll back if necessary:
START TRANSACTION;
DELETE b
FROM ban b JOIN user u ON b.bannedUserID = u.id
WHERE u.type = 'Admin';
/* do some SELECT to see if the correct rows have been deleted and no others */
/* but if anything appears wrong, and ban is an InnoDB table, you can... */
ROLLBACK;
Note that I'm using MySQL's multi-table DELETE syntax. It should work identically to your subquery approach, but in some cases it can be more flexible.

sql on mysql about join

the code below provide a result too much Infact i want to list the customer that never buy somethink How can i fix the code below
SELECT
webboard.listweb.id,
webboard.listweb.iditempro,
webboard.listweb.url,
webboard.listweb.useradddate,
webboard.listweb.expiredate,
webboard.prorecord.urlpostonweb
webboard.prorecord.urlpostonweb
FROM
webboard.listweb ,
webboard.prorecord
Where listweb.id Not In
(select webboard.prorecord.idlist From webboard.prorecord )
Using the syntax
FROM
webboard.listweb ,
webboard.prorecord
will perform a cartesian, or cross, join on the tables involved. So for every row in the table listweb all the rows in prorecord are displayed.
You need to use an INNER JOIN to only select the rows in listweb that have related rows in the prorecord table. What are the fields which identify the rows (your Primary Keys) and what is the name of the foreign key field in the prorecord table?
EDIT: Just re-read the question and comments and I see you want the rows in listweb which do not have an entry in prorecord
Your SELECT will then look like:
SELECT
webboard.listweb.id,
webboard.listweb.iditempro,
webboard.listweb.url,
webboard.listweb.useradddate,
webboard.listweb.expiredate,
webboard.prorecord.urlpostonweb
-- webboard.prorecord.urlpostonweb -- You have this field twice
FROM webboard.listweb LEFT JOIN webboard.prorecord
ON webboard.listweb.id = webboard.prorecord.idlist -- I'm guessing at the foreign key here
WHERE webboard.prorecord.idlist IS NULL