Insert a column to an existing table but with null values - postgresql

I am trying to add a column to one of my existing tables, but keep getting the following error at this place:
ALTER TABLE fact_parkingtransaction ADD COLUMN dateinserted timestamp without time zone NOT NULL;
ERROR: column "dateinserted" contains null values
What does this exactly mean? Postgres doesn't let me create an empty column because the rows will already exist if the column is created or what am I getting wrong?
Here's my script:
do $$
declare
arow record;
curtable varchar(50);
begin
IF EXISTS (SELECT relname FROM pg_class WHERE relname='durations') THEN
-- DO STUFF HERE
-- DROP OLD TRIGGER
DROP TRIGGER IF EXISTS durations_partition_trigger ON durations;
ALTER SEQUENCE IF EXISTS durations_id_seq RENAME TO fact_parkingtransaction_id_seq;
-- Rename events table to fact_entriesexits
ALTER TABLE durations RENAME TO fact_parkingtransaction;
ALTER TABLE fact_parkingtransaction rename column duration to duration_old; -- new value will be set later on
-- Add new columns
ALTER TABLE fact_parkingtransaction ADD COLUMN entryfacilitykey integer;
ALTER TABLE fact_parkingtransaction ADD COLUMN exitfacilitykey integer;
ALTER TABLE fact_parkingtransaction ADD COLUMN systeminterfacekey integer;
ALTER TABLE fact_parkingtransaction ADD COLUMN manufacturerkey integer;
ALTER TABLE fact_parkingtransaction ADD COLUMN tickettypekey integer;
ALTER TABLE fact_parkingtransaction ADD COLUMN entrydatekey integer;
ALTER TABLE fact_parkingtransaction ADD COLUMN entrytimekey integer;
ALTER TABLE fact_parkingtransaction ADD COLUMN exitdatekey integer;
ALTER TABLE fact_parkingtransaction ADD COLUMN exittimekey integer;
ALTER TABLE fact_parkingtransaction ADD COLUMN entrydevicekey integer;
ALTER TABLE fact_parkingtransaction ADD COLUMN exitdevicekey integer;
ALTER TABLE fact_parkingtransaction ADD COLUMN entrytime timestamp without time zone;
ALTER TABLE fact_parkingtransaction ADD COLUMN exittime timestamp without time zone;
ALTER TABLE fact_parkingtransaction ADD COLUMN duration integer;
ALTER TABLE fact_parkingtransaction ADD COLUMN eventid_arrival bigint;
ALTER TABLE fact_parkingtransaction ADD COLUMN eventid_departure bigint;
ALTER TABLE fact_parkingtransaction ADD COLUMN cardnumber character varying(100);
ALTER TABLE fact_parkingtransaction ADD COLUMN licenseplate character varying(100);
ALTER TABLE fact_parkingtransaction ADD COLUMN licenseplatekey integer;
ALTER TABLE fact_parkingtransaction ADD COLUMN dateinserted timestamp without time zone NOT NULL;
ALTER TABLE fact_parkingtransaction ADD COLUMN etlsource integer;
-- Rename events_yyyy_mm to fact_entriesexits_yyyymm
for arow in
SELECT table_name FROM information_schema.tables WHERE table_schema='public' and table_name like 'durations_%'
loop
curtable := arow.table_name;
RAISE NOTICE 'Calling table(%)', curtable;
--Rename
execute ('ALTER TABLE ' || curtable ||' RENAME TO fact_parkingtransaction_' || replace(right(curtable, 8), '_', ''));
--Add idx on datekey
execute('CREATE INDEX idx_fact_parkingtransaction_' || replace(right(curtable, 8), '_', '') || ' ON fact_parkingtransaction_' || replace(right(curtable, 8), '_', '') || ' ( datekey )');
end loop;
-- END RENAME PARTITION
-- Update to 2.0 Mapping
-- TICKET TYPES
Update fact_parkingtransaction e set ticket_type = 14 where ticket_type in (2,15);
Update fact_parkingtransaction e set ticket_type = 41 where ticket_type = 8;
Update fact_parkingtransaction e set ticket_type = 9 where ticket_type = 30;
Update fact_parkingtransaction e set ticket_type = 21 where ticket_type = 20;
Update fact_parkingtransaction e set ticket_type = 33 where ticket_type = 18;
-- fill new fields
Update fact_parkingtransaction e
set entrydatekey = cast(to_char((event_time_arrival)::TIMESTAMP,'yyyymmdd') as integer),
entrytimekey = cast(to_char((event_time_arrival)::TIMESTAMP,'hhmiss') as integer),
exitdatekey = cast(to_char((event_time_departure)::TIMESTAMP,'hhmiss') as integer),
exittimekey = cast(to_char((event_time_departure)::TIMESTAMP,'yyyymmdd') as integer),
entrytime = event_time_arrival,
exittime = event_time_departure,
duration = duration_old,
eventid_arrival = event_id_arrival,
eventid_departure = event_id_departure,
cardnumber = card_nr,
manufacturerkey =
CASE
WHEN manufacturer LIKE '%IPCP%' THEN 1
WHEN manufacturer LIKE '%DESIGNA%' THEN 2
WHEN manufacturer LIKE '%SKIDAT%' THEN 3
WHEN manufacturer LIKE '%SCHEIDT%' THEN 4
END,
dateinserted = odb_created_at;
-- set facilitykey
Update fact_parkingtransaction e set entryfacilitykey = df.key from dim_facility df
where df.facilityid = e.carpark_id and event_time_arrival >= df.scd_start AND (event_time_arrival < df.scd_end OR df.scd_end IS NULL);
-- SET tickettypekey
Update fact_parkingtransaction e set tickettypekey = dt.key from dim_tickettype dt
where dt.tickettypeid = e.ticket_type and event_time_arrival >= dt.scd_start AND (event_time_arrival < dt.scd_end OR dt.scd_end IS NULL);
-- Execute following if ANALYTICS never has been installed
ELSE
CREATE TABLE public.fact_parkingtransaction (
id bigint NOT NULL,
entryfacilitykey integer,
exitfacilitykey integer,
systeminterfacekey integer,
manufacturerkey integer,
tickettypekey integer,
entrydatekey integer,
entrytimekey integer,
exitdatekey integer,
exittimekey integer,
entrydevicekey integer,
exitdevicekey integer,
entrytime timestamp without time zone,
exittime timestamp without time zone,
duration integer,
eventid_arrival bigint,
eventid_departure bigint,
cardnumber character varying(100),
licenseplate character varying(100),
licenseplatekey integer,
dateinserted timestamp without time zone NOT NULL,
etlsource integer
);
CREATE SEQUENCE public.fact_parkingtransaction_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
END IF;
end;
$$;
Thank you

You are asking Postgres to add column that is NOT NULL with a NULL value, as the default DEFAULT is NULL. To correct means you either need to:
Add an explicit DEFAULT value that is NOT NULL to the ADD COLUMN.
Don't use the NOT NULL.
If you choose 2) you can then set the values for the individual rows as you want. Once you have them all filled then you can SET NOT NULL on the column.

Related

Postgresql update multiple rows with same name and id, and update the consecutive rows vrersion

I have a table where insertion is in this form.
Table
I want the verion to get update by 1 whenever there is a new row with same name and id.
Required output
I tried using a function and trigger.
CREATE OR REPLACE FUNCTION update_ver()
RETURNS TRIGGER
LANGUAGE PLPGSQL
AS
$$
BEGIN
update version
set ver = ver + 1
where new.name = 'A' and new.id ='1';
RETURN new;
END;
$$
-- create table
CREATE TABLE mytable (
"name" varchar NULL,
id int4 NULL,
phone varchar NULL,
email varchar NULL,
ver int4 NULL
);
-- create trigger function
CREATE OR REPLACE FUNCTION before_insert()
RETURNS TRIGGER
LANGUAGE PLPGSQL
AS
$$
begin
new.ver = (select coalesce(max(ver), 0) + 1 from mytable where name = new.name and id = new.id);
return new;
end;
$$
-- set trigger function to table
create trigger trg_before_insert before
insert
on
mytable for each row execute function before_insert();
-- inserting sample data
INSERT INTO mytable ("name", id, phone, email) VALUES('A', 1, '123', '123#email.com');
INSERT INTO mytable ("name", id, phone, email) VALUES('B', 2, '345', '345#email.com');
INSERT INTO mytable ("name", id, phone, email) VALUES('A', 1, '456', '456#email.com');
-- select data and view
select * from mytable;
Result:
name|id|phone|email |ver|
----+--+-----+-------------+---+
A | 1|123 |123#email.com| 1|
B | 2|345 |345#email.com| 1|
A | 1|456 |456#email.com| 2|

SELECT value from another table with similar date pgSQL

Please advise proper solution for:
CREATE TABLE test.rate
(
date date NOT NULL,
rate numeric(10,4) NOT NULL,
)
CREATE TABLE test.purchase
(
prod_id text COLLATE pg_catalog."default",
import_id text COLLATE pg_catalog."default",
exw_price_usd numeric(10,2),
exw_price_eur numeric(10,2),
import_exp numeric(10,2),
stock_price numeric(10,2),
qty numeric(10,0),
date date NOT NULL
)
BEGIN
SELECT rate
FROM test.rate
WHERE test.rate (date) = test.purchase (date);
UPDATE purchase
SET exw_price_eur=exw_price_usd/test.rate;
RETURN NEW exw_price_eur;
END;
ERROR: function test.rate(date) does not exist
LINE 3: WHERE test.rate (date) = test.purchase (date);
we need to choose value of rate from table test.rate which corresponds with date value in table test.purchase
For example...
test.rate
date '2020-10-10'
rate '1.2345'
test.purchase
exw_price_usd '30'
date '2020-10-10'
return calculation 30/1.2345
Partially solved this issue, but anyway don't have result as a new record in the exw_price_eur column
BEGIN
SELECT rate
FROM test.rate
INNER JOIN test.purchase
ON purchase.date=rate.date;
UPDATE test.purchase
SET purchase.exw_price_eur=purchase.exw_price_usd/rate.rate;
RETURN NEW.exw_price_eur;
END;
and when process
INSERT INTO test.purchase(
exw_price_usd, date)
VALUES (42, '10-10-2020');
ERROR: query has no destination for result data.
Please advise
Proper solution
BEGIN
UPDATE test.purchase pr
SET exw_price_eur = exw_price_usd/e.rate
FROM test.eurusd e
WHERE e.date=pr.date;
RETURN NEW.exw_price_eur;
END

I'm having an issue with this code when I try to input values into the transactions table

So I'm setting up a schema in which I can input transactions of a journal entry independent of each other but also that rely on each other (mainly to ensure that debits = credits). I set up the tables, function, and trigger. Then, when I try to input values into the transactions table, I get the error below. I'm doing all of this in pgAdmin4.
CREATE TABLE transactions (
transactions_id UUID PRIMARY KEY DEFAULT uuid_generate_v1(),
entry_id INTEGER NOT NULL,
post_date DATE NOT NULL,
account_id INTEGER NOT NULL,
contact_id INTEGER NULL,
description TEXT NOT NULL,
reference_id UUID NULL,
document_id UUID NULL,
amount NUMERIC(12,2) NOT NULL
);
CREATE TABLE entries (
id UUID PRIMARY KEY,
test_date DATE NOT NULL,
balance NUMERIC(12,2)
CHECK (balance = 0.00)
);
CREATE OR REPLACE FUNCTION transactions_biut()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
EXECUTE 'INSERT INTO entries (id,test_date,balance)
SELECT
entry_id,
post_date,
SUM(amount) AS ''balance''
FROM
transactions
GROUP BY
entry_id;';
END;
$$;
CREATE TRIGGER transactions_biut
BEFORE INSERT OR UPDATE ON transactions
FOR EACH ROW EXECUTE PROCEDURE transactions_biut();
INSERT INTO transactions (
entry_id,
post_date,
account_id,
description,
amount
)
VALUES
(
'1',
'2019-10-01',
'101',
'MISC DEBIT: PAID FOR FACEBOOK ADS',
-200.00
),
(
'1',
'2019-10-01',
'505',
'MISC DEBIT: PAID FOR FACEBOOK ADS',
200.00
);
After I execute this input, I get the following error:
ERROR: column "id" of relation "entries" does not exist
LINE 1: INSERT INTO entries (id,test_date,balance)
^
QUERY: INSERT INTO entries (id,test_date,balance)
SELECT
entry_id,
post_date,
SUM(amount) AS "balance"
FROM
transactions
GROUP BY
entry_id;
CONTEXT: PL/pgSQL function transactions_biut() line 2 at EXECUTE
SQL state: 42703
There are a few problems here:
You're not returning anything from the trigger function => should probably be return NEW or return OLD since you're not modifying anything
Since you're executing the trigger before each row, it's bound to fail for any transaction that isn't 0 => maybe you want a deferred constraint trigger?
You're not grouping by post_date, so your select should fail
You've defined entry_id as INTEGER, but entries.id is of type UUID
Also note that this isn't really going to scale (you're summing up all transactions of all days, so this will get slower and slower...)
#chirs I was able to figure out how to create a functioning solution using statement-level triggers:
CREATE TABLE transactions (
transactions_id UUID PRIMARY KEY DEFAULT uuid_generate_v1(),
entry_id INTEGER NOT NULL,
post_date DATE NOT NULL,
account_id INTEGER NOT NULL,
contact_id INTEGER NULL,
description TEXT NOT NULL,
reference_id UUID NULL,
document_id UUID NULL,
amount NUMERIC(12,2) NOT NULL
);
CREATE TABLE entries (
entry_id INTEGER PRIMARY KEY,
post_date DATE NOT NULL,
balance NUMERIC(12,2),
CHECK (balance = 0.00)
);
CREATE OR REPLACE FUNCTION transactions_entries() RETURNS TRIGGER AS $$
BEGIN
IF (TG_OP = 'DELETE') THEN
INSERT INTO entries
SELECT o.entry_id, o.post_date, SUM(o.amount) FROM old_table o GROUP BY o.entry_id, o.post_date;
ELSIF (TG_OP = 'UPDATE') THEN
INSERT INTO entries
SELECT o.entry_id, n.post_date, SUM(n.amount) FROM new_table n, old_table o GROUP BY o.entry_id, n.post_date;
ELSIF (TG_OP = 'INSERT') THEN
INSERT INTO entries
SELECT n.entry_id,n.post_date, SUM(n.amount) FROM new_table n GROUP BY n.entry_id, n.post_date;
END IF;
RETURN NULL; -- result is ignored since this is an AFTER trigger
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER transactions_ins
AFTER INSERT ON transactions
REFERENCING NEW TABLE AS new_table
FOR EACH STATEMENT EXECUTE PROCEDURE transactions_entries();
CREATE TRIGGER transactions_upd
AFTER UPDATE ON transactions
REFERENCING OLD TABLE AS old_table NEW TABLE AS new_table
FOR EACH STATEMENT EXECUTE PROCEDURE transactions_entries();
CREATE TRIGGER transactions_del
AFTER DELETE ON transactions
REFERENCING OLD TABLE AS old_table
FOR EACH STATEMENT EXECUTE PROCEDURE transactions_entries();
Any thoughts on optimization?

Create Sqlite Table with a Column having Autocalculation

I m using below code for creating SQLite table.
db.execSQL("Create table if not exists " + datausage + "(Id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, mainentry text, stime integer , endtime integer, usedtime integer)");
But I want usedtime should autocalculate when I insert stime and etime. usedtime will be difference of etime and stime.
so can we create a column which do calculation automatically. I got this for sql as stated below.
CREATE TABLE dbo.Products
(
ProductID int IDENTITY (1,1) NOT NULL
, QtyAvailable smallint
, UnitPrice money
, InventoryValue AS QtyAvailable * UnitPrice
)
;
https://learn.microsoft.com/en-us/sql/relational-databases/tables/specify-computed-columns-in-a-table?view=sql-server-2017
but this does not work in case of SQLite and I m getting invalid sql statement
can we do this ?
But I want usedtime should autocalculate when I insert stime and
etime.
Assuming that you mean endtime rather than etime. Then you can add a TRIGGER that will automatically UPDATE the usedtime AFTER the row has been INSERTed.
e.g.
CREATE TRIGGER IF NOT EXISTS datausage_timedifference AFTER INSERT ON datausage
BEGIN
UPDATE datausage SET usedtime = endtime - stime WHERE id = new.id;
END
;
Example
Consider the following demonstration:-
DROP TABLE IF EXISTS datausage;
Create table if not exists datausage (Id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, mainentry text, stime integer , endtime integer, usedtime integer);
CREATE TRIGGER IF NOT EXISTS datausage_timedifference AFTER INSERT ON datausage
BEGIN
UPDATE datausage SET usedtime = endtime - stime WHERE id = new.id;
END
;
INSERT INTO datausage VALUES (null,'mainentry1',10,30,0),(null,'mainentry2',50,75,0);
SELECT *, endtime - stime AS alternative_usedtime FROM datausage;
This will :-
drop the table (for ease of rerunning/adjusting, you wouldn't do this normally)
create the table
create the trigger named datausage_timedifference that will automatically update the usedtime after a row is inserted.
add some rows where the usedtime is 0
select the rows from the table showing that usedtime has been automatically calculated
The result being :-
Note that the above also shows that there is no real need to store a value that is derived from other values, as this can be calculated when extracting the value from the database as per
endtime - stime AS alternative_usedtime
This generates an extra column in the result set (Cursor) called alternative_usedtime that is the difference between stime and endtime. As such the usedtime column isn't required.
Deriving a value in this way also does away with the potential need for a second AFTER UPDATE trigger to handle any changes made to the stime or endtime columns.

Create Trigger that Insert data after update on specific column

So i want to insert data to history_rent table and delete data in rent table after update status_peminjaman column on rent table, i am already create Trigger but it doesn't triggered
CREATE OR ALTER TRIGGER AfterUpdateStatus on dbo.peminjaman
FOR UPDATE
AS DECLARE
#nama_peminjam varchar(100),
#tanggal_pinjam datetime,
#tanggal_kemblali datetime,
#nama_guru varchar(100),
#status_peminjaman varchar(50),
#kode_barang varchar(255);
SELECT #nama_peminjam = ins.nama_peminjam FROM INSERTED ins;
SELECT #tanggal_pinjam = ins.tanggal_pinjam FROM INSERTED ins;
SELECT #tanggal_kembali = ins.tanggal_kembali FROM INSERTED ins;
SELECT #nama_guru = ins.nama_guru FROM INSERTED ins;
SELECT #kode_barang = ins.kode_barang FROM INSERTED ins;
SELECT #status_peminjaman = ins.status_peminjaman FROM INSERTED ins;
IF UPDATE(status_peminjaman)
BEGIN
SET #status_peminjaman = 'Selesai'
END
INSERT INTO dbo.history_peminjaman
VALUES(#nama_peminjam,#tanggal_pinjam,#tanggal_kembali,#nama_guru,#kode_barang,#status_peminjaman);
PRINT 'TRIGEREDDDDDDDDD'
GO