Updating column with sequence numbers - sql

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.

Related

Trigger for conditional insert into table

I have subscription data that is being appended to a table in real-time (via kafka). i have set up a trigger such that once the data is added it is checked for consistency. If checks pass some of the data should be added to other tables (that have master information on the customer profile etc.). The checks function i wrote works fine but i keep getting errors on the function used in the trigger. The function for the trigger is:
CREATE OR REPLACE FUNCTION update_tables()
RETURNS TRIGGER
LANGUAGE plpgsql
AS
$$
BEGIN
CASE (SELECT check_incoming_data()) WHEN 0
THEN INSERT INTO
sub_master(sub_id, sub_date, customer_id, product_id)
VALUES(
(SELECT sub_id::int FROM sub_realtime WHERE CTID = (SELECT MAX(CTID) FROM sub_realtime)),
(SELECT sub_date::date FROM sub_realtime WHERE CTID = (SELECT MAX(CTID) FROM sub_realtime)),
(SELECT customer_id::int FROM sub_realtime WHERE CTID = (SELECT MAX(CTID) FROM sub_realtime)),
(SELECT product_id::int FROM sub_realtime WHERE CTID = (SELECT MAX(CTID) FROM sub_realtime))
);
RETURN sub_master;
END CASE;
RETURN sub_master;
END;
$$
The trigger is then:
CREATE TRIGGER incoming_data
AFTER INSERT
ON claims_realtime_3
FOR EACH ROW
EXECUTE PROCEDURE update_tables();
What I am saying is 'if checks pass then select data from the last added row and add them to the master table'. What is the best way to structure this query?
Thanks a lot!
The trigger functions are executed for each row and you must use a record type variable called "NEW" which is automatically created by the database in the trigger functions. "NEW" gets only inserted records. For example, I want to insert data to users_log table when inserting records to users table.
CREATE OR REPLACE FUNCTION users_insert()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
begin
insert into users_log
(
username,
first_name,
last_name
)
select
new.username,
new.first_name,
new.last_name;
return new;
END;
$function$;
create trigger store_data_to_history_insert
before insert
on users for each row execute function users_insert();

Trigger function to UPDATE a column up on INSERT

I would like to setup a trigger function for a postgresql table, which should update a column with the data derived from another column.
Table:
CREATE TABLE day (
symbol varchar(20) REFERENCES another_table(symbol),
date date,
open NUMERIC(8,2),
high NUMERIC(8,2),
low NUMERIC(8,2),
close NUMERIC(8,2),
sma8 NUMERIC(8,2),
PRIMARY KEY(symbol, date));
Note: composite primary key.
Sample INSERT:
INSERT INTO day VALUES('ABC', '2019-03-19', 102.3, 110.0, 125.3, 120.4, 0);
INSERT INTO day VALUES('ABC', '2019-03-20', 112.3, 109.0, 119.3, 118.4, 0);
INSERT INTO day VALUES('DEF', '2019-03-19', 1112.3, 1100.0, 1155.3, 1120.4, 0);
INSERT INTO day VALUES('DEF', '2019-03-20', 1202.3, 1180.0, 1205.3, 1190.4, 0);
and so on.
The following trigger function works fine when the 'date' column is the only primary key and the table contains data pertaining to one 'symbol' only (i.e the table contains data of one particular symbol on various unique dates).
create or replace function update_sma8() RETURNS TRIGGER AS
$$
BEGIN
UPDATE day d SET sma8 = s.simple_mov_avg
FROM
(
SELECT sec.date,AVG(sec.close)
OVER(ORDER BY sec.date ROWS BETWEEN 7 PRECEDING AND CURRENT ROW) AS
simple_mov_avg FROM day sec
)s where s.date = NEW.date --The newly inserted date
AND d.date = s.date;
RETURN NULL;
END $$ language plpgsql;
Refer: SQL trigger function to UPDATE daily moving average upon INSERT
I would like to update 'sma8' column with the value derived by averaging the current 'close' value and the last 7 'close' values of one particular symbol ('date' varies i.e past data.). Likewise for other symbols.
Kindly guide me. Thank you.
You need to know how to filter rows by "symbol".
Add WHERE clause to filter.
WHERE sec.symbol = NEW.symbol
And then, you register the trigger.
CREATE TRIGGER day_insert
AFTER INSERT ON day
FOR EACH ROW
EXECUTE PROCEDURE update_sma8();
Make sure that the "sma8" column will be updated when a row is inserted.
Here is full code.
DROP FUNCTION public.update_sma8();
CREATE FUNCTION public.update_sma8()
RETURNS trigger
LANGUAGE 'plpgsql'
COST 100
VOLATILE NOT LEAKPROOF
AS $BODY$
BEGIN
UPDATE day d SET sma8 = s.simple_mov_avg
FROM
(
SELECT sec.date,AVG(sec.close)
OVER(ORDER BY sec.date ROWS BETWEEN 7 PRECEDING AND CURRENT ROW) AS
simple_mov_avg FROM day sec WHERE symbol = NEW.symbol
)s where s.date = NEW.date --The newly inserted date
AND d.date = s.date ;
RETURN NULL;
END $BODY$;
ALTER FUNCTION public.update_sma8()
OWNER TO postgres;
You may add PARTITION BY symbol and then use it in the where clause to calculate for each symbol.
create or replace function update_sma8() RETURNS TRIGGER AS
$$
BEGIN
UPDATE day d SET sma8 = s.simple_mov_avg
FROM
(
SELECT sec.date,sec.symbol,AVG(sec.close)
OVER( partition by symbol ORDER BY sec.date ROWS BETWEEN
7 PRECEDING AND CURRENT ROW) AS
simple_mov_avg FROM day sec
)s where s.date = NEW.date --The newly inserted date
AND d.date = s.date
AND d.symbol = s.symbol;
RETURN NULL;
END $$ language plpgsql;
DEMO

PostgreSQL - How to keep a column updated

I'm new to SQL and I'm trying to update a column (ex_counter) of a table (ex_table). This column consists of a counter of the number of times an ID (ex_id) appears on a second table (ex2_id in ex2_table).
An ID can be inserted into the second table at any moment. If that ID is already existing, the counter of its corresponding ID in the first table must be updated (by simply adding 1, I guess).
These are the two tables:
CREATE TABLE ex_table(
ex_id SMALLINT,
ex_counter SMALLINT;)
CREATE TABLE ex2_table(
ex2_id SMALLINT;)
I think it should be done more or less like this. The commented code is the pseudocode that I don't know how to implement:
CREATE TRIGGER ex_trigger AFTER
INSERT ON ex2_table
FOR EACH ROW EXECUTE PROCEDURE ex_func();
CREATE FUNCTION ex_func() RETURNS trigger AS $$ BEGIN
/*
if ex2_id = ex_id
ex_counter = ex_counter + 1
*/
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
Thanks in advance!
Something like this
IF EXISTS (SELECT * FROM ex_table WHERE ex_id = new.ex2_id) THEN
UPDATE ex_table
SET ex_counter = ex_counter + 1
WHERE ex_id = new.ex2_id
ELSE
INSERT INTO ex_table VALUES (new.ex2_id, 1)
END IF;
Note that it is no point really to store a counter since you so easily can retrieve the value by doing a SELECT COUNT()...

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

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;

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;