Sql Trigger Trouble - sql

Hey guys i cant get this trigger to work, ive worked on it for an hour or so and cant see to figure out where im going wrong, any help would be appreciated
CREATE OR REPLACE TRIGGER allergy
BEFORE INSERT ON
DECLARE
med VARCHAR2(20);
BEGIN
SELECT v.medication RCD.specify
INTO med
FROM visit v, relcondetails RCD
WHERE :new.medication = v.medication AND RCD.specifiy = 'allergies';
IF med = allergies THEN
RAISE_APPLICATION_ERROR(-20000, 'Patient Is alergic to this medication');
END IF;
END allergy;
When put into oracle
ERROR at line 6: ORA-04079: invalid
trigger specification

CREATE OR REPLACE TRIGGER allergy BEFORE INSERT ON
name of table here
FOR EACH ROW -- forgot this too
DECLARE
med VARCHAR2(20);
You should really be declaring this as %type.
med visit.medication%type;
BEGIN
SELECT v.medication RCD.specify
Requires a comma between columns
INTO med
Two columns need two variables
FROM visit v, relcondetails RCD
WHERE :new.medication = v.medication AND RCD.specifiy = 'allergies';
You have no join condition between your two tables, that's very bad. This query will perform a Cartesian between the two tables and then return all of them that have 'allergies' and :new.medication in their respective columns.
you also probably need a filter condition to limit the query to a particular patient or a particular visit. This query will do it for all patients and all their visits squared.
IF med = allergies THEN
I don't know what /allergies/ is in this IF. There's no variable that's defined as that and without quotes it's not a string.
RAISE_APPLICATION_ERROR(-20000, 'Patient Is alergic to this medication');
This error message reinforces what I said about your query. You think you're querying for a single patient but you're not.
END IF;
END allergy;
Seriously, if you're writing software to save a person from getting potentially life threatening medication then please consider some other line of work. I swear I'm not saying this to be rude, but your code sample shows almost no understanding of the pl/sql language or sql or any scrap of programming background. I think you started with some sample code and tried to modify it into something something. But you're really left with gibberish. I'm starting to think this is homework.

In addition to Mark's point that you are missing the table name, and Martin's point that you want this to be a row-level trigger, your actual body won't compile, for a couple of reasons.
You look like you're trying to select two columns, but you don't have a comma between them, and you only have one local variable in the INTO clause
You use an identifier allergies which is not declared anywhere.
I also doubt that your query is logically correct, but of course I don't know the database design so I can't say for sure.

BEFORE INSERT ON <TABLE NAME>
and why select both v.medication and RCD.specify when you're only selecting into one variable?

You've probably seen this but:
http://msdn.microsoft.com/en-us/library/aa258254(SQL.80).aspx
CREATE TRIGGER TriggerName
ON MyTableName
FOR MyEvent
AS
-- My Trigger Logic

Related

FOR loop in Oracle SQL or Apply SQL to multiple Oracle tables

My SQL is a bit rusty, so I don't know whether the following is even possible:
I have multiple tables t_a, t_b, t_c with the same column layout and I want to apply the same operation to them, namely output some aggregation into another table. For a table t_x this would look like this:
CREATE TABLE t_x_aggregate (
<here the col definitions which are the same for all new tables t_[abc]_aggregate>
);
INSERT INTO t_x_aggregate(id, ...)
SELECT id, SUM(factor*amount)
FROM t_x
WHERE some fixed condition
GROUP BY id;
I now want to execute something like a FOR loop around this:
for t_x in t_a, t_b, t_c
CREATE TABLE ...
INSERT INTO ...
end for
Is this possible in SQL? Or would I need to build a wrapper in another language for this?
So, the result of that operation would be 3 new tables? T_A_AGGREGATE, T_B_AGGREGATE and T_C_AGGREGATE?
I think that the fastest way is to write 3 separate CREATE TABLE statements, e.g.
create table t_a_aggregate as
select id, sum(factor * amount) suma
from t_a
where some_condition
group by id;
create table t_b_aggregate as
select id, sum(factor * amount) suma
from t_b
where some_condition
group by id;
create table t_c_aggregate as
select id, sum(factor * amount) suma
from t_c
where some_condition
group by id;
OK; I understand that queries aren't that simple, but nothing much changes - only table names in CREATE and FROM (perhaps somewhere else, but that's more or less "it"). Any decent text editor's search/replace capabilities should be able to do it quickly.
If you want to do it dynamically in a loop (read: PL/SQL), you can - but dynamic SQL doesn't scale, is difficult to maintain, is painful to debug. Therefore, if you're doing it only once, consider running 3 separate statements.
How to do it dynamically?
You'd have to create a string (we usually put them into a locally declared variable) which contains the whole DDL statement. Why? Because you can't execute DDL from a PL/SQL otherwise.
If there are multiple tables and/or columns involved, you'll have to combine "fixed" parts of the statement (like create table, select, from, order by) concatenated with "dynamic" parts - such as column names. Note that in between you have to concatenate commas as separators. Pay attention to usage of multiple single quotes as you have to escape them (or use the q-quoting mechanism).
Also, for multiple columns you'll probably have to do it in a loop, concatenating each new column to previously composed string.
It (the statement stored into the varirable) is the executed by EXECUTE IMMEDIATE. If it is correctly written, it'll succeed. Otherwise, it'll fail, but it won't tell you why it failed (that's why I said difficult debugging").
So, instead of executing it, we usually display that string (using dbms_output.put_line) so that we see how it looks like and - using copy/paste - try to execute it.
Basically, it can be quite complex and - as I said - difficult to maintain and debug.
For the FOR loop you need to use PL/SQL like this:(*)
declare
type array_t is table of varchar2(10);
array array_t := array_t('a', 'b', 'c');
lo_stmt varchar2(2000);
begin
lo_stmt :=
'CREATE TABLE t_'||array(i)||'_aggregate ('||
' <here the col definitions which are the same for all new tables t_[abc]_aggregate>'||
');'||
''||
'INSERT INTO t_'||array(i)||'_aggregate(id, ...)'||
'SELECT id, SUM(factor*amount)'||
'FROM t_'||array(i)||
'WHERE some fixed condition'||
'GROUP BY id;'||
execute immediate lo_stmt;
end loop;
end;
/
Look also at this SO question: How to use Oracle PL/SQL to create...
(*) #Littlefoot describes in the 2nd part of his answer valuable background to this program.

Cannot use OUTPUT clause with INTO clause when using Triggers to update records in SQL, when adding a record in FileMaker

I know a similar question to this has been asked a fair few times on here, but none seem to give me the answer I need - specifically because I am talking about the relationship between SQL and FileMaker.
See, I'm not actually using an OUTPUT clause at all in my Trigger! But FileMaker seems to either think I am, or insert something itself as an OUTPUT clause that I can't see.
Here is an example trigger:
CREATE TRIGGER [dbo].[TRG_person__name]
ON [dbo].[person]
AFTER INSERT,UPDATE
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
IF UPDATE ( person_name_first )
BEGIN
UPDATE person
SET person_name_first = dbo.mslTitleCase ( i.person_name_first )
FROM person AS x
JOIN inserted AS i ON x._pk_person = i._pk_person
WHERE x._pk_person = i._pk_person
END
--END IF
END
The mslTitleCase function has been declared and works, if I was to create a record using a SQL Query.
But I can an error when creating a new record in FileMaker.
Does anyone have any tips to find a way to stop this error in FileMaker?
Any guidance greatly appreciated.
Thank you
Lewis
This has been reported as a bug: http://help.filemaker.com/app/answers/detail/a_id/7870/~/external-sql-data-sources-(ess)%3A-unable-to-write-to-a-table-with-triggers-on
It does not look like it was resolved.
You may have other problems, but the correct syntax for the update is:
UPDATE p
SET person_name_first = dbo.mslTitleCase ( i.person_name_first )
FROM person p JOIN
inserted i
ON p._pk_person = i._pk_person
WHERE p._pk_person = i._pk_person;
The important change is that the update references the table alias not the table name. I think p is a much better alias than x for a table named person.
It sounds like your application is using the OUTPUT clause to directly return the modified data. If the table has any trigger, this is not allowed. They would have to output the modified data into a table, and then select the data from the table.
The issue is covered in this article:
https://blogs.msdn.microsoft.com/sqlprogrammability/2008/07/11/update-with-output-clause-triggers-and-sqlmoreresults/
If this is the case, you will not be able to add a trigger to the table. The corrections to the data will need to happen from an external source.

Oracle SQL Function using data from another schema says table or view does not exist

Im trying to create a function which takes in 3 parameters and returns one. The value of the returned parameter is obtained by querying two tables, however, one of the tables is on a different schema. The SQL im using is below:
create or replace FUNCTION tester
(
originaltext IN VARCHAR2, lang IN VARCHAR2, category IN VARCHAR2 DEFAULT NULL)
RETURN VARCHAR2 AS
translatedttextval VARCHAR2(255) := '';
BEGIN
--if category is null, disregard category
if category is null then
SELECT distinct nvl(trans.translatedtext, originaltext)
INTO translatedttext
FROM tbl_translations trans, SECDEVSCHEMA.tbl_instanceids ids
WHERE trans.translatedlang = ids.id_a
AND ids.name = lang
AND trans.originaltext = originaltext;
end if;
RETURN translatedttextval;
END;
I've removed a bit of the query here which searches with category because it does something similar and has the same issue.
So, when I run this and pass in the params, I get an error which reads:
Error(16,46): PL/SQL: ORA-00942: table or view does not exist
If I do the following query it works fine when not on the SECDEVSCHEMA and returns the reults from tbl_instanceids which is on the SECDEVSCHEMA schema:
SELECT * FROM SECDEVSCHEMA.tbl_instanceids ids WHERE ids.id_a = 1234555;
I don't have DBA access to the DB but if I need some select priviledge granted or something I can get it done. Not sure if this the case though as the above query works.
Small additional question also:
where it says in the query
nvl(trans.translatedtext, originaltext)
If i wanted to surround the original text value with brackets when no translatedtext value exists, how would I go about this?
I'm using SQL Developer by the way in case that's important.
Thanks
for your smaller question
nvl(trans.translatedtext, '<'|| originaltext ||'>' )
This was down to a priviledges issue.
In SQL Developer, I expanded the tables package and selected the table I needed access to in the Function, right clicked and selected Priviledges > Grant.
As this is just a Development DB I just assigned all the priviledges to the appropriate schema or User and its working now.
Still unsure about this part though:
Small additional question also: where it says in the query nvl(trans.translatedtext, originaltext)
If i wanted to surround the original text value with brackets when no translatedtext value exists, how would >I go about this?
Any takers for this?
Thanks

Can I block an insert inside a trigger on Oracle 11g?

So, what I want to do is to block an insert if the condition is met. In this case, I'm developing a simple library database system and I don't want to allow someone to be able to borrow a book if that certain person has already borrowed 2 books and still hasn't returned any of them.
I need to do this via trigger, is it possible? Also, I'd like to know if I can supply a query into the WHEN clause like I did.
CREATE OR REPLACE TRIGGER trigger_borrowing_limit
BEFORE INSERT ON Borrowing
REFERENCING NEW ROW AS NROW
FOR EACH ROW
WHEN SELECT COUNT(*) FROM Borrowing WHERE RETURN_DATE IS NULL AND NROW.ID = Borrowing.ID > 2
BEGIN
?
END
You can't supply a query for the WHEN clause. From the Oracle 11 docs (search for "WHEN (condition)" once you get to the page):
Restrictions on WHEN (condition)
If you specify this clause, then you must also specify FOR EACH ROW.
The condition cannot include a subquery or a PL/SQL expression (for example, an invocation of a user-defined function).
As for preventing the row insert, the comment from Mihai is the way to go. Your front end can catch the exception, and if it's error number -20101 (per Mihai's example) you'll know that the person already has two books out. Note that some drivers will report the absolute value of the exception number, so the error number that reaches you may be 20101.
Addendum: a followup question asked how to apply the "two books out" logic since it's not valid for the WHEN clause.
The answer is to drop the WHEN clause and put the logic into the trigger body. Note that I normally stick with the standard NEW for referencing the new row, so my answer doesn't have the REFERENCING NEW AS NROW:
CREATE OR REPLACE TRIGGER trigger_borrowing_limit
BEFORE INSERT ON Borrowing
DECLARE
booksOut NUMBER;
BEGIN
SELECT COUNT(*) INTO booksOut
FROM Borrowing
WHERE RETURN_DATE IS NULL AND NEW.ID = Borrowing.ID;
IF booksOut > 2 THEN
-- Next line courtesy of Mihai's comment under the question
raise_application_error(-20101, 'You already borrowed 2 books');
END IF;
END;

Stored PL/SQL function

I'm trying to implement a stored function in PL/SQL that finds the total number of cities visited by a given driver, return the driving license number.
I tried something like this, but i keep getting "Function created with compilation errors.". The thing is, oracle 11g doesn't show where is the error at?
Here is what i got.
create or replace function driverL return number
as licence L#%TYPE;
begin
select L#, count(T#)
from driver d
join trip t ON d.L# = t.L#
join leg l ON l.t# = l.t#;
return licence;
end;
/
How do i consider multiple visits to the same city as one visit?
If you are using SQL*Plus you can use the statement show errors after the create statement to show the compilation errors for the most recently created or altered stored procedure/function/package.
In your code I see at least two problems:
You are missing an INTO clause in your select
The column L# in your SELECT clause is ambiguously defined since two tables have a column with that name