Is there a way to check if the rows already exist and is active?
Let say the rows look like that:
ID: 123456
Active : 1
I know I could do something like that:
SELECT * FROM TableName WHERE id=123456 AND Active=1;
But that's not what I want.
And if I try to insert a query into the table again with the same information it should not duplicate.
But the constraint should work if it's not active (0).
I don't want to do the verification code wise. I would like the database server to do the verification.
Thanks!
You could use a trigger before insert to block the insert if and only if there is already an active row.
create trigger tr_bi_TableName
before insert on TableName
for each row
begin
if((select count(*) from TableName where id = new.id and active = 1) > 0) then
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'There is already an active row for this id.';
end if
end
SQLState 45000 means unhandled user-defined exception.
See documentaton for more info on signal.
Related
I have a certain user and i want to grant him permissions to alter tables, but only tables with his data, for example the user can change his profile data, but only his data. what i mean by this is altering the rows with his id on it.
grant update on table1 where table1.id_user = id_user to user;
is this possible to do?
This is not possible in a standard way in MySQL. You would have to use a trigger.
For example, the following compares the value of ID_USER that was passed in the update statement to the name of the current database user, and aborts the update if they are different, using the SIGNAL syntax to raise the error.
DELIMITER $$
CREATE TRIGGER checkUpdateTable1
BEFORE UPDATE ON Table1
FOR EACH ROW
BEGIN
SELECT 1
FROM mysql.user
INTO #is_root
WHERE super_priv='Y' AND USER() = CONCAT(user, '#', host);
IF (#is_root IS NULL AND NEW.ID_USER <> USER()) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'error message';
END IF
END
$$
DELIMITER ;
NB : after Raymond Nijland's comment, I edited the post to allow SUPER users to update any row.
I currently have two separate tables with a third to be added which store recipes, items and custom_meal (the one to be created).
I am looking to create a Meal Planner where the user may add either a recipe, item or custom item as their breakfast/lunch/dinner for a specific day.
I have an idea for my new tables below although going down this path I would have no way of ensuring that the meal_planner.id can only be either a recipe, item or custom.
With well written queries I realise that this should never be allowed to be an issue, although as someone with a great deal still to learn about databases I would much prefer a solution that ensures data may never be entered when it shouldn't be.
The current columns of interest on tables I'm already using are:
recipe.id
item.id
user.id
My current thoughts on the "Meal Planner" tables would be:
Table: meal_planner
id - primary key
user_id - foreign key user.id
mdate - the day which the meal is planned for
mtime - whether the meal is breakfast, lunch or dinner.
mcomment (can be null)
Table: meal_is_recipe
meal_id - foreign key meal_planner.id
recipe_id - foreign key recipe.id
Table: meal_is_custom
meal_id - foreign key meal_planner.id
custom_id - foreign key custom_meal.id
Table: meal_is_item
meal_id - foreign key meal_planner.id
item_id - foreign key item.id
Table: custom_meal
id - primary key
user_id - foreign key user.id
name - varchar to hold the name of the meal
This should allow me to use joins to grab all the required data for display although as mentioned it bugs me that their is no constraint stopping a meal.id being used in recipe, item and/or custom.
Ok so since posting this I have discovered "triggers".
Triggers are similar to a stored procedure although in a triggers case they will fire automatically when the set condition is met, for example before completing an insert, update or delete on a table.
In my case I have created six triggers, two for each meal_is_x table to check if the meal_id already exists in one of the other tables and return an error instead of the insert if it does.
I didn't have any luck through the phpmyadmin query using examples so I created my triggers in phpmyadmin by selecting the database, clicking on the Triggers tab, clicking add new trigger and then entering the following (modified slightly for each different table and whether it was for insert or update).
Trigger name: meal_is_recipe_BeforeInsert
Table: meal_is_recipe
Time: BEFORE
Event: INSERT
Definition:
BEGIN
DECLARE custom_match INT;
DECLARE item_match INT;
SELECT COUNT(1) INTO custom_match FROM meal_is_custom
WHERE meal_id = NEW.meal_id;
IF custom_match > 0 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'A Custom record with that meal id already exists';
ELSE
SELECT COUNT(1) INTO item_match FROM meal_is_item
WHERE meal_id = NEW.meal_id;
IF item_match > 0 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'An Item record with that meal id already exists';
END IF;
END IF;
END
Definer: I left this blank as the user that made the query if fine to run the trigger.
I believe I should also have been able to get the same result with the MySQL query but as I mention this did not work straight away for me in phpmyadmin and rather than look into it further the triggers tab was simple enough:
DELIMITER //
CREATE TRIGGER meal_is_recipe_BeforeInsert BEFORE INSERT ON meal_is_recipe
BEGIN
DECLARE custom_match INT;
DECLARE item_match INT;
SELECT COUNT(1) INTO custom_match FROM meal_is_custom
WHERE meal_id = NEW.meal_id;
IF custom_match > 0 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'A Custom record with that meal id already exists';
ELSE
SELECT COUNT(1) INTO item_match FROM meal_is_item
WHERE meal_id = NEW.meal_id;
IF item_match > 0 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'An Item record with that meal id already exists';
END IF;
END IF;
END//
DELIMITER ;
As I mentioned in the question my knowledge working with databases still has a long way to go so whilst I have tested this with my initial question schema and it works, I cannot guarantee this is the most efficient way of achieving the outcome.
I'm using MySQL. I have a record on A table with a soft delete column: active with a value of 0.
This row is linked to 11 tables. All have the same active column.
I need to be sure that the record on A is deleted only if all the references across the 11 tables have active = 0 also.
I know I can write a view with these queries to get if I can "deleted" or not. But this is one example and IMO not very practical solution. Cascade update won't work either because I can't delete the parent row if any of the child is still active.
Thanks!
This should work if you create the active_view as you said you could. Just add the active flags of all the related tables into the foreign_active column, and you should be good to go.
CREATE TRIGGER before_update_student
BEFORE UPDATE ON student FOR EACH ROW
BEGIN
IF NEW.active = 0 AND (SELECT foreign_active FROM active_view
WHERE id = NEW.id) > 0
THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Cannot delete student when active roles exist.';
END IF;
END;
I have the following three (InnoDB-) tables in a MySQL database:
Entity (DLID)
Category (CatID, CatName)
hasCategory (DLID, CatID)
Now, upon insertion into the table hasCategory I would like to make sure, that each Entity is associated with at least one Category. Thus, I wrote the following trigger:
delimiter |
create trigger Max before insert on hasCategory for each row begin
if (exists (select distinct DLID from Entity where not exists (select distinct new.DLID from new))) then
signal sqlstate '45000'
set message_text = 'Min of 1 category per entity required';
end if;
end|
delimiter ;
Now, when I execute the following query: insert into hasCategory values (1, 1); I get the error error code 1146: table mydb.new does not exist. I have created some other triggers similar to this one, also referring to the new-table, where it worked perfectly well. Yet, I don't get it, what causes the error in this particular trigger.
Is it possible that the select statement causes some trouble? I've read that only select into statements are valid in procedures, but I don't know if this has got anything to do with this.
Thanks for your help!
select distinct new.DLID from new
there is no table new in your DB just as the error states. You can use NEW for the record that will be inserted in your trigger but you cannot use it as a table name and select from it.
try
if (select 1 from Entity where DLID = new.DLID) = 1 then
I have a row in a table that I do not want to be changed (ever).
Is it possible to set a MySQL row to READ-ONLY so that it cannot be updated in any way? If so, how?
If not, is it possible to set a permanent value in one of the columns of that row so that it cannot be changed? If so, how?
Thanks.
This is likely to be business logic, which probably doesn't belong in your data storage layer. However, it can nonetheless be accomplished using triggers.
You can create a BEFORE UPDATE trigger that raises an error if a "locked" record is about to be updated; since an error occurs before the operation is undertaken, MySQL ceases to proceed with it. If you also want to prevent the record from being deleted, you'd need to create a similar trigger BEFORE DELETE.
To determine whether a record is "locked", you could create a boolean locked column:
ALTER TABLE my_table ADD COLUMN locked BOOLEAN NOT NULL DEFAULT FALSE;
DELIMITER ;;
CREATE TRIGGER foo_upd BEFORE UPDATE ON my_table FOR EACH ROW
IF OLD.locked THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Cannot update locked record';
END IF;;
CREATE TRIGGER foo_del BEFORE DELETE ON my_table FOR EACH ROW
IF OLD.locked THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Cannot delete locked record';
END IF;;
DELIMITER ;
UPDATE my_table SET locked = TRUE WHERE ...;
Note that SIGNAL was introduced in MySQL 5.5. In earlier versions, you must perform some erroneous action that causes MySQL to raise an error: I often call an non-existent procedure, e.g. with CALL raise_error;
I cannot create an additional column on this table, but the row has a unique id in one of the columns, so how would I do this for that scenario?
Again, if you absolutely must place this logic in the storage layer—and cannot identify the locked records through any means other than the PK—you could hard-code the test into your trigger; for example, to "lock" the record with id_column = 1234:
DELIMITER ;;
CREATE TRIGGER foo_upd BEFORE UPDATE ON my_table FOR EACH ROW
IF OLD.id_column <=> 1234 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Cannot update locked record';
END IF;;
CREATE TRIGGER foo_del BEFORE DELETE ON my_table FOR EACH ROW
IF OLD.id_column <=> 1234 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Cannot delete locked record';
END IF;;
DELIMITER ;
But this is absolutely horrible and I would do almost anything to avoid it whenever possible.