Instead of trigger not firing - stored-procedures

CREATE OR REPLACE PROCEDURE insert_ol
(
p_ord_id order_line.order_id%type,
p_pid order_line.product_id%type,
p_qty order_line.quantity%type,
p_price order_line.price%type,
p_pname order_line.prod_name%type,
p_alias order_line.prod_alias%type)
IS
BEGIN
INSERT INTO order_line values (p_ord_id, p_pid, p_qty, p_price, p_pname,
p_alias);
END;
And I have a INSTEAD OF TRIGGER like this:
CREATE OR REPLACE TRIGGER insert_ol
INSTEAD OF INSERT ON ol_ins
FOR EACH ROW
BEGIN
insert_ol
(:new.order_id,:new.prod_code,
:new.qty,prod_cost,:new.prod_name,:new.palias);
end;
The view on which the trigger is based is:
CREATE OR REPLACE FORCE VIEW "OL_INS"
AS
SELECT ot.order_id, prd.prod_code, ot.qty, prd.prod_cost, prd.prod_name,
ot.palias
FROM ol_temp ot
JOIN product prd
ON ot.palias=prd.prod_alias;
When I insert data into the "OL_TEMP" table, the partial data gets joined with the "PRODUCT" table and gets into the view. But the trigger does not fire to call the procedure and insert data into the "ORDER_LINE" table.
What is the mistake I am doing and how to I correct it?

You seem to have things backwards, based on
When I insert data into the "OL_TEMP" table...
The instead of trigger fires when you insert into the OL_INS view, it will never fire if you manipulate the underlying table(s) directly. So you should be doing something like:
insert into ol_ins (order_id, prod_code, qty, prod_cost, prod_name, palias)
values ( ... )
The trigger will then fire, call the procedure, and insert data into the order_line table. Except it will error because your trigger refers to prod_cust (which isn't a known identifier) instead of :new.prod_cust.
From what you have described that isn't what you want at all though, and a trigger on the view doesn't make sense. You seem to want a simple insert, based on your temporary table and the permanent product table - it isn't even clear that you need the view at all:
INSERT INTO order_line (order_id, product_id, quantity, price, prod_name, prod_alias)
SELECT ot.order_id, prd.prod_code, ot.qty, prd.prod_cost, prd.prod_name, ot.palias
FROM ol_temp ot
JOIN product prd
ON ot.palias = prd.prod_alias;
If you really do need the view for something else and want to use it here you still could:
INSERT INTO order_line (order_id, product_id, quantity, price, prod_name, prod_alias)
SELECT order_id, prod_code, qty, prod_cost, prod_name, palias
FROM ol_ins;
but I wouldn't create a view just for that. And you could wrap either of those inserts in a procedure if you really wanted to, but you would still have to call the procedure explicitly - not from a trigger, unless you used an after-statement trigger on the temporary table.

Related

How to insert value (data) to a field reference by ID of the specific row (record) in the delphi clientdataset

I am trying to insert a data to a field referenced by specific ID of row (or record) in the clientdataset at runtime.
I am using delphi and here's the structure of my case — mysql database > mysqluniprovider > uniquery > dataset provider > clientdataset > datasource > dbgrid.
The data I am trying to insert is generated during runtime by another code in the same procedure. Hence, dbnavigator will not work for me here. On the otherhand, I prefer to do this at the clientdataset level and do not want to direct to sql.
I was able to find the reference id by using the clientdataset.lookup/locate/findkey. But I could not be able to direct the cursor to the cell of the same row of reference id and specific field to insert the data.
I believed there must be a code component for this type of case just like cds.lookup/locate/findkey to update data in an existing table at runtime.
I will greatly appreciate any help on this.
The fact that you are using a TDBGrid to display the data is incidental, you change the data in the clientdataset and the TDBGrid will automatically update its display of the record. What you neeed to do is to use methods of of the clientdataset to move to the record and update its field data. You can use the clientdataset's Locate method to move to the record you want, as in:
ID := 99; // the ID of the record to change
if ClientDataSet1.Locate('ID', ID, []) then begin
ClientDataSet1.Edit; // Put the CDS into dsEdit mode so you can change its field data
ClientDataSet1.FieldByName('SomeField').AsString := 'Whatever';
ClientDataSet1.Post; // save the change(s) to the record
end;
See the online help for TField for its various AsXXX methods, such as AsInteger, AsFloat, etc.

Stored procedure to insert new records in the child tables

I'm trying to create a stored procedure to insert new records in a number of child tables (using Greenplum) I have a master table and a set of child tables. I would like to insert new records that I have in the master table into the child tables (I have around 20 child tables). My assumption is that I should create a function, then a trigger.
Note that I only want to insert some fields into the child tables.
I made a few attempts, but here's the last one: (sorry in advance if it looks very bad. I never created any trigger functions)
FUNCTION:
CREATE OR REPLACE FUNCTION
schema1.newcustomerdata() RETURNS trigger AS $new_customer_data$
BEGIN
INSERT INTO schema1.customeridentifiers
(customer_id,
date_time)
SELECT NEW.customer_id,
date_time
FROM schema1.customersmaster
;
RETURN NEW;
END;
$new_customer_data$ LANGUAGE plpgsql;
TRIGGER:
CREATE TRIGGER newcustomerdata
AFTER INSERT ON schema1.customersmaster
FOR EACH ROW EXECUTE PROCEDURE newcustomerdata();
The function and trigger runs. However, I can't insert data in the master table anymore.
I get this error message:
function cannot execute on segment because it issues a non-select statement
So my questions are:
What would be the best solutions to update the child tables?
What's wrong with my function or trigger?
What are your recommendations?
Apparently "Triggers are not supported since they typically rely on the use of VOLATILE functions" Use RULES instead of triggers
CREATE RULE newcustomerdata AS ON INSERT TO schema1.customersmaster
DO also INSERT INTO schema1.customeridentifiers VALUES (NEW.customer_id, NEW.date_time);

Firebird deactivate triggers

I have clients-->|cascade rule|-->orders_table-->|cascade rule|-->order_details
in my order_details I have after delete trigger that increment the quantity in my product table
CREATE OR ALTER TRIGGER TABLEAU_DETAIL_VENTES_AD0 FOR TABLEAU_DETAIL_VENTES
ACTIVE AFTER DELETE POSITION 0
AS
declare variable qte numeric_15_2;
begin
select qte_article from tableau_articles where id_article = old.id_article
into :qte;
qte = :qte + old.qte;
update tableau_articles
set qte_article = :qte
where id_article = old.id_article;
end
If I delete a client than all orders depending on it will be deleted
and the orders_detail so on.
The problem is that order_details after delete trigger will be fired and incrementing the product quantity I don't want that to happen.
My question: is there any way to know if the trigger has been fired by cascade rule or sql delete statement that come from the application?
I want to achieve something like:
If trigger triggered by the cascade rule then disable_all_triggers. Thanks in advance for your help.
You can try to wrap your delete code in stored procedure with execute statement for in/activate the trigers
CREATE PROCEDURE DeleteClient(
ID INTEGER)
AS
begin
execute statement 'alter trigger TABLEAU_DETAIL_VENTES_AD0 inactive;';
/*
Your Delete statement here
*/
execute statement 'alter trigger TABLEAU_DETAIL_VENTES_AD0 active;';
END^
I end up using context variables in my clients table i add after delete trigger and set a flag using rdb$set_context
SET TERM ^ ;
CREATE OR ALTER TRIGGER TABLEAU_CLIENTS_AD0 FOR TABLEAU_CLIENTS
ACTIVE AFTER DELETE POSITION 0
AS
declare variable id integer;
begin
execute statement 'select rdb$set_context(''USER_SESSION'', ''myvar'', 100) from rdb$database' into :id;
end
^
SET TERM ; ^
in the detail orders i check my flag with rdb$get_context and skip the trigger if the flag exist with the value associated
select rdb$get_context('USER_SESSION', 'myvar') from rdb$database into :i;
if (i = 100) then exit;
You can't determine that, but you can determine if your foreign key is still valid. Since Firebird cascaded deletes are sequential (rows that are referenced in a foreign keys are deleted first), you can check if your old.id_article is still valid before updating the record.
I'm not sure you would achieve what you want like that. What if you just delete an order and its items. Wouldn't you want to increment quantities in that case?
Anyway... I wouldn't deactivate triggers from within triggers. That is bad design.
Use some sort of variable... update a flag in a support table. From within the client delete trigger you can set this variable. Then in the order_items delete trigger you can check it to see if you need to update quantities.
Another better option is to analyze the situation better and determine why and when you actually want to update quantities. If you are deleting an old order which has already been fulfilled and delivered, you probably wouldn't want to. If you are canceling a new order, you probably would. So maybe updating the quantities depends actually more on the state of the order (or some other variable) then simply on the fact that you are deleting an order_items row.
Ok, so you say orders cannot be deleted, except when deleting the client. Then maybe you should flag the client or its order with a flag that states the client is being deleted. In the order_items delete trigger you update article quantities only if the client is not being deleted.

stored procedure which return fields unless it's used by code via datatableadapter

I have got an issue to retreive stored procedure result ( Table ) into a data table,
when i try to run my procedure from sql server , it's working, same as testing it from a dataset ( I have used a datatableadapter which invoque my stored procedure.
but when i try to get my stored procedure via code, it return an empty datatable.
here is what my query look like (it contain a lot of field so i tried to resume all,
CREATE TABLE #TemporaryTable(Code_Suivi smallint IDENTITY(1,1), DATECREATION date, NOMPROJET varchar(20), ProducedHours decimal(10,1), Accumulation)
insert into #TemporaryTable ( DATECREATION , NOMPROJET , ProducedHours )
SELECT query -- it will insert into #TemporaryTable what I have selected
to avoid getting an empty I have to not work with insert into... which is impossible for me, have any one encountered this befoure??
I have used another way to get it work, by avoiding datatable adapter, and using sqldatasource to invoque a select query

Informix 4GL and triggers

I want a simple SQL (trigger) for duplicate checking.
My table name is test1 with 2 columns, code and sname. Before inserting a new record, check if the record already exists: if it does, generate an error and do not insert; if it does not, let the insert proceed.
How do I do that?
The simplest, most reliable way to ensure that there is no duplicate data in the table is not using triggers at all, but using UNIQUE or PRIMARY KEY constraints:
CREATE TABLE test1
(
code INTEGER NOT NULL PRIMARY KEY,
sname VARCHAR(32) NOT NULL UNIQUE
);
The four constraints (two NOT NULL, one PRIMARY KEY, one UNIQUE) automatically ensure that no duplicate records are inserted into the table.
If you choose to add a trigger, it will be duplicating the work that is done by these constraints.
As to how to do it, you will need to create a stored procedure which is invoked from the trigger statement. It will be given the new code and new name, and will do a SELECT to see whether any matching record occurs, and will raise an exception if it does and will not raise an exception if not.
CREATE PROCEDURE trig_insert_test1(ncode INTEGER, nname VARCHAR(32))
DEFINE ocode INTEGER;
FOREACH SELECT code INTO ocode
FROM test1
WHERE code = ncode OR sname = nname
RAISE EXCEPTION -271, -100, "Value to be inserted already exists in table test1";
END FOREACH;
END PROCEDURE
Then you use:
CREATE TRIGGER ins_test1 INSERT ON test1
REFERENCING NEW AS NEW
FOR EACH ROW (EXECUTE PROCEDURE ins_trig_test1(new.code, new.sname))
In Informix 4GL, you can either create strings containing these statements, and then PREPARE and EXECUTE (and FREE) them, or you can use SQL blocks:
SQL
CREATE TRIGGER ins_test1 INSERT ON test1
REFERENCING NEW AS NEW
FOR EACH ROW (EXECUTE PROCEDURE ins_trig_test1(new.code, new.sname))
END SQL
But, as I said at the outset, using triggers for this is not the best way to go; it is redundant given the table definition.
I've not run any of the SQL or SPL past the server; you'll need to check that the semi-colons are in the right places in the SPL, as SPL is fussy about that.
You can find the syntax for the SQL and SPL statements in the Informix 11.70 Information Centre.

Resources