SQL Server FOR/AFTER trigger - sql-server

I have an issue with an assignment. I believe I have the code all worked out properly but I keep getting a syntax error saying that the column "Total" Does not exist. I was under the impression that when you used "AS" it renamed the column that you are trying to use.
Here is my code any pointers would be greatly appreciated.
Use [IST278EagleCorp13-1]
Go
Alter Trigger DPInvOrderTrigger
On InventoryPart
For insert
As
If exists ( Select Count(ReorderLevel * 4) ReorderLevel,Count(StockOnOrder + StockLevel) AS Total
From InventoryPart
Where Total > ReorderLevel)
Begin
RaisError('Inventory to low. Transaction failed.',16,1)
rollback tran
End
These are the directions for this assignment.
/* Create a FOR|AFTER trigger named xxInvOrderTrigger for updates
to the InventoryPart table. If an update produces a record where
the (stockOnOrder + stockLevel) > (4 * reorderLevel) raise an
error (display an error) and rollback the change. */

To my understanding you are using SQL Server as your RDBMS and if I am not wrong in your below query Total is a column alias and you can't use column alias in where clause. That's why the error is for.
See a sample fiddle here for your understanding http://sqlfiddle.com/#!3/f902c/1
Select Count(ReorderLevel * 4) ReorderLevel,
Count(StockOnOrder + StockLevel) AS Total
Rather you can do it using derived table like below
select ReorderLevel,
Total
from
(
Select Count(ReorderLevel * 4) ReorderLevel,
Count(StockOnOrder + StockLevel) AS Total,
ReorderLevel
From InventoryPart
) tab
Where Total > ReorderLevel

Related

SP2-0552: Bind variable "NEW" not declared and END Error report - Unknown Command

I have to write a trigger for the tables I made and in insert update, I have to record a separate log table for those that are updated or inserted.
Columns in the log table will be like;
Done_process (will write update, insert)
Person (student number of the person treated)
Before (previous value for update, blank for insert)
After (new value for update, new value for insert)
This is my student_info table,
CREATE TABLE student_info (
school_id NUMBER,
id_no NUMBER NOT NULL UNIQUE,
name VARCHAR2(50) NOT NULL,
surname VARCHAR2(50) NOT NULL,
city VARCHAR2(50) NOT NULL,
birth_date DATE NOT NULL,
CONSTRAINT student_info_pk PRIMARY KEY(school_id )
);
CREATE TABLE og_log(
done_process VARCHAR2(30),
person VARCHAR2(30),
before VARCHAR2(30),
after VARCHAR2(30)
);
CREATE OR REPLACE TRIGGER og_trigger
BEFORE INSERT OR UPDATE OR DELETE ON student_info
REFERENCING OLD AS OLD NEW AS NEW
FOR EACH ROW
ENABLE
DECLARE
BEGIN
IF INSERTING THEN
INSERT INTO og_log(done_process, person, before, after)
VALUES ('Insert',:new.school_id,:old.name,:new.name);
ELSIF UPDATING THEN
INSERT INTO og_log(done_process, person, before, after)
VALUES ('Update',:new.school_id,:old.name,:new.name);
END IF;
END;
/
When I try to run the code it gave an error as follows;
> Trıgger OG_TRIGGER created.
>
>
> Error starting at line : 280 in command - ELSIF UPDATING THEN Error
> report - Unknown Command
>
> SP2-0552: Bind variable "NEW" not declared.
>
> 0 rows inserted.
>
>
> Error starting at line : 283 in command - END IF Error report -
> Unknown Command
>
> SP2-0044: For a list of known commands enter HELP and to leave enter
> EXIT.
>
> Error starting at line : 284 in command - END Error report - Unknown
> Command
I believe you are creating this trigger for learning purpose and not something a real use case because what you do in trigger doesn't really making any sense.
The trigger you have mentioned is not compiling due to syntactical problems like where v_id := 20201033.
Where clause is used to compare the value and thus you should use = instead := which is an assignment operator.
Besides this problem few points which still needs to be taken care
Give a explicit convention for creating local variables. e.g. you have created a local variable v_id and the same column is also available in student_info table. Though it is not a problem in this case but it's good practice to keep the local variable specific like let's say l_v_id.
You have used a select statement inside trigger which could leads to NO_DATA_FOUND error and you should handle it by either in the exception section or another way would be using aggregate function like max() if obviously v_id is primary key. I doubt why you need this select statement ( you could use between old and new using something like coalesce(:old.school_id,:new_schoold_id) if I understood you) but I would leave it open to you to decide and act accordingly.
Considering above points final code will be,
CREATE OR REPLACE TRIGGER og_trigger
BEFORE INSERT OR UPDATE OR DELETE ON student_info
REFERENCING OLD AS OLD NEW AS NEW
FOR EACH ROW
ENABLE
DECLARE
BEGIN
IF INSERTING THEN
INSERT INTO og_log(done_process, person, before, after)
VALUES ('Insert',:new.school_id,:old.city,:new.city);
ELSIF UPDATING THEN
INSERT INTO og_log(done_process, person, before, after)
VALUES ('Update',:new.school_id,:old.city,:new.city);
END IF;
END;
/
Find demo db<>fiddle
EDITED: Solving probably tool issue
I doubt the issue is with SQL Developer tool usage , however last try i would like to make,
Step1:
Drop both the tables used by issuing drop command
drop table STUDENT_INFO;
drop table og_log;
Step2:
Open another SQL worksheet using alt+F10 and do as I have shown in the following image. Please try and let me know.

Issues with SQL Server functions and triggers

I have been working on this project by using "advanced" features of SQL Server and creating functions, triggers, but two of these are giving me serious issues.
I am supposed to create a function that includes one input parameter and returns a table. The function itself will return a summary of the individual costs of each insurance expense (base, spouse, etc.), a total of those expenses and the employees name given the number of dependents a given employee has- the user enters the number of dependents as an input parameter.
CREATE FUNCTION fnInsCosts
(#NoDependants int)
--returns table datatype
RETURNS table
AS
--Set the Return to select the columns and aggregated columns from vwPayRoll as listed in exercise 3 of Module 12 --assignment sheet where Dependants is = to the input variable.
RETURN (SELECT
EmpName,
SUM(BaseCost) AS TotBaseCost, SUM(SpouseIns) AS TotSpouseCost,
SUM(DepIns) AS TotDepCost, SUM(DentalCost) AS TotDentalCost,
SUM(BaseCost * SpouseIns * DepIns * DentalCost) AS TotInsCost
FROM
vwPayroll
WHERE
Dependants = #NoDependants
GROUP BY
Dependants);
SELECT * FROM dbo.fnInsCosts(2);
SELECT * FROM dbo.fnInsCosts(0);-- Unfinished/error with select and EmpName?
Here is the error I get when I try to run the whole thing:
Msg 156, Level 15, State 1, Procedure fnInsCosts, Line 15
Incorrect syntax near the keyword 'SELECT'.
And it says this when I run everything except for the part where I invoke it:
Msg 8120, Level 16, State 1, Procedure fnInsCosts, Line 10
Column 'vwPayroll.EmpName' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
And here is the last one; I am creating a trigger and I need to create two table copies:
--Test for the existence of a fake table named TempEmpData. If it exists, drop it.
IF OBJECT ID('TempEmpData') IS NOT NULL
DROP TABLE TempEmpData;
--Test for the existence of a fake table named TempWork. If it exists, drop it.
IF OBJECT ID('TempWork') IS NOT NULL
DROP TABLE TempWork;
--Select everything from EmpData into the appropriate fake table
SELECT * INTO TempEmpData FROM EmpData
--Select everything from Work into the appropriate fake table
SELECT * INTO TempWork FROM Work
GO
CREATE TRIGGER TempEmpDate_INSERT_UPDATE_DELETE
ON TempEmpData
AFTER INSERT, UPDATE, DELETE
AS
--(USE THIS CONDITIONAL STRUCTURE- substitute variable names where needed and remove the "--")
IF EXISTS (SELECT * FROM Deleted JOIN TempEmpData ON Deleted.EmpID = TempEmpData.EmpID)
--the correct primary key)
BEGIN;
--Custom error message
THROW 11, 'EmpID is in use; transaction cancelled!', 1;
--rollback the transaction
ROLLBACK TRAN;
END;
ELSE
BEGIN
--Update the appropriate fake table
CREATE TRIGGER TempEmpData_INSERT_UPDATE
ON TempEmpData
AS
--Set the appropriate column to the correct value
--Where the correct primary key is in a subquery selecting that same key from the
--system table that handles inserts
UPDATE TempEmpData
SET BenPlanID = 0;
DELETE TempEmpData
WHERE EmpID = 41;
INSERT TempEmpData
VALUES ('Bill', 'Smith', '11/14/2014', 0, 0, 1, NULL, 2);
SELECT *
FROM TempEmpData
ORDER BY EmpID DESC
END;
And here are the errors:
Msg 4145, Level 15, State 1, Line 4
An expression of non-boolean type specified in a context where a condition is expected, near 'ID'.
Msg 4145, Level 15, State 1, Line 8
An expression of non-boolean type specified in a context where a condition is expected, near 'ID'.
Msg 156, Level 15, State 1, Procedure TempEmpDate_INSERT_UPDATE_DELETE, Line 17
Incorrect syntax near the keyword 'TRIGGER'.
I would be extremely grateful for any insight.
You are using the "new" inline syntax to create a function, great!
The first error comes from the missing "GO" to separate the creation of your function from the call to it
CREATE FUNCTION fnInsCosts()
--You function code here
GO
SELECT * FROM dbo.fnInsCosts(2);
The second error in your function has its reason - as stated by JamesZ already - in the wrong grouping column:
SELECT EmpName,
SUM(BaseCost) AS TotBaseCost,
SUM(SpouseIns) AS TotSpouseCost,
SUM(DepIns) AS TotDepCost,
SUM(DentalCost) AS TotDentalCost,
SUM(BaseCost * SpouseIns * DepIns * DentalCost) AS TotInsCost
FROM vwPayroll
WHERE Dependants = #NoDependants
GROUP BY EmpName
You see, that the only column which has not got any aggregation function is the EmpName...
And the last one - again stated by JamesZ already is the missing underscore in the function's name OBJECT ID:
IF OBJECT_ID('TempEmpData') IS NOT NULL
In your code T-SQL is searching for the meaning of "object" and for a function called "ID"... (The same with IF OBJECT ID('TempWork') IS NOT NULL)
And one last point: On SO it is highly advised to create one question per issue. This question should've been at least parted in two separate questions. Just think of future readers searching for similar problems...
EDIT Ups, there was even one more...
Your last error message points to some code where I do not understand what you really want to achieve... Are you creating a trigger from within the trigger? Might be, the MERGE statement was the better approach for your needs anyway...

"The column prefix '*' does not match with a table name or alias name used in the query." while using a temp table

In a SQL Server 2000 stored procedure, I'm trying to perform an update statement to a table variable. It is currently giving me the error: "The column prefix 'WST' does not match with a table name or alias name used in the query."
UPDATE WST
SET
WST.QtySold = (SELECT SUM(II.QtyShipped)
FROM #InvoiceItems II
WHERE II.InvoiceDate >= WST.StartDate
AND II.InvoiceDate <= WST.EndDate),
WST.TotalSales = (SELECT SUM(ISNULL(II.QtyShipped, 0) * ISNULL(II.UnitPrice, 0))
FROM #InvoiceItems II
WHERE II.InvoiceDate >= WST.StartDate
AND II.InvoiceDate <= WST.EndDate),
WST.TotalCost = (SELECT SUM(ISNULL(II.QtyShipped, 0) * ISNULL(II.UnitCost, 0))
FROM #InvoiceItems II
WHERE II.InvoiceDate >= WST.StartDate
AND II.InvoiceDate <= WST.EndDate)
FROM #WeeklySalesTrend WST
WHERE WST.WeekNo = 1
This error only appeared after I created the temp table #InvoiceItems and replaced the Inner Join of two tables with the temp table. Why would changing the two-table inner join out for a temp table cause this error and how do I fix/get around it?
I don't have a SQL 2000 box to test this on but I think your query syntax is correct. I suspect there may be invisible control characters that's messing up the parsing. This used to happen from time to time but I haven't seen the issue for a while. Try typing the query into a new query window from scratch.
The answer boils down to this, SQL Query Analyzer gave me the wrong location for the error. The error was actually because of an Insert statement 17 lines higher in the code where I was trying to use WST when I never aliased it up there. I don't know if this was a glitch/bug on Query Analyzer's side or if the stored procedure optimized to more lines and there its location was off. Either way, the problem has been fixed. Thanks for the suggestions and quick responses though and sorry for wasting your time.

Stored Procedure errors out only when calling from a trigger

I am using SQL Server to programming something that's triggered by a UPDATE even in a table.
So the structure is like: in the trigger, I have:
EXEC StoredProcedure1
And in StoredProcedure1 I have:
INSERT dbo.UnbalancedFreqStops
SELECT APremise AS PremiseID
,geography::ConvexHullAggregate(Location) AS Shape
FROM
(SELECT
A.[Premise ID] AS APremise, B.[Premise ID] AS BPremise,
B.LatLon AS Location, B.[Service Frequency] AS FreqT, B.Mon_Routed AS MonT,
B.Tue_Routed AS TueT, B.Wed_Routed AS WedT, B.Thu_Routed AS ThuT,
B.Fri_Routed AS FriT, B.Sat_Routed AS SatT, B.Sun_Routed AS SunT
FROM
dbo.Weekly AS A
INNER JOIN
dbo.Weekly AS B ON A.LatLon.STDistance(B.LatLon) <= (80 * 2)
) AS Clusters
GROUP BY
APremise
HAVING
MAX(FreqT) <> (MAX(MonT) + MAX(TueT) + MAX(WedT) + MAX(ThuT) + MAX(FriT) + MAX(SatT) + MAX(SunT));
It's interesting that it errors out with violation of primary key error (PremiseId being unique PK) ONLY WHEN I am calling the stored procedure from the trigger. And the stored procedure runs fine if I (1) run it alone, or (2) call it from another stored procedure.
Moreover, I ran the query alone, and found there is NO duplicate key AT ALL.
Can somebody enlighten me?
Thanks!

How can I copy records between tables only if they are valid according to check constraints in Oracle?

I don't know if that is possible, but I want to copy a bunch of records from a temp table to a normal table. The problem is that some records may violate check constraints so I want to insert everything that is possible and generate error logs somewhere else for the invalid records.
If I execute:
INSERT INTO normal_table
SELECT ... FROM temp_table
nothing would be inserted if any record violates any constraint. I could make a loop and manually insert one by one, but I think the performance would be lower.
Ps: if possible, I'd like a solution that works with Oracle 9
From Oracle 10gR2, you can use the log errors clause:
EXECUTE DBMS_ERRLOG.CREATE_ERROR_LOG('NORMAL_TABLE');
INSERT INTO normal_table
SELECT ... FROM temp_table
LOG ERRORS REJECT LIMIT UNLIMITED;
In its simplest form. You can then see what errors you got:
SELECT ora_err_mesg$
FROM err$_normal_table;
More on the CREATE_ERROR_LOG step here.
I think this approach works from 9i, but don't have an instance available to test on, so this is actually run on 11gR2
Update: tested and tweaked (to avoid PLS-00436) in 9i:
declare
type t_temp_table is table of temp_table%rowtype;
l_temp_table t_temp_table;
l_err_code err_table.err_code%type;
l_err_msg err_table.err_msg%type;
l_id err_table.id%type;
cursor c is select * from temp_table;
error_array exception;
pragma exception_init(error_array, -24381);
begin
open c;
loop
fetch c bulk collect into l_temp_table limit 100;
exit when l_temp_table.count = 0;
begin
forall i in 1..l_temp_table.count save exceptions
insert into normal_table
values l_temp_table(i);
exception
when error_array then
for j in 1..sql%bulk_exceptions.count loop
l_id := l_temp_table(sql%bulk_exceptions(j).error_index).id;
l_err_code := sql%bulk_exceptions(j).error_code;
l_err_msg := sqlerrm(-1 * sql%bulk_exceptions(j).error_code);
insert into err_table(id, err_code, err_msg)
values (l_id, l_err_code, l_err_msg);
end loop;
end;
end loop;
end;
/
With all your real columns instead of just id, which I've done just for demo purposes:
create table normal_table(id number primary key);
create table temp_table(id number);
create table err_table(id number, err_code number, err_msg varchar2(2000));
insert into temp_table values(42);
insert into temp_table values(42);
Then run the anonymous block above...
select * from normal_table;
ID
----------
42
column err_msg format a50
select * from err_table;
ID ERR_CODE ERR_MSG
---------- ---------- --------------------------------------------------
42 1 ORA-00001: unique constraint (.) violated
This is less satisfactory on a few levels - more coding, slower if you have a lot of exceptions (because of the individual inserts for those), doesn't show which constraint was violated (or any other error details), and won't retain the errors if you rollback - though you could call an autonomous transaction to log it if that was an issue, which I doubt here.
If you have a small enough volume of data to not want to worry about the limit clause you can simplify it a bit:
declare
type t_temp_table is table of temp_table%rowtype;
l_temp_table t_temp_table;
l_err_code err_table.err_code%type;
l_err_msg err_table.err_msg%type;
l_id err_table.id%type;
error_array exception;
pragma exception_init(error_array, -24381);
begin
select * bulk collect into l_temp_table from temp_table;
forall i in 1..l_temp_table.count save exceptions
insert into normal_table
values l_temp_table(i);
exception
when error_array then
for j in 1..sql%bulk_exceptions.count loop
l_id := l_temp_table(sql%bulk_exceptions(j).error_index).id;
l_err_code := sql%bulk_exceptions(j).error_code;
l_err_msg := sqlerrm(-1 * sql%bulk_exceptions(j).error_code);
insert into err_table(id, err_code, err_msg)
values (l_id, l_err_code, l_err_msg);
end loop;
end;
/
The 9i documentation doesn't seem to be online any more, but this is in a new-features document, and lots of people have written about it - it's been asked about here before too.
If you're specifically interested only in check constraints then one method to think about is to read the definitions of the target check constraints from the data dictionary and apply them as predicates to the query that extracts data from the source table using dynamic sql.
Given:
create table t1 (
col1 number check (col1 between 3 and 10))
You can:
select constraint_name,
search_condition
from user_constraints
where constraint_type = 'C' and
table_name = 'T1'
The result being:
"SYS_C00226681", "col1 between 3 and 10"
From there it's "a simple matter of coding", as they say, and the method will work on just about any version of Oracle. The most efficient method would probably be to use a multitable insert to direct rows to either the intended target table or to an error logging table based on the result of a CASE statement that applies the check constraint predicates.

Resources