I'm new to SQL so I don't understand why this this query is not working. Thank you in advance
CREATE VIEW temp AS
SELECT return_date_time, renting_date_time
FROM renting;
CREATE TRIGGER charge_calc AFTER UPDATE ON renting.return_date_time
FOR EACH ROW
BEGIN
UPDATE renting
SET new.charge =(select m.charge_per_day
from movies m,renting as r
where (m.id=r.id_movie))*datediff(temp.return_date_time,temp.renting_date_time);
END
DATA DIAGRAM
I don't think you should run the update of the charge on that way; if you're wanting to set the values of a row in a trigger that is firing because the row has been updated, all you need to do is
SET new.columnname = somevalue
To set the value of columnname on the updated row. You don't kick off another update of the table within the update trigger that is firing upon update of the table.
Next, you seem to be joining all the rows in movies together with all the rows in renting, which will surely return hundreds or thousands of rows, and you're trying to set one value. This is broken logic: which of the thousands of movie rows do you want MySQL to pick? It won't choose; the logic is broken
Step back for moment and consider: this is an update trigger of the renting table. It fires for every row updated and the row being updated is accessible by the new. specifier. There is a new.movie_id property - that's the id of the movie being updated right now. If you want some data out of the movies table, select it based on the movie id in the new row (the row being updated) I.e. new.movie_id
You don't need the temp view either - if you want to know the return date, surely that is also part of the new. row
All in, this trigger should probably be a single line along the following idea:
SET new.charge = (select rate from movies where id = new.movie_id) * datediff(date_rented, date_returned, day)
As a side comment, I think the front end app should be doing this, not triggers in the database
Related
Want to Insert multiple rows into table "txn_dbtransactionnotification" when new data is inserted/update into the "Txn_Sales" table.
Suppose 5 rows are present into the transaction table "Txn_Sales" and 1 row into the master table "Mst_Sales".
So when Data into the master table "Mst_Sales" is modified then related data into the table "Txn_Sales" must be modified and the update be sent to the table "txn_dbtransactionnotification" so how can I use a Cursor inside a Trigger so when data in table "Mst_Sales" is modified then data into the table "Txn_Sales" must be modified row by row.
Cursor support is incomplete in MySQL's stored routine language. It does not support UPDATE ... WHERE CURRENT OF CURSOR like some other brands of SQL implementation.
But in many cases, it's not necessary to update row by row. It's simpler to update a batch. Think of updating sets of rows, instead of row by row.
For example:
CREATE TRIGGER mytrigger AFTER UPDATE ON Mst_Sales
FOR EACH ROW
BEGIN
-- update a set of rows related to the same product that
-- spawned this trigger.
UPDATE Txn_Sales SET price = NEW.price WHERE product = NEW.product;
-- and enter a record in the notifications table
INSERT INTO txn_dbtransactionnotification ...;
END
I had to imagine what type of update you were talking about, because you described the problem in such an abstract way. In a real-life scenario, I don't think changing the price of a product would apply retroactively to past transactions. So the example above is only to show the technique, not the real code you would use.
I have a MYSQL database with a table like:
Id myId Description
ABD1 0 some desc
ABD2 1 some desc
....
myId is an autoincremented column. I need to create a mysql trigger that will prevent anyone from changing the first myId value assigned to a row at the time of its insertion. How can this be done in mysql? I was thinking:
CREATE TRIGGER upd_check BEFORE UPDATE ON myTable
FOR EACH ROW
BEGIN
NEW.myId = OLD.myID
END
Could this be enough? If so, is this trigger going to run for all rows of my table? only for the new ones? I just need for one row.
Thx
To answer your question directly:
Could this be enough?
Yes, this will make sure any UPDATE will not change the value of your myID column. It will always reset that column to the value it was prior to the UPDATE.
If so, is this trigger going to run for all rows of my table? only for the new ones?
The answer is in the manual page https://dev.mysql.com/doc/refman/5.7/en/trigger-syntax.html which says:
The statement following FOR EACH ROW defines the trigger body; that is, the statement to execute each time the trigger activates, which occurs once for each row affected by the triggering event.
In other words, the trigger executes once for each row matching the condition in your UPDATE's WHERE clause.
It will not apply to every row in the table—unless your WHERE clause matches every row.
i have a table called 'tblDive' with columns:
create table tblDive (
DiveNumber int
InstructorNumber int
ClubNumber int
InstructorSigniture date
)
and another table:
create table tblWorksAt (
InstructorNumber int
ClubNumber int
StartWorkingDate date
EndWorkingDate date
)
the table 'tblWorksAt' has this record:
InstructorNumber | ClubNumber | StartWorkingDate | EndWorkingDate
1 2 1.1.2000 1.1.2005
i want to create a trigger that checks when inserting a new dive, if the instructor really worked at this club in the same time of signing on the dive.
so for example if i insert a new dive:
insert into tblDive (DiveNumber InstructorNumber ClubNumber InstructorSigniture)
values 111, 1, 2, 1.1.2009
i won't be able to insert this record because instructor number 1 stopped working at club number 2 in 1.1.2005
An alternative to using a trigger is to use a check constraint and a user defined function.
A function that checks is the instructor is employed at the right club at the right time:
CREATE FUNCTION CheckEmployment(#InstructorNumber int, #ClubNumber int, #checkdate date)
RETURNS int
AS
BEGIN
DECLARE #retval int
SELECT #retval = COUNT(*)
FROM tblWorksAt
WHERE InstructorNumber = #InstructorNumber
AND ClubNumber = #ClubNumber
AND (EndWorkingDate IS NULL OR EndWorkingDate > #checkdate)
RETURN #retval
END;
GO
And a check constraint using it:
ALTER TABLE tblDive
ADD CONSTRAINT chkEmployed
CHECK (dbo.CheckEmployment(InstructorNumber, ClubNumber, InstructorSigniture) != 0);
This might not be the most efficient way, but it should get the job done. The logic in the function might need improvement too, I might have missed something.
Sample SQL Fiddle showing it in action.
What I'll do is give you some hints and not-so-obvious information about triggers that may help you write the trigger, but you need to write it.
The inserted table
In SQL Server triggers, there are 2 pseudo-tables that you can reference: inserted and deleted tables. The names are somewhat deceiving, particularly if you are doing an UPDATE. The thing to remember is that under the hood, an UPDATE is a delete plus insert.
So essentially, inserted is the new (or updated) rows and deleted is the deleted rows and/or previous rows from an UPDATE before the changes were applied.
For a straight INSERT statement, the deleted table ought to be empty.
Therefore, you want to look for rows in inserted that meet a certain set of criteria. There are two logical ways to approach this:
Check if all rows meet this condition
Look for any rows that do not meet the condition.
Join
If you join inserted to tblWorksAt, you now have all the data you need. Something like this to join the tables and find rows that pass your business rules:
select 1
from inserted i
inner join tblWorksAt wa on wa.InstructorNumber = i.InstructorNumber and i.ClubNumber = wa.ClubNumber
where i.InstructorSignature between wa.StartWorkingDate and wa.EndWorkingDate
What to do with the query
Like I said before:
Check if all rows meet this condition
Look for any rows that do not meet the condition.
To check if all rows pass that criteria, you could:
Check if the count of rows matching that query equals the number of total rows in inserted, without a join.
Loop over each row in inserted which I will tell you right now, is almost never a good idea in a trigger.
To check if at least one row fails to pass the criteria, you could:
Change the between to not between and wrap this query around if (exists(...)).
Change the inner join to left outer join, move the where clause to the join, and then add a new WHERE clause that says tblWorksAt.InstructorNumber is null, then wrap this query around if (exists(...)).
Throwing an error
Now you know how to find rows that pass or fail. Now you just need to throw an error to prevent the statement from completing and prevent the data from persisting. I will leave that as an exercise to you. It should be easy to research.
I'm trying to update a table with values from another table. What I want to do is, each time an update happens, to delete the row in the table where I'm getting the data from.
This is my code so far:
UPDATE city SET city_longitude = (SELECT city_longitude FROM cities WHERE city.shortCity = cities.city_name OR city.cityName = cities.city_name LIMIT 1) LIMIT 100
This update is working so far, but I have to delete the rows where I'm updating FROM (the ones in the cities table).
Is there any way to do this?
delete from cities where some_id = 1
Are you unable to do something along these lines after your UPDATE statement has finished? You may want to have something that actually confirms your UPDATE statement succeeded and then only DELETE afterwards.
The safer alternative is to have a flag called isUpdated or something along those lines, which you can set to true after an UPDATE but you keep the row in the original table.
An example for the sake of the question: I have a database which contains users, questions, and answers. Each user has a score which can be calculated using the data from the questions and answers tables. Therefore if I had a score field in the users table, it would be redundant. However, if I don't use a score field, then calculating the score every time would significantly slow down the website.
My current solution is to put it in a score field, and then have a cron running every few hours which recalculates everybody's score, and updates the field.
Is there a better way to handle this?
In my opinion, eliminating redundancy is secondary to creating efficient (and legible!) code.
Instead of using cron to update the score, why not create a trigger to update the user's score when a record is inserted in the appropriate place?
If it's a matter of updating scores when when the answers table is updated, then you would do something like this:
create trigger 'scores_increment'
after insert on 'answers'
for each row begin
update 'users' set user_score = user_score + 1 where user_id = NEW.user_id;
end;
create trigger 'scores_decrement'
after delete on 'answers'
for each row begin
update 'users' set user_score = user_score - 1 where user_id = NEW.user_id;
end;
Although, occasionally, DBMSes 'hiccup' and forget to run a trigger, or something. I'd suggest putting your original cron script that sets the scores to running once a week.