I want to run a trigger each time a record is inserted or updated on table knowledgebase_messages which will vectorize the message column, using the joined pg_language_dictionary (e.g.: "French") for that message's locale_id.
Currently, the trigger working on row update, but not initial insert. Meaning, if I insert a row, then run a query to get records, updated the row in any way, or refresh the postgres UI, I see the vectorized value correctly there.
Trigger function:
runMessageVectorTriggerFn = 'CREATE OR REPLACE FUNCTION run_message_vector() ' +
'RETURNS TRIGGER ' +
'AS $BODY$ ' +
'BEGIN ' +
'IF pg_trigger_depth() <> 1 THEN ' +
'RETURN NEW; ' +
'END IF; ' +
'UPDATE knowledgebase_messages kbm ' +
'SET vector = to_tsvector(lang.pg_dictionary_name::regconfig, kbm.message) ' +
'FROM locales as loca ' +
'JOIN languages as lang ON loca.language_id = lang.id ' +
'WHERE kbm.locale_id = loca.id;' +
'RETURN NEW; ' +
'END; ' +
'$BODY$ LANGUAGE plpgsql';
Trigger:
runMessageVectorTrigger = 'CREATE TRIGGER run_message_vector AFTER INSERT OR UPDATE ON knowledgebase_messages ' +
'FOR EACH ROW EXECUTE PROCEDURE run_message_vector()';
When doing this in the BEFORE INSERT trigger, it doesn't update
the row being inserted because the row doesn't exist yet:
UPDATE knowledgebase_messages kbm...
Besides, it's not clear what is the intention in your UPDATE because
the WHERE clause seems to ignore the NEW row, as if it was implicit
that it was the target.
Anyway, instead of that, to change the row being inserted/updated, do
something along the lines of:
-- the SELECT must return 1 row, or in the worst case no row
-- otherwise an error will occur.
NEW.vector = (
SELECT
to_tsvector(lang.pg_dictionary_name::regconfig, NEW.message)
FROM locales as loca JOIN languages as lang
ON loca.language_id = lang.id
WHERE NEW.locale_id = loca.id
);
RETURN NEW;
both in BEFORE UPDATE and BEFORE INSERT cases.
Then you can remove this IF pg_trigger_depth() <> 1...
Related
I have created a function to update a column in a postgresSQL table using Sequence nextval() function.Function body is as follows
BEGIN
EXECUTE 'CREATE SEQUENCE '|| sequence_name || ' START 1';
EXECUTE 'UPDATE ' ||selected_table_name|| ' SET record_id = '||nextval(sequence_name);
RETURN 'SUCCESS';
END;
But when I call function as follows
SELECT staging.update_record_id('staging.test_table','staging.sq_test_table');
Its update my relevant column with 1 for all the records.But when I just use the following command in the console directly it update the all the values with increments.The console code as follows.
update staging.test_table set record_id = nextval('staging.sq_test_table');
Is anyone can give a solution for this, would be much grateful
I found a solution for the question.The function body should change as follows
BEGIN
EXECUTE 'CREATE SEQUENCE '|| sequence_name || ' START 1';
EXECUTE 'UPDATE ' ||selected_table_name|| ' SET record_id = nextval('''||sequence_name||''')';
RETURN 'SUCCESS';
END;
My problem is as follow: I insert or update a row in a postgresql database and need to modify one field in this row. BUT I need to know the new serial PK when I insert a new row to make a SELECT with JOIN on other tables.
I'm now stucked because I've done a AFTER INSERT AND UPDATE trigger to get the new PK (kkw_block_id). I get the value I need with the SELECT but after that I can't modify the value in the row: modifying the NEW.value is not possible with AFTER INSERT AND UPDATE and if I do an UPDATE on the row, I enter in an infinite loop, the trigger beeing called in the trigger...
CREATE TRIGGER tsvectorupdate
AFTER INSERT OR UPDATE
ON kkw_block
FOR EACH ROW
EXECUTE PROCEDURE kkw_search_trigger();
CREATE OR REPLACE FUNCTION kkw_search_trigger()
RETURNS trigger AS
$BODY$
DECLARE vector_en TEXT;
DECLARE vector_fr TEXT;
DECLARE vector_de TEXT;
BEGIN
-- I need the new serial PK(kkw_id) in the following section.
SELECT coalesce(modell_en, '') || ', ' || coalesce(bezeichnung_en,'') || ', ' || coalesce(kkw.kkw_name_en,'') || ', ' || coalesce(kkw_typ.typ_abr,'') || ', ' || coalesce(kkw_typ.typ_desc_en,'') || ', ' || coalesce(kkw_typ.typ_desc_short_en,'') INTO vector_en
FROM kkw_block
LEFT JOIN kkw ON NEW.kkw_id = kkw.kkw_id
LEFT JOIN kkw_typ ON NEW.kkw_typ_id = kkw_typ.kkw_typ_id
WHERE kkw_block_id = NEW.kkw_block_id;
-- I need to update a field of the newly created or updated row.
NEW.search_vector_en := to_tsvector('english', 'new test vector'); --- This doesn't work with 'AFTER UPDATE' trigger.
RETURN NULL;
END
$BODY$
Any idea?
Drop default for your PK and assign it in your BEFORE trigger. You will have to change that existing trigger from AFTER to BEFORE.
You can assign PK from sequence like that:
NEW.kkw_block_id = nextval('your_sequence_name_here');
Since you are using the same function for both INSERT and DELETE, you need to check if it is INSERT and only then use sequence. I have also included check if PK is null or not. I suppose that alone would be enough to not overwrite it during update.
IF (TG_OP = 'INSERT') AND NEW.kkw_block_id IS NULL THEN
NEW.kkw_block_id = nextval('your_sequence_name_here');
END IF;
This will be fine as long as this trigger will work for each new row, with seems to be the case. This will let you modify NEW and it will be reflected in data saved in table.
I end up with the following solution. I made a BEFORE trigger. The problem was the LEFT JOIN with reference to the table where the new row doesn't exist yet. It's not ideal but here it is:
CREATE TRIGGER tsvectorupdate
BEFORE INSERT OR UPDATE
ON kkw_block
FOR EACH ROW
EXECUTE PROCEDURE kkw_search_trigger();
CREATE TYPE kkw_type_record_type AS (typ_abr TEXT, typ_desc_en TEXT, typ_desc_short_en TEXT);
CREATE TYPE kkw_record_type AS (kkw_name_en TEXT);
CREATE OR REPLACE FUNCTION kkw_search_trigger()
RETURNS trigger AS
$BODY$
DECLARE kkw_rec kkw_record_type;
DECLARE kkw_typ_rec kkw_type_record_type;
DECLARE vector_en TEXT;
BEGIN
--- make a individual select instead of LEFT JOIN
SELECT kkw_name_en INTO kkw_rec.kkw_name_en
FROM kkw
WHERE kkw.kkw_id = NEW.kkw_id;
--- make a individual select instead of LEFT JOIN
SELECT typ_abr, typ_desc_en, typ_desc_short_en INTO kkw_typ_rec.typ_abr, kkw_typ_rec.typ_desc_en, kkw_typ_rec.typ_desc_short_en
FROM kkw_typ
WHERE kkw_typ.kkw_typ_id = NEW.kkw_typ_id;
vector_en := coalesce(NEW.modell_en, '') || ', ' || coalesce(NEW.bezeichnung_en,'') || ', ' || coalesce(kkw_rec.kkw_name_en,'') || ', ' || coalesce(kkw_typ_rec.typ_abr,'') || ', ' || coalesce(kkw_typ_rec.typ_desc_en,'') || ', ' || coalesce(kkw_typ_rec.typ_desc_short_en,'');
NEW.search_vector_en := to_tsvector('english', vector_en);
RETURN NEW;
END
$BODY$
I want to rewrite a functioning, but slow stored procedure using CTE (common table Expression)
I have a big stored procedure where i build the nececary SQL dynamicaly based on parameter used. Currently there are 27 parameters, i compose the SQL string that i execute at the end it looks like:
DECLARE #SqlWhereClause NVARCHAR(MAX)
SET #SqlWhereClause = ' WHERE ([InTimeStamp] BETWEEN ''' + CONVERT(VARCHAR(19), #fromDate, 120) + ''' AND ''' + CONVERT(VARCHAR(19), #toDate, 120) + ''')'
IF #showOnlyErrors = '1'
BEGIN
SET #SqlWhereClause += ' AND Status = ''Error'''
END
IF LEN(LTRIM(RTRIM(#docNo))) > 0
BEGIN
IF #matchExact = '1'
BEGIN
SET #SqlWhereClause += ' AND DocumentNumber = ''' + #docNo + ''''
END
ELSE
BEGIN
SET #SqlWhereClause += ' AND (contains([DocumentNumber],'''+ #docNo +'''))'
END
END
At the end, i add the pagination and transform it to the final formSQL:
IF CONVERT(int, LTRIM(RTRIM(#takeRows))) > 0
BEGIN
SET #SqlOrderByClause += ' OFFSET ' + #rowNumberToSkip +' ROWS FETCH NEXT '+ #takeRows +' ROWS ONLY '
Set #RowCount = ' Select #totalRecords = count(1) from dbo.Messages WITH (NOLOCK) ' + #SqlWhereClause
END
SET #SQL = #SqlSelect + #SqlFrom + #SqlWhereClause + #SqlOrderByClause + ' ; ' + #RowCount
PRINT #SQL
EXECUTE sp_executesql #SQL, #params, #totalRecords OUTPUT
Everithing is working like a charm. No problems. Only performance problems. To solve one of it, i would try to use CTE (common table extpression)
But this is not working:
With DataSQL AS
(#SqlSelect + #SqlFrom + #SqlWhereClause + #SqlOrderByClause),
- incorrect syntax near #SqlSelect - Expecting '(' or Select.
I also tried this one:
WITH DataSQL AS
( Select #SqlSelect From #SqlFromFast
Where #SqlWhereClause Order By #SqlOrderByClause),
here i get:
An expression of non-boolean type specified in a context where a condition is expected, near 'Order'
Any idea? Or is it not possible to use CTE with multiple variables? All i found until now are simply queries with one, maybe two variables.
You could try:
WITH DataSQL AS
(
SELECT #SqlSelect SqlSelect
, #SqlFromFast SqlFromFast
, etcetera
)
Make sure you give aliases, or it won't work. I doubt it will help your performance issues though. You could try to use a temp table, that's usually quicker. you could also try to use inner variables:
DECLARE #Select VARCHAR(MAX) = #SQLSelect
And use those instead, that may help the optimizer, though that depends on your data.
My issue is when i call the stored procedure it is not working
drop procedure DELETE_WITH_COMMIT_COUNT1
DB20000I The SQL command completed successfully.
CREATE PROCEDURE DELETE_WITH_COMMIT_COUNT1(IN v_TABLE_NAME VARCHAR(24), IN v_COMMIT_COUNT INTEGER )
NOT DETERMINISTIC
LANGUAGE SQL
P1 : BEGIN
-- DECLARE Statements
DECLARE SQLCODE INTEGER;
DECLARE v_DELETE_QUERY VARCHAR(1024);
DECLARE v_DELETE_STATEMENT STATEMENT;
P2 : BEGIN
DECLARE V1 CHAR(24) FOR BIT DATA;
DECLARE V2 CHAR(24) FOR BIT DATA ;
DECLARE cur1 CURSOR WITH RETURN TO CLIENT FOR select min(MESSAGE_ID),max(MESSAGE_ID) from TESTING where TIMESTAMP between (select TIMESTAMP(date(min(timestamp))) from TESTING with ur) and (select TIMESTAMP(date(min(timestamp))) + 1 day from TESTING with ur) ;
OPEN cur1;
FETCH FROM cur1 INTO V1, V2;
SET v_DELETE_QUERY = 'DELETE FROM (SELECT 1 FROM ' || v_TABLE_NAME || ' WHERE MESSAGE_ID between V1 and V2 '
|| ' FETCH FIRST ' || RTRIM(CHAR(v_COMMIT_COUNT)) || ' ROWS ONLY) AS DELETE_TABLE';
PREPARE v_DELETE_STATEMENT FROM v_DELETE_QUERY;
DEL_LOOP:
LOOP
EXECUTE v_DELETE_STATEMENT;
IF SQLCODE = 100 THEN
LEAVE DEL_LOOP;
END IF;
COMMIT;
END LOOP;
COMMIT;
END P2;
END P1
DB20000I The SQL command completed successfully.
My procedure was created successfully but when I call it the following issue happens:
db2 "call DELETE_WITH_COMMIT_COUNT1 ('TESTING',50)" SQL0206N "V1" is
not valid in the context where it is used. SQLSTATE=42703
More information :
db2 "select min(MESSAGE_ID),max(MESSAGE_ID) from TESTING where
TIMESTAMP between (select TIMESTAMP(date(min(timestamp))) from TESTING
with ur) and (select TIMESTAMP(date(min(timestamp))) + 1 day from
TESTING with ur) "
1 2
--------------------------------------------------- ---------------------------------------------------
x'4B5753313032353039313133392020202020202020202020' x'4B5753313032353039313230302020202020202020202020'
1 record(s) selected.
I want to delete the records between these values and i currently have 99 records between minimum and maximum message id
message_id column is defined as CHAR(24) FOR BIT DATA on the table .
Your problem is with this statement:
SET v_DELETE_QUERY = 'DELETE FROM (SELECT 1 FROM ' || v_TABLE_NAME || ' WHERE MESSAGE_ID between V1 and V2 '
|| ' FETCH FIRST ' || RTRIM(CHAR(v_COMMIT_COUNT)) || ' ROWS ONLY) AS DELETE_TABLE';
In this statement, it looks like you want to use the values of your previously declared variables, V1 and V2:
' WHERE MESSAGE_ID between V1 and V2 '
DB2 sees this as a string literal. Instead, try changing this part of the statement like so:
SET v_DELETE_QUERY = 'DELETE FROM (SELECT 1 FROM ' || v_TABLE_NAME || ' WHERE MESSAGE_ID between ' || V1 || ' and ' || V2
|| ' FETCH FIRST ' || RTRIM(CHAR(v_COMMIT_COUNT)) || ' ROWS ONLY) AS DELETE_TABLE';
I trying to open the transaction and then delete one record now i need to insert the deleted record into event table. The problem is i can't see the result because it has been deleted.
CREATE procedure [dbo].[TestData] ( #clientid bigint ) As
Begin
print ''abc''
insert into Client_Event_Log values ( getdate(),0,#clientid,100,''B0AE3162-671C-E211-AF2A-00155D051024'',NULL)
BEGIN TRY
BEGIN TRAN
Delete from access_types -- There is only record in the table.
8559230 abc 101 0 2010-01-01 10:25:25.000
select * from access_types -- cann't see the deleted record even before the session.
DECLARE #cGTAEventLog bigint
select #cGTAEventLog=Access_Type_Id from access_types
exec TestData #cGTAEventLog -- Now i am passing 8559230 to the SP to insert into event
table but it has been delete before so can't insert NULL
Commit Tran
END TRY
BEGIN CATCH
ROLLBACK TRAN
--Error message
PRINT 'Error: ' +
CONVERT(VARCHAR,ERROR_NUMBER()) + ' - ' +
CONVERT(VARCHAR,ERROR_SEVERITY()) + ' - ' +
CONVERT(VARCHAR,ERROR_STATE()) + ' - ' +
ERROR_MESSAGE() +
' Raise Error occurred at line ' + CONVERT(VARCHAR,ERROR_LINE())
END CATCH
END
I need to find a way to access the data after deleting so i can insert into a event table.
Assuming you are on SQL Server 2005 or later, you can use the output clause to access the virtual 'deleted' table and park its contents in a table variable. Something like the following
DECLARE #TmpTable TABLE (ID INT, ......)
DELETE
FROM access_types
OUTPUT Deleted.ID,... INTO #TmpTable
select * from #TmpTable