I've got a table that people have been inserting into getting the primary key by doing a
SELECT max(id)+1 from table_a;
I want to add some records to that table using a INSERT INTO table_a SELECT ... FROM table_b, table_c ... simple SQL script, and I'm wondering how to generate the primary keys. My first thought was to create a temporary sequence, but Oracle evidently doesn't have a select setval to set the first value. So how do I get the current value of max(id)+1 to set the "start with" parameter to my sequence?
I found something on-line that I thought would work:
COLUMN S new_value st select max(id)+1 S from table_a;
CREATE SEQUENCE cra_seq start with &st;
But it doesn't actually use st in the CREATE SEQUENCE but instead prompts me to enter it, which isn't what I need.
Is this something like what you want?
1 declare
2 id integer;
3 begin
4 select max(rownum)+1 into id from dual;
5 execute immediate 'create sequence myseq start with '||TO_CHAR(id);
6* end;
7 /
Couldn't you use the row_number function like so:
Insert Destination( Id, ...)
Select row_number() over( order by TableA.Col1... ) + MaxDestination.MaxId + 1 Num
, ....
From TableA, TableB,...
Cross Join ( Select Max(Id) MaxId From Destination ) MaxDestination
You can use the row_number analytical function to generate row numbers (1 through N).
Before you do the insert, get the max id that is in the table, and then add the row number to that max and it will populate your table correctly.
In PostgreSQL:
CREATE SEQUENCE new_seq;
ALTER TABLE existing_table ADD COLUMN new_serial_column bigint DEFAULT 0;
UPDATE existing_table SET new_serial_column = nextval('new_seq');
ALTER TABLE existing_table ALTER COLUMN new_serial_column SET NOT NULL;
ALTER TABLE existing_table ALTER COLUMN new_serial_column SET DEFAULT nextval('new_seq');
Although, that code is not idempotent, so check you havn't already created the new sequence, something like:
CREATE FUNCTION fixup_existing_table() RETURNS void AS $$
DECLARE
new_id bigint;
seq_column_exists integer;
BEGIN
SELECT Count(column_name) INTO seq_column_exists FROM information_schema.columns WHERE table_name='existing_table' and column_name='new_serial_column';
IF seq_column_exists != 0 THEN RETURN; END IF;
CREATE SEQUENCE new_seq;
ALTER TABLE existing_table ADD COLUMN new_serial_column bigint DEFAULT 0;
UPDATE existing_table SET new_serial_column = nextval('new_seq');
ALTER TABLE existing_table ALTER COLUMN new_serial_column SET NOT NULL;
ALTER TABLE existing_table ALTER COLUMN new_serial_column SET DEFAULT nextval('new_seq');
END;
$$ LANGUAGE plpgsql;
Then, you can safely call: SELECT fixup_existing_table() to alter the schema as many times as you like, i.e. call from some dumb update script.
Related
I am trying to convert this if statement from SQL Server to Postgres:
IF COL_LENGTH('Flex','revision') IS NOT NULL and OBJECT_ID(N'df_revision', N'D') IS NULL
BEGIN
ALTER TABLE Flex ADD CONSTRAINT df_revision DEFAULT 0 FOR revision
END
The purpose of this query is to set the default on the revision column if the table has that column.
Postgres has default values for columns. Those aren't "constraints".
To change the default value of a column you use:
alter table flex alter column revision set default 0;
If for some strange reason you don't know if the column exists in the table, you can use a PL/pgSQL block:
do
$$
begin
if exists (select *
from information_schema.columns
where table_name = 'flex'
and column_name = 'df_revision')
then
alter table flex alter column revision set default 0;
end if;
end;
$$
;
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()...
I have a postgres table with millions of record in it. Now I want to add new column to that table called "time_modified" with the value in another column "last_event_time". Running a migration script is taking long time , so need a simple solution to run in production.
Assuming that the columns are timestamps you can try:
alter table my_table add time_modified text;
alter table my_table alter time_modified type timestamp using last_event_time;
I suggest use function with pg_sleep, which wait between iteriation in loop
This way don't invoke exclusive lock and others locks on your_table.
SELECT pg_sleep(seconds);
But time of execute is long
alter table my_table add time_modified timestamp;
CREATE OR REPLACE FUNCTION update_mew_column()
RETURNS void AS
$BODY$
DECLARE
rec record;
BEGIN
for rec in (select id,last_event_time from your_table) loop
update your_table set time_modified = rec.last_event_time where id = rec.id;
PERFORM pg_sleep(0.01);
end loop;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
and execute function:
select update_mew_column();
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;`
attempting to maintain an edit log using rules.
create table t1(
id serial primary key,
c1 text,
... );
create table edit_log(
id int references t1,
editor_id int references users,
edit_ts timestamp default current_timestamp );
with an update, wish to update t1 and insert into edit_lot
update t1 set c1='abc', ... where id=456;
insert into edit_log( id, editor_id, current_timestamp );
this would be a pretty straightforward except for the arbitrary number of columns, eg,
update t1 set c1='abc', c2='def', editor_id=123 where id=456;
update t1 set c3='xyz', editor_id=123 where id=456;
how to write a rule for that?
I think a trigger will serve you better than a rule. Consider this demo.
Test setup
CREATE TEMP TABLE t1(id int, editor_id int, c1 text);
INSERT INTO t1(id, editor_id) VALUES (1,1),(2,2);
CREATE TEMP TABLE edit_log(id int, editor_id int, edit_ts timestamp);
Create trigger function
CREATE OR REPLACE FUNCTION trg_t1_upaft_log()
RETURNS trigger AS
$BODY$
BEGIN
IF OLD IS DISTINCT FROM NEW THEN -- to avoid empty updates
INSERT INTO edit_log(id, editor_id, edit_ts)
VALUES(NEW.id, NEW.editor_id, now()::timestamp);
END IF;
RETURN NULL; -- trigger will be fired AFTER updates, return value is irrelevant.
END;
$BODY$
LANGUAGE plpgsql VOLATILE;
Create trigger
CREATE TRIGGER upaft_log
AFTER UPDATE ON t1
FOR EACH ROW
EXECUTE PROCEDURE trg_t1_upaft_log();
Test
UPDATE t1 SET c1 = 'baz' WHERE id = 1;
SELECT * FROM edit_log; -- 1 new entry
UPDATE t1 SET c1 = 'baz' WHERE id = 1;
SELECT * FROM edit_log; -- no new entry, update changed nothing!
UPDATE t1 SET c1 = 'blarg';
SELECT * FROM edit_log; -- 2 new entries, update changed two rows.
Cleanup
DROP TRIGGER upaft_log ON t1;
DROP FUNCTION trg_t1_upaft_log()
-- Temp. tables will be dropped automatically at end of session.
Comment
It is very hard or plain impossible (depending on the details of your setup) for a rule to figure out which rows are updated.
A trigger AFTER UPDATE can decide after the fact and is the better choice. Also easy to integrate with (most) additional triggers and / or rules in this scenario.