I need to clean up the users table and several related tables. So I create a variable that contains the userids and I want to use those values instead of using a sub-query for each delete statement.
I get a syntax error with this. What am I doing wrong?
DELIMITER $$
CREATE PROCEDURE `DBNAME`.`SP_PURGE_DISABLED_USERS` ()
BEGIN
select userid into #disabled_users from USERS where disabled=1;
delete from USER_ACTIVITY where userid in SELECT userid FROM #disabled_users;
delete from USER_PREFS where userid in SELECT userid FROM #disabled_users;
-- <snip> several other related tables to be cleaned up
delete from USERS where userid in SELECT userid FROM #disabled_users;
END
For one thing, at the end of your procedure you need to put your delimiter:
END$$
Then reset you're delimiter back to ;
DELIMITER ;
In the procedure, your delete statements don't make sense. I'd stop using the parameter to store the results of the select and an inner select:
delete from USER_ACTIVITY
WHERE userid in ( select userid from users where disabled=1 );
If you only have a realatively small set of disabled users to delete, you could use group concat and find in set:
DELIMITER $$
CREATE PROCEDURE `DBNAME`.`SP_PURGE_DISABLED_USERS` ()
BEGIN
select GROUP_CONCAT(userid) into #disabled_users from USERS where disabled=1;
delete from USER_ACTIVITY where FIND_IN_SET(userid, #disabled_users);
delete from USER_PREFS where FIND_IN_SET(userid, #disabled_users);
-- <snip> several other related tables to be cleaned up
delete from USERS where FIND_IN_SET(userid, #disabled_users);
END$$
DELIMITER ;
I wouldn't do this with many thousands of userid's though... there may be a limit to string length in a variable.
I decided to go with a temporary table. It cut the stored procedure execution time in half.
CREATE PROCEDURE `DBNAME`.`SP_PURGE_DISABLED_USERS` ()
BEGIN
create temporary table disabled_users
as (select userid from USERS where disabled=1);
delete from USER_ACTIVITY where userid in SELECT userid FROM disabled_users;
delete from USER_PREFS where userid in SELECT userid FROM disabled_users;
-- <snip> several other related tables to be cleaned up
delete from USERS where userid in SELECT userid FROM disabled_users;
drop table disabled_users;
END
Thanks to all who helped on this question.
Related
So I'm trying to insert information into a table upon deletion in another table, but I want to gather information also from other tables again which will also be deleted under a cascade. I mean best way to explain it is:
DELIMITER $$
CREATE TRIGGER before_student_delete
BEFORE DELETE
ON students FOR EACH ROW
BEGIN
INSERT INTO student_deletion(student_ID, course_code, department_code)
VALUES(OLD.student_ID,select course_code
from enrolled
where student_ID = OLD.student_ID,
select department_code
from course_dept_key
where course_code = select course_code
from enrolled
where student_ID = OLD.student_ID);
END$$
You know I'm trying to use the old information to find that info in other tables, but...
I'm totally new at this, thanks for any help.
Use for the Selects session variables and insert them into the table.
Like
DELIMITER $$
CREATE TRIGGER before_student_delete
BEFORE DELETE
ON students FOR EACH ROW
BEGIN
SELECT course_code INTO #course_code
FROM enrolled
WHERE student_ID = OLD.student_ID;
SELECT department_code INTO #department_code
FROM course_dept_key
WHERE course_code = #course_code;
INSERT INTO student_deletion(student_ID, course_code, department_code)
VALUES(OLD.student_ID,#course_code,#department_code);
END$$
DELIMITER ;
I have a table which must have minimum one item. How can I prevent to remove the last item?
For example, I have a table named USER, this table must contain at least one user. I can delete a user, but I cannot delete when the number of users is equal to one.
you may need to write sp like this.
CREATE DEFINER=`root`#`localhost` PROCEDURE `deleteuser`(userId INT)
BEGIN
IF EXISTS(SELECT * FROM USER WHERE Id<>userId) THEN
DELETE FROM USER WHERE Id=userId;
END IF;
END$$
DELIMITER ;
and you can call it.
call deleteuser(1);
How about:
DELETE FROM USER
WHERE ID = #ID
AND (SELECT COUNT(*) FROM USER AS t1) > 0
Try to use TRIGGERS Which will validate the number of rows before Delete query on that table.
I have a relationship between two entities in my database. So before I create a single row I need to delete the row in the relation entity, like so:
user userHous
id 1 1 2
So when I delete a user row, it should do a before trigger and delete userHous.
in Mysql this was easily done with my old trigger:
USE `db`;
DELIMITER $$
CREATE DEFINER=`user`#`ip` TRIGGER `BDEL` BEFORE DELETE ON `user`
FOR EACH ROW
begin
DELETE FROM user WHERE userid = OLD.userid;
DELETE FROM house WHERE userid = OLD.userid;
end
I'm not sure how to go about doing this with { FOR | AFTER | INSTEAD OF }
I made this
CREATE TRIGGER [dbo].[Trigger]
ON [dbo].[user]
INSTEAD OF DELETE
AS
BEGIN
DELETE FROM userHouse WHERE userid = userid;
DELETE FROM user WHERE userid = userid;
END
But this just deletes all rows in both Tables, I guess because I put userid = userid. How do I retrieve the "parameter" which was in the query that made the trigger?
Try this
CREATE TRIGGER [dbo].[Trigger]
ON [dbo].[user]
AFTER DELETE
AS
DECLARE #user_id INT
SELECT #user_id = (SELECT userid FROM DELETED)
BEGIN
DELETE FROM userHouse WHERE userid = #user_id;
END
I've the following trigger which is really bugging me.
I want to do an UPDATE inside the trigger, but MySQL doesn't allow it, since the statement "triggering" the trigger refer to the participants table.
What can I do in order to make it work? I've considered stored procedures, but they are also forbidden in this case.
The logic is: I've a table with participants in a meeting. The invariant that people from the 'tap' group should always accept the invitation if someone from the 'vip' group is attending. In this case, if a 'vip' is attending the meeting I must UPDATE all 'tap' attendances, but how is this possible???
DELIMITER $$
CREATE TRIGGER tap_meet_vip
BEFORE INSERT ON participants
FOR EACH ROW BEGIN
IF (NEW.pid IN (SELECT userid FROM people WHERE `group` = 'vip')) THEN # new participant is a member of the vip group
UPDATE participants SET `status` = 'a' WHERE pid IN (SELECT userid FROM people WHERE `group` = 'tap') AND meetid = NEW.meetid ; # update status to `accept` of all tap group participants
ELSEIF (NEW.pid IN (SELECT userid FROM people WHERE `group` = 'tap') AND EXISTS (SELECT * FROM participants INNER JOIN people ON pid = userid WHERE `group` = 'vip')) THEN # new participant is a member of the tap group and meeting has vip participant
SET NEW.`status` = 'a';
END IF;
END;$$
The above is written using MySQL and the version is 5.6.
It seems that you can't do all this in a trigger. Trigger is already locked the table so update statement can't get the lock. According to the mysql:
Within a stored function or trigger, it is not permitted to modify a
table that is already being used (for reading or writing) by the
statement that invoked the function or trigger
So you can create a stored procedure, that inserts into/Updates the target table, then updates the other row(s), all in a transaction. With a stored proc you'll manually commit the changes (insert and update).
DROP PROCEDURE IF EXIST marks;
DELIMITER $$
CREATE PROCEDURE marks(IN marks INT)
BEGIN
INSERT INTO first VALUES(marks);
END $$
DELIMITER ;
START TRANSACTION;
INSERT INTO first VALUES (16);
CALL marks(18);
COMMIT;
So far I have tried many different ways of accessing the data on three tables using a stored procedure. First I tried a simple select statement :
create procedure reportCodes () begin
SELECT Numbers.serial_numb, numOwner.lName, numOwner.fName, numOwner.email,
location.long, location.lat, Numbers.dateValidated
FROM Numbers, Owner, location
WHERE Numbers.used = true AND Numbers.id=numOwner.Numbers_id AND
Numbers.id=location.Numbers_id;
end$$
(names changed to protect the guilty)
Running the stored procedure in phpmyadmin results in the first instance of the record (1 out of two ‘true’ in the test database). Running just:
SELECT Numbers.serial_numb, numOwner.lName, numOwner.fName, numOwner.email,
location.long, location.lat, Numbers.dateValidated
FROM Numbers, Owner, location
WHERE Numbers.used = true AND Numbers.id=numOwner.Numbers_id AND
Numbers.id=location.Numbers_id;
in the phpmyadmin SQL tab returns both records. Then I tried a temp table:
create procedure reportCodes () begin
CREATE TEMPORARY TABLE used_numbers AS (
SELECT Numbers.serial_numb, numOwner.lName, numOwner.fName, numOwner.email,
location.long, location.lat, Numbers.dateValidated
FROM Numbers, Owner, location
WHERE Numbers.used = true AND Numbers.id=numOwner.Numbers_id AND
Numbers.id=location.Numbers_id);
SELECT * FROM used_numbers; end$$
Returns 1 of 2 records as the procedure but both records in console. Finally I tried changing my table to a join:
CREATE PROCEDURE reportCodes()
begin
create temporary table used_numbers AS (
SELECT Numbers.serial_numb, numOwner.lName, numOwner.fName, numOwner.email,
location.long, location.lat, Numbers.dateValidated
FROM Numbers JOIN numOwner
ON Numbers.id=numOwner.Numbers_id
JOIN location ON
numOwner.Numbers_id=location.Numbers_id
WHERE Numbers.used = true
);
SELECT * FROM used_numbers; end$$
Same results as above. I’m at a loss as to why running just the SQL would show both test records but running the procedure with the exact same code only yields one.
Thanks
in your query, numOwners isn't a valid table being selected against, so something's wrong. Have you tried running your SQL in the Query window in phpMyAdmin to ensure that the EXACT same query is returning 2 rows?
I presume the "Owner" table is supposed to be "numOwner", so I've re-written the stored procedure call below. Also, I'm not sure what types of values you're storing in Numbers.used to evaluate to "TRUE". I will presume you're using a TINYINT(1), so I've altered that, as well. I hope this helps.
DELIMITER $$
USE `db`$$
DROP PROCEDURE IF EXISTS `reportCodes`$$
CREATE PROCEDURE `reportCodes`()
BEGIN
SELECT
n.serial_numb,
o.lName,
o.fName,
o.email,
l.long,
l.lat,
n.dateValidated
FROM Numbers n
INNER JOIN numOwner o ON n.id=o.Numbers_id
INNER JOIN location l ON n.id=l.Numbers_id;
WHERE n.used = 1
END$$
DELIMITER ;