HSQL: How to save next value of sequence into a variable? - hsqldb

I am using HSQL and inside my stored procedure I need to save the "NEXT VALUE FOR MY_SEQUENCE" into a variable. How do I do this?
CREATE PROCEDURE MYPROCEDURE(
IN PARAM1 NUMERIC)
MODIFIES SQL DATA
BEGIN ATOMIC
DECLARE NEXTID NUMERIC;
SELECT NEXT VALUE FOR MY_SEQUENCE INTO NEXTID FROM MY_TABLE;
INSERT INTO MY_TABLE(ID, COLUMN1) VALUES(NEXTID, PARAM1);
END;
Problem:
The variable NEXTID is returning null.

It looks like the problem is with the SELECT ... INTO statement. MY_TABLE has no rows or has more than one row and the NEXTID variable is not assigned to.
You can try direct assignment with the latest versions of HSQLDB:
SET NEXTID = NEXT VALUE FOR MY_SEQUENCE
With older versions of HSQLDB, you need to have a table with only one row and select from this table to assign to the sequence. When the Oracle compatibility mode is set, the DUAL table can be used. Otherwise create your own table with a single row.
SELECT NEXT VALUE FOR MY_SEQUENCE INTO NEXTID FROM DUAL;

Related

Adding (count) sequence value to column value via trigger in Oracle SQL

I would like to write a trigger which will be able to add sequence value to column value.
Example: Sequence value is 5 and column value in table is 45. Result after inserting new row should be 50.
Problem is that I dont know how to fetch value from the column and then add it to the sequence value in trigger code.
After using my trigger I am able to insert a new row which will create columns with value = 5.
CREATE SEQUENCE Adding_Seq
START WITH 5
INCREMENT BY 1;
CREATE OR REPLACE TRIGGER Adding_Trigger
BEFORE INSERT ON Table
FOR EACH ROW
BEGIN
SELECT Adding_Seq.nextval
INTO :new.Value
FROM dual;
END;
I tried to add some "+" in section SELECT or later in INTO with name of the column from table, but there were errors.
Is is possible to do something like this in trigger or not?
Thanks for you help!
This is really strange. But you can just use addition:
BEGIN
SELECT :new.Value + Adding_Seq.nextval INTO :new.Value
FROM dual;
END;

Updating column with sequence numbers

I have a table for patients and this table has an empty column (varying character) and I want to fill it with sequence numbers and the sequence should start from 1 and end with the number of rows
I also have an existing column that takes holds the date when the row was created, I want the numbers that will be filled in the empty column to be ordered by giving 1 to the oldest date and so on.
You should just use ROW_NUMBER here, at the time you query, rather than updating your table:
SELECT *,
ROW_NUMBER() OVER (ORDER BY date_col DESC) rn
FROM yourTable;
The reason for not attempting an update here is that as soon as new data gets inserted into the table, you might be forced to run the update again, and that could get messy after a while.
I would use this for update:
Let's say your table is called test and columns are id(the one that is empty) and date_col(the column with date when the row was created).
And update your table with this statement:
update test t1
set id = t2.row_num
from (select ROW_NUMBER() OVER (ORDER BY date_col desc) row_num, date_col
from test) t2
where t1.date_col = t2.date_col;
Then to prepare everything for the future inserts I would create a function and a trigger. Function returns the next number that needs to be inserted in the id column and a trigger makes sure that that number will be inserted every next time the insert is made.
CREATE OR REPLACE FUNCTION sequence_test_up()
RETURNS "trigger" AS
$BODY$
BEGIN
New.id:= (select max(id)+1 from test);
Return NEW;
END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE;
CREATE TRIGGER Test_Tr
BEFORE INSERT
ON test
FOR EACH ROW
EXECUTE PROCEDURE sequence_test_up();
And then the next insert would insert ID with the value of the next number in line.
Here is the DEMO with step by step example.

Values of the inserted row in a Trigger Oracle

I want a trigger that updates the value of a column, but I just want to update a small set of rows that depends of the values of the inserted row.
My trigger is:
CREATE OR REPLACE TRIGGER example
AFTER INSERT ON table1
FOR EACH ROW
BEGIN
UPDATE table1 t
SET column2 = 3
WHERE t.column1 = :new.column1;
END;
/
But as I using FOR EACH ROW I have a problem when I try it, I get the mutating table runtime error.
Other option is not to set the FOR EACH ROW, but if I do this, I dont know the inserted "column1" for comparing (or I dont know how to known it).
What can I do for UPDATING a set of rows that depends of the last inserted row?
I am using Oracle 9.
You should avoid the DML statements on the same table as defined in a trigger. Use before DML to change values of the current table.
create or replace trigger example
before insert on table1
for each row
begin
:new.column2 := 3;
end;
/
You can modify the same table with pragma autonomous_transaction:
create or replace trigger example
after insert on table1 for each row
declare
procedure setValues(key number) is
pragma autonomous_transaction;
begin
update table1 t
set column2 = 3
where t.column1 = key
;
end setValues;
begin
setValues(:new.column1);
end;
/
But I suggest you follow #GordonLinoff answere to your question - it's a bad idea to modify the same table in the trigger body.
See also here
If you need to update multiple rows in table1 when you are updating one row, then you would seem to have a problem with the data model.
This need suggests that you need a separate table with one row per column1. You can then fetch the value in that table using join. The trigger will then be updating another table, so there will be no mutation problem.
`create table A
(
a INTEGER,
b CHAR(10)
);
create table B
(
b CHAR (10),
d INTEGER
);
create trigger trig1
AFTER INSERT ON A
REFERENCING NEW AS newROW
FOR EACH ROW
when(newROW.a<=10)
BEGIN
INSERT into B values(:newROW.b,:newROW.a);
END trig1;
insert into A values(11,'Gananjay');
insert into A values(5,'Hritik');
select * from A;
select * from B;`

Merge statement issue in oracle

I came from Microsoft SQL environment.I have two tables tak_ne and tak_beb and my requirement was to insert values from tak_beb to tak_ne if value is not present,if it is present just update.So i made a merge statement as shown below.But the problem now i am facing is veryday 50000 count is getting increment for sequence number.Oracle is stable database, and i don't know why they made it like that.So i create a Function and prevented incrementing sequence number.My question is ,is it a right approach by creating function.Following is what i did
merge into tak_ne a using tak_beb b ON (a.NAME=b.NAME)
When matched then
Update
Set a.AC_NO = b.AC_NO
a.LOCATION = b.LOCATION
a.MODEL = b.MODEL
When not matched then
insert
(
sl_no,
AC_NO,
LOCATION
MODEL
)
Values
(
s_slno_nextval
b.AC_NO
b.LOCATION
b.MODEL
)
and then i created a function
CREATE OR REPLACE FUNCTION s_slno_nextval
RETURN NUMBER
AS
v_nextval NUMBER;
BEGIN
SELECT s_emp.nextval
INTO v_nextval
FROM dual;
RETURN v_nextval;
END;
Oracle uses this approach to generate unique id for each row inserted by a statement. Your TAK_BEB table has probably 50000 rows, so the sequence is incremented 50000 times.
To hide increment into a function does not help. Function is called AND EXECUTED for every row, it increments sequence for 50000 times again. And it adds overhead with 50000 selects from dual table.
If you really need to use ONE value from sequence for ALL rows inserted by statement, use package variable:
create package single_id_pkg is
id Number;
function get_id return number;
end;
/
create or replace package body single_id_pkg is
function get_id return number is
begin
return id;
end;
end;
/
Now use for example before statement trigger on table to set the variable:
create trigger tak_ne_BSI_trg
before insert
on tak_ne
begin
select s_emp.nextval
into single_id_pkg.id
from dual;
end;
Insert trigger has one disadvantage - with MERGE clause it fires even if the statement does only updates rows (see https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:25733900083512). If it is a problem, you have to initialize the variable in other way.
Next modify your statement to use a package variable:
merge into tak_ne a
using tak_beb b
on (a.NAME=b.NAME)
when matched then
update
set a.AC_NO = b.AC_NO
a.LOCATION = b.LOCATION
a.MODEL = b.MODEL
when not matched then
insert (sl_no,
AC_NO,
LOCATION,
MODEL)
values (single_id_pkg.get_id
b.AC_NO,
b.LOCATION,
b.MODEL)
In Oracle standard way to use autoincrement field is by using sequences. And of course it will increment sequence number each time you want to use it.
But you can omit calling sequence_name.nextval, hiding it in trigger it is considered the standard approach also.
CREATE OR REPLACE EDITIONABLE TRIGGER TAK_NE_ID_TR"
BEFORE INSERT ON tak_ne
FOR EACH ROW
BEGIN
IF :old.sl_no IS NULL THEN
:new.sl_no := s_emp.nextval;
END IF;
END;
If you want to add same id for a batch of your inserts you can use global temporary table for saving it. For example, like this:
create global temporary table tak_ne_id ("id" number) on commit delete rows
create or replace trigger tak_ne_BSI_trg
before insert
on tak_ne
begin
insert into tak_ne_id("id")
values(s_emp.nextval);
end
create or replace TRIGGER TAK_NE_ID_TR
BEFORE INSERT ON tak_ne
FOR EACH ROW
BEGIN
if :old.sl_no is null then
SELECT "id"
INTO :new.sl_no
FROM tak_ne_id;
end if;
END;
Then you can use you merge as before, and without calling nextval:
merge into tak_ne a using tak_beb b ON (a.NAME=b.NAME)
When matched then
update
set a.AC_NO = b.AC_NO,
a.LOCATION = b.LOCATION,
a.MODEL = b.MODEL
When not matched then
insert
(
AC_NO,
LOCATION,
MODEL
)
Values
(
b.AC_NO,
b.LOCATION,
b.MODEL
);

How insert rows with max(order_field) + 1 transactionally in PostgreSQL

I need to insert in a PostgreSQL table a row with a column containing the max value + 1 for this same column on a subset of the rows of the table. That column is used to ordering the rows in that subset.
I´m trying to update the column value in an after insert trigger but I´m obtaining duplicate values for this column in different rows.
What´s the best way to do that avoiding duplicate values for the ordering column in the subset in a concurrent environment with a lot of inserts in a short time?
Thanks in advance
EDIT:
The subset is defined by another column of the same table: this column has the same value for all the related rows.
If that column is used only for ordering then use a sequence:
create table t (
column1 integer,
ordering_column serial
);
http://www.postgresql.org/docs/current/static/datatype-numeric.html#DATATYPE-NUMERIC-TABLE
New transactional-safe answer:
To make it in a transactional-safe way you could use this trigger, which creates sequences for each different "set_id" value:
create or replace function calculate_index() returns trigger
as $$
declare my_indexer_name text;
begin
my_indexer_name = 'my_indexer_name_' || NEW.my_set_id;
if NOT EXISTS (SELECT * FROM pg_class WHERE relname = my_indexer_name)
then
execute 'create sequence ' || my_indexer_name;
end if;
select nextval(my_indexer_name) into NEW.my_index;
return new;
end
$$
language plpgsql;
CREATE TRIGGER my_indexer_trigger
BEFORE INSERT ON my_table FOR EACH ROW
EXECUTE PROCEDURE calculate_index();
Also you could create manually sequences named 'my_indexer_name_1', 'my_indexer_name_2', etc. if your set_id possible values are known beforehand, then you could eliminate the if-then from the trigger function above.
This was my initial and not transactional-safe answer:
I would create a new helper table let's call it set_indexes:
create table set_indexes( set_id integer, max_index integer );
each record has the set_id and the max index value of that set. e.g.:
set_id, max_index
1 53
2 12
3 43
in the trigger code you would:
select max_index + 1 from set_indexes where set_indexes.set_id = NEW.my_set_id
into NEW.my_index;
// Chek if the set_id is new:
if NEW.my_index is null then
insert into set_indexes( set_id, max_index) values (NEW.my_set_id, 1);
NEW.my_index = 0;
else
update set_indexes set max_index = NEW.my_index where set_indexes.set_id = NEW.my_set_id;
end if;