Oracle trigger implementation - database

I have to implement a trigger which would:
7) Show the DDL for how a trigger can be used to transfer all rental copies from a store being deleted from the Store information table to the central store
8) Show how this trigger can be extended to make
sure that the central store is never deleted from the database
So far I have done this:
CREATE OR REPLACE TRIGGER stores BEFORE DELETE ON stores FOR
EACH ROW BEGIN IF DELETING WHERE cvr = 123456789 THEN
Raise_Application_Error ( num => -20050, msg => 'You can
not delete Main Store.'); END IF; IF DELETING THEN
UPDATE store_id=123456789 ON movies WHERE isActive = 0 END
IF; END;
so main store is with cvr which is written, but it gives me a compilation error. Any help? Thanks in advance.

You have two errors in your code.
there is no "DELETING WHERE" expression, you have to use two boolean exceptions like this:
IF DELETING AND :old.cvr = 123456789 THEN...
:old.cvr refers to value of cvr column in deleted record
Syntax of UPDATE clause is
UPDATE table_name
SET column_name1 = value1,
column_name1 = value2
WHERE where_clause;
in your case probably somethink like this:
UPDATE movies
set store_id = 123456789
WHERE store_id = :old.cvr
I guess, I don't know required functionality

Related

Set values on a new table from a historical table, before adding new table back into historical table

I would like to create a historical view of alerts in an application. To do this, I am grabbing all events and timestamping them, then uploading them into a MS SQL table. I would also like to be able to exempt certain objects from the total count by flagging either the finding (to exclude the finding across all systems) or the object in the finding (to exclude the object from all findings).
The idea is, I will have all previous alerts in the main table, then I will set an 'exemptobject' or 'exemptfinding' bit column in the row. When I re-run the script weekly, I will upload the results directly into a temporary table and then I would like to compare either the object or the finding for each object in the temporary table to the main database's 'object' or 'finding' and set the respective 'exemptobject' or 'exemptfinding' bit. Once all the temporary table's objects have any exemption bits set, insert the temporary table into the main table and drop the temporary table to keep a historical record.
This will give me duplicate findings and objects, so I am having difficulty with the merge command:
BEGIN TRANSACTION
MERGE INTO [dbo].[temp_table]
USING [dbo].[historical]
ON [dbo].[temp_table].[object] = [dbo].[historical].[object] OR
[dbo].[temp_table].[finding] = [dbo].[historical].[finding]
WHEN MATCHED THEN
UPDATE
SET [exemptfinding] = [dbo].[historical].[exemptfinding]
,[exemptobject] = [dbo].[historical].[exemptobject]
,[exemptdate] = [dbo].[historical].[exemptdate]
,[comments] = [dbo].[historical].[comments];
COMMIT
This seems to do what I want, but I see that the results are going to grow exponentially and I think it won't be sustainable for long.
BEGIN TRANSACTION
UPDATE [dbo].[temp]
SET [dbo].[temp].[exemptfinding] = [historical].[exemptfinding]
,[dbo].[temp].[exemptobject] = [historical].[exemptobject]
,[dbo].[temp].[exemptdate] = [historical].[exemptdate]
,[dbo].[temp].[comments] = [historical].[comments]
FROM [dbo].[temp] temp
INNER JOIN [dbo].[historical] historical
ON (
[temp].[finding] = [sci].[finding] OR
[temp].[object] = [sci].[object] OR
) AND
(
[historical].[exemptfinding] = 1 OR
[historical].[exemptobject] = 1
)
COMMIT
I feel like I need to normalize the database, but I can't think of a way to separate things out and be able to:
See a count of each finding based on date the script was run
Be able to drill down into each day and see all the findings, objects and recommendations for each
Control the count shown for each finding by removing 'exempted' findings OR objects.
I feel like there's something obvious I'm missing or I'm thinking about this incorrectly. Any help would be greatly appreciated!
EDIT - The following seems to do what I want, but as soon as I add an additional WHERE condition to the final result, the query time goes from 7 seconds to 90 seconds, so I fear it will not scale.
BEGIN TRANSACTION
UPDATE [dbo].[temp]
SET [dbo].[temp].[exemptrecommendation] = [historical].[exemptrecommendation]
,[dbo].[temp].[exemptfinding] = [historical].[exemptfinding]
,[dbo].[temp].[exemptobject] = [historical].[exemptobject]
,[dbo].[temp].[exemptdate] = [historical].[exemptdate]
,[dbo].[temp].[comments] = [historical].[comments]
FROM (
SELECT *
FROM historical h
WHERE EXISTS (
SELECT id
,recommendation
FROM temp t
WHERE (
t.id = s.id OR
t.recommendation = s.recommendation
)
)
) historical
WHERE [dbo].[temp].[recommendation] = [historical].[recommendation] OR
[dbo].[temp].[id] = [historical].[id]
COMMIT

Error update trigger after new row has inserted into same table

I want to update OrigOrderNbr and OrigOrderType (QT type) because when I create first both of column are Null value. But after S2 was created (QT converted to S2) the OrigOrderType and OrigOrderNbr (S2) take from QT reference. Instead of that, I want to update it to QT also.
http://i.stack.imgur.com/6ipFa.png
http://i.stack.imgur.com/E6qzT.png
CREATE TRIGGER tgg_SOOrder
ON dbo.SOOrder
FOR INSERT
AS
DECLARE #tOrigOrderType char(2),
#tOrigOrderNbr nvarchar(15)
SELECT #tOrigOrderType = i.OrderType,
#tOrigOrderNbr = i.OrderNbr
FROM inserted i
UPDATE dbo.SOOrder
SET OrigOrderType = #tOrigOrderType,
OrigOrderNbr = #tOrigOrderNbr
FROM inserted i
WHERE dbo.SOOrder.CompanyID='2'
and dbo.SOOrder.OrderType=i.OrigOrderType
and dbo.SOOrder.OrderNbr=i.OrigOrderNbr
GO
After I run that trigger, it showed the message 'Error #91: Another process has updated 'SOOrder' record. Your changes will be lost.'.
Per long string of comments, including some excellent suggestions in regards to proper trigger writing techniques by #marc_s and #Damien_The_Unbeliever, as well as my better understanding of your issue at this point, here's the re-worked trigger:
CREATE TRIGGER tgg_SOOrder
ON dbo.SOOrder
FOR INSERT
AS
--Update QT record with S2 record's order info
UPDATE SOOrder
SET OrigOrderType = 'S2'
, OrigOrderNbr = i.OrderNbr
FROM SOOrder dest
JOIN inserted i
ON dest.OrderNbr = i.OrigOrderNbr
WHERE dest.OrderType = 'QT'
AND i.OrderType = 'S2'
AND dest.CompanyID = 2 --Business logic constraint
AND dest.OrigOrderNbr IS NULL
AND dest.OrigOrderType IS NULL
Basically, the idea is to update any record of type "QT" once a matching record of type "S2" is created. Matching here means that OrigOrderNbr of S2 record is the same as OrderNbr of QT record. I kept your business logic constraint in regards to CompanyID being set to 2. Additionally, we only care to modify QT records that have OrigOrderNbr and OrigOrderType set to NULL.
This trigger does not rely on a single-row insert; it will work regardless of the number of rows inserted - which is far less likely to break down the line.

Optionally saving selectively data in one-go only when you want it

I have a table where data does not initially exist until an action is taken that stores all settings made by client in one-go. To illustrate this simply, a button click that stores all column values off a (HTML) table into a database table (let's call this dbo.Settings).
So instead of inserting into this dbo.Settings all the default values prior to user making any changes to their individual settings (ever), I kind of created the pseudo data for them that will be returned whenever requested, kind of like SELECT-ing the default values:
SELECT
CanView = ISNULL(CanView, 1),
CanRead = ISNULL(CanRead, 1),
CanWrite = ISNULL(CanWrite, 0)
FROM
dbo.Settings AS s
WHERE
UserId = #id
Rather than doing:
IF NOT EXISTS(SELECT * FROM dbo.Settings WHERE UserId = #id)
BEGIN
INSERT INTO dbo.Settings (UserId, CanView, CanRead, CanWrite)
VALUES (#id, 1, 1, 0)
END
The problem with this is whenever I need to add a new setting column in the future, I now have to note one more procedure to modify/add the default value for this column as well -- which I don't like. Using TRIGGER would be an option but I wonder what the best practice in managing data like this would be. Or would you do something like this:
CREATE PROC Settings_CreateOrModify
#userId INT,
#canView BIT = NULL,
#canRead BIT = NULL,
#canWrite BIT = NULL
AS
BEGIN
IF EXISTS(SELECT * FROM dbo.Settings WHERE UserId = #userId) BEGIN
UPDATE s
SET
CanView = #canView,
CanRead = #canRead,
CanWrite = #canWrite
FROM
dbo.Settings AS s
WHERE
s.UserId = #userId AND
(#canView IS NULL OR #canView <> s.CanView) AND
(#canRead IS NULL OR #canRead <> s.CanRead) AND
(#canWrite IS NULL OR #canWrite <> s.CanWrite)
END
ELSE BEGIN
INSERT INTO
dbo.Settings(UserId, CanView, CanRead, CanWrite)
SELECT
#userId, #canView, #canRead, #canWrite
END
END
How would you handle data structure like this? Any recommendation or correction would be greatly appreciated. Thanks in advance!
Your SP is a good way to go, and doing it like this is commonly called an "UPSERT".
It also looks to me as if the block:
(#canView IS NULL OR #canView <> s.CanView) AND
(#canRead IS NULL OR #canRead <> s.CanRead) AND
(#canWrite IS NULL OR #canWrite <> s.CanWrite)
is problematic since it causes the UPDATE to run only if ALL parameters changed their value. I don't think that's what you wanted to say. Just SET the three values regardless of what's already there.
You still end up with three places to change when you add a new setting: The Table, the Upsert and the Defaults.
One very different approach is this:
Apply the defaults to the columns in the table definition.
Whenever you need the values for a new user, do: INSERT INTO dbo.Settings(UserId) The defaults will fill the rest of the columns.
Now you can retrieve the values for ALL users (new or not) in the same way from the table.
Since you already inserted the user in step 2, you know the userid is there already and you can always save the changes via a simple update.
This eliminates the SP and the need of providing the defaults in one extra place.

Add Multiple IDs in a Transaction to a LinkSet in Orientdb

I'm trying to add IDs from a inserted column to another inserted column in a Transaction. I tried like this:
begin
let doorOne = INSERT INTO doors SET color = green
let doorTwo = INSERT INTO doors SET color = blue
let car = INSERT INTO Cars SET doors = [$doorOne , $doorTwo]
commit retry 100
return $car
I get:
Unhandled rejection OrientDB.RequestError: The field 'Cars.doors' has been declared as LINKSET but the value is not a record or a record-id
I also tried to update it afterwards in the Transaction, but this won't work either (i think because the car is not created yet, so nothing to update) and i dont want to do it in two different calls, if there is a way to do it in one Transaction.
This is because INSERT returns, by default, the number of inserted entries. Try adding this clause at the end of each INSERT statement: RETURN #rid

Avoid Adding Duplicate Records

I m trying to write if statement to give error message if user try to add existing ID number.When i try to enter existing id i get error .untill here it s ok but when i type another id no and fill the fields(name,adress etc) it doesnt go to database.
METHOD add_employee.
DATA: IT_EMP TYPE TABLE OF ZEMPLOYEE_20.
DATA:WA_EMP TYPE ZEMPLOYEE_20.
Data: l_count type i value '2'.
SELECT * FROM ZEMPLOYEE_20 INTO TABLE IT_EMP.
LOOP AT IT_EMP INTO WA_EMP.
IF wa_emp-EMPLOYEE_ID eq pa_id.
l_count = l_count * '0'.
else.
l_count = l_count * '1'.
endif.
endloop.
If l_count eq '2'.
WA_EMP-EMPLOYEE_ID = C_ID.
WA_EMP-EMPLOYEE_NAME = C_NAME.
WA_EMP-EMPLOYEE_ADDRESS = C_ADD.
WA_EMP-EMPLOYEE_SALARY = C_SAL.
WA_EMP-EMPLOYEE_TYPE = C_TYPE.
APPEND wa_emp TO it_emp.
INSERT ZEMPLOYEE_20 FROM TABLE it_emp.
CALL FUNCTION 'POPUP_TO_DISPLAY_TEXT'
EXPORTING
TITEL = 'INFO'
TEXTLINE1 = 'Record Added Successfully.'.
elseif l_count eq '0'.
CALL FUNCTION 'POPUP_TO_DISPLAY_TEXT'
EXPORTING
TITEL = 'INFO'
TEXTLINE1 = 'Selected ID already in database.Please type another ID no.'.
ENDIF.
ENDMETHOD.
I'm not sure I'm getting your explanation. Why are you trying to re-insert all the existing entries back into the table? You're just trying to insert C_ID etc if it doesn't exist yet? Why do you need all the existing entries for that?
If so, throw out that select and the loop completely, you don't need it. You have a few options...
Just read the table with your single entry
SELECT SINGLE * FROM ztable INTO wa WITH KEY ID = C_ID etc.
IF SY-SUBRC = 0.
"this entry exists. popup!
ENDIF.
Use a modify statement
This will overwrite duplicate entries with new data (so non key fields may change this way), it wont fail. No need for a popup.
MODIFY ztable FROM wa.
Catch the SQL exceptions instead of making it dump
If the update fails because of an exception, you can always catch it and deal with exceptional situations.
TRY .
INSERT ztable FROM wa.
CATCH sapsql_array_insert_duprec.
"do your popup, the update failed because of duplicate records
ENDTRY.
I think there's a bug when appending in internal table 'IT_EMP' and inserting in 'ZEMPLOYEE_20' table.
Suppose you append the first time and then you insert. But when you append the second time you will have 2 records in 'IT_EMP' that are going to be inserted in 'ZEMPLOYEE_20'. That is because you don't refresh or clear the internal table and there you will have a runtime error.
According to SAP documentation on 'Inserting Lines into Tables ':
Inserting Several Lines
To insert several lines into a database table, use the following:
INSERT FROM TABLE [ACCEPTING DUPLICATE KEYS] . This
writes all lines of the internal table to the database table in
one single operation. The same rules apply to the line type of
as to the work area described above. If the system is able to
insert all of the lines from the internal table, SY-SUBRC is set to 0.
If one or more lines cannot be inserted because the database already
contains a line with the same primary key, a runtime error occurs.
Maybe the right direction here is trying to insert the work area directly but before you must check if record already exists using the primary key.
Check the SAP documentation on this issue clicking the link before.
On the other hand, once l_count is zero because of l_count = l_count * '0'. that value will never change to any other number making that you won't append or insert again.
why are you retrieving all entries from zemployee_20 ?
You can directly check wether the 'id' exists already or not by using select single. If exists, then show message, if not, add.
It is recommended to retrieve only one field when its needed and not the entire table with asterisc *
SELECT single employee_id FROM ZEMPLOYEE_20 where employee_id = p_id INTO v_id. ( or field in structure )
if sy-subrc = 0. "exists
"show message
else. "not existing id
"populate structure and then add record to Z table
endif.
Furthermore, l_count is not only unnecessary but also bad implemented.
You can directly use the insert query,if the sy-subrc is unsuccessful raise the error message.
WA_EMP-EMPLOYEE_ID = C_ID.
WA_EMP-EMPLOYEE_NAME = C_NAME.
WA_EMP-EMPLOYEE_ADDRESS = C_ADD.
WA_EMP-EMPLOYEE_SALARY = C_SAL.
WA_EMP-EMPLOYEE_TYPE = C_TYPE.
INSERT ZEMPLOYEE_20 FROM WA_EMP.
If sy-subrc <> 0.
Raise the Exception.
Endif.

Resources