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
Related
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.
I have a table like below. I am trying to merge into this table based on the value in a CTE. But when I try to update the table when there is a conflict, it cannot get the value in CTE
CREATE TABLE IF NOT EXISTS master_config_details
(
master_config_id INT NOT NULL,
account_id INT NOT NULL,
date_value TIMESTAMP(3) NULL,
number_value BIGINT NULL,
string_value VARCHAR(50) NULL,
row_status SMALLINT NOT NULL,
created_date TIMESTAMP(3) NOT NULL,
modified_date TIMESTAMP(3) NULL,
CONSTRAINT pk_master_config_details PRIMARY KEY (master_config_id, account_id, row_status)
);
INSERT INTO master_config_details VALUES (
1, 11, NULL,100,NULL, 0, '2020-11-18 12:01:18', '2020-11-18 12:02:31');
select * from master_config_details;`
Now using a cte I want to insert/update records in this table. Below is the code I am using to do the same. When the record already exist in the table I want to update the table based on the data_type_id value in the cte (cte_input_data.data_type_id ) but it fails with the error.
SQL Error [42703]: ERROR: column excluded.data_type_id does not exist
what it should achieve is
if cte_input_data.data_type_id = 1 update master_config_details set date_value = cte.value
if cte_input_data.data_type_id = 2 update master_config_details set number_value = cte.value
if cte_input_data.data_type_id = 3 update master_config_details set string_value = cte.value
The below code should do an update to the table master_config_details.number_value = 22 as there is already a record in that combination (master_config_id, account_id, row_status) which is (1,11,1) ( run this to see the record select * from master_config_details;) but its throwing an error instead
SQL Error [42703]: ERROR: column excluded.data_type_id does not exist
WITH cte_input_data AS (
select
1 AS master_config_id
,11 AS account_id
,2 AS data_type_id
,'22' AS value
,1 AS row_status)
INSERT INTO master_config_details
SELECT
cte.master_config_id
,cte.account_id
,CASE WHEN cte.data_type_id = 1 THEN cte.value::timestamp(3) ELSE NULL END AS date_time_value
,CASE WHEN cte.data_type_id = 2 THEN cte.value::integer ELSE NULL END AS number_value
,CASE WHEN cte.data_type_id = 3 THEN cte.value ELSE NULL END AS string_value
,1
,NOW() AT TIME ZONE 'utc'
,NOW() AT TIME ZONE 'utc'
FROM cte_input_data cte
ON CONFLICT (master_config_id,account_id,row_status)
DO UPDATE SET
date_value = CASE WHEN excluded.data_type_id = 1 THEN excluded.date_time_value::timestamp(3) ELSE NULL END
,number_value = CASE WHEN excluded.data_type_id = 2 THEN excluded.number_value::integer ELSE NULL END
,string_value = CASE WHEN excluded.data_type_id = 3 THEN excluded.string_value ELSE NULL END
,modified_date = NOW() AT TIME ZONE 'utc';
Special excluded table is used to reference values originally proposed for insertion.
So you’re getting this error because this column doesn’t exist in your target table, and so in special excluded table. It exists only in your cte.
As a workaround you can select it from cte using nested select in on conflict statement.
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?
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.
Please help me with the issue, I want to write a trigger where I can insert the values into new table whenever insert/update happens in source table.
Below is the table structure from where I want to fetch data into another table.
LISTING TABLE
Name Null Type
LISTINGID NOT NULL VARCHAR2(28)
LISTINGMANAGERID NOT NULL VARCHAR2(28)
MANAGEAVAILABILITYFLAG VARCHAR2(1)
AVAILABILITYTEXT VARCHAR2(2000)
AREAINQUIRYFLAG VARCHAR2(1)
COUNTRYTEXT VARCHAR2(50)
STATEPROVINCETEXT VARCHAR2(50)
CITYTEXT VARCHAR2(50)
CHECKINTIME VARCHAR2(10)
CHECKOUTTIME VARCHAR2(10)
TIMEZONEID VARCHAR2(20)
PERSONALLINKURL VARCHAR2(150)
GOLDSUBSCRIPTIONSINCE DATE
AUDITPASSFLAG NOT NULL VARCHAR2(1)
MGRONLINEFLAG NOT NULL VARCHAR2(1)
ADMINAPPROVALFLAG NOT NULL VARCHAR2(1)
DELETEDFLAG VARCHAR2(1)
LASTUPDATED NOT NULL DATE
UPDATEDBY NOT NULL VARCHAR2(28)
OCA NOT NULL NUMBER(38)
SUSPENDEDFLAG NOT NULL VARCHAR2(1)
POSSIBLEFEATUREDCITYFLAG NOT NULL VARCHAR2(1)
TOTALPHOTOS NUMBER(38)
SUSPENDEDDATE DATE
OFFLINEDATE DATE
CURRENCY VARCHAR2(3)
POINTCHARGE VARCHAR2(2)
AVERAGEOFREVIEWS FLOAT(126)
NUMBEROFREVIEWS NUMBER(38)
RENTALMODEL VARCHAR2(10)
NOTHANKS VARCHAR2(1)
DIGITALSIGN VARCHAR2(50)
Need result in below table with any update or new insert from listing table.
i)listingid should contain listingid's from listing table.
ii)OFFLINE COLUMN should contain data with below condition.
AuditPassFlag = 'Y' AND AdminApprovalFlag='Y' AND MgrOnlineFlag='Y' AND DeletedFlag is NULL AND SuspendedFlag !='Y'
iii)TIMESTAMP COLUMN lastupdated date from listing table.
LISTING_LASTUPDATE TABLE
Name Null Type
LISTINGID NOT NULL VARCHAR2(20)
IS_OFFLINE VARCHAR2(1)
TIMESTAMP TIMESTAMP(0)
Below the trigger I am have written:
Its not working properly for where condition which means Listing is online: apart from that its work fine.
CREATE OR REPLACE TRIGGER listingLast_updated
AFTER INSERT OR UPDATE
ON listing
FOR EACH ROW
DECLARE
L_Listingid VARCHAR2 (20);
L_ISOFFLINE VARCHAR2 (1);
LASTUPDATED_TIMESTAMP DATE;
BEGIN
SELECT SYSDATE INTO LASTUPDATED_TIMESTAMP FROM DUAL;
IF ( :NEW.AuditPassFlag = 'Y'
AND :NEW.AdminApprovalFlag = 'Y'
AND :NEW.MgrOnlineFlag = 'Y'
AND :NEW.DeletedFlag IS NULL
AND :NEW.SuspendedFlag != 'Y') THEN
L_ISOFFLINE := 'Y';
ELSE
L_ISOFFLINE := 'N';
INSERT
INTO LISTING_LastUPDATED (LISTINGID, IS_OFFLINE, LASTUPDATED_TIMESTAMP)
VALUES (:NEW.LISTINGID, L_ISOFFLINE, LASTUPDATED_TIMESTAMP);
END IF;
END;
You've written:
Need result in below table with any update or new insert from listing table.
That is not what your trigger does. Take another look at your IF statement. It simplifies to:
if ... then
...
else
insert ...
end if;
Your INSERT is in the ELSE clause. You will therefore execute the INSERT when the initial IF statement is true. Not "with any update or new insert".
There are a number of other things you can change:
The variable L_Listingid is never used, remove it.
There's no need to use SELECT INTO ... in order to assign a variable. This can be done as follows in the declaration block:
lastupdated_timestamp date := sysdate;
However, I see no particular need for this variable to exist, so you can remove it and simply use SYSDATE in your sole INSERT.
There's no need to execute an ELSE at all. Set the default value of L_ISOFFLINE to be N and then use that if the IF condition doesn't evaluate to be true.
Two cosmetic changes, there's no need to wrap the ANDs in brackets; I've added the trigger name to the END so it's clearer.
You've you've also written:
TIMESTAMP COLUMN lastupdated date from listing table.
You actually have a LASTUPDATED date in your LISTING table, which means you're not following your own guidelines. You should use the column instead.
Putting all this together, your trigger might look like this:
create or replace trigger listinglast_updated
after insert or update
on listing
for each row
declare
l_isoffline varchar2(1) := 'N';
begin
if :new.auditpassflag = 'Y'
and :new.adminapprovalflag = 'Y'
and :new.mgronlineflag = 'Y'
and :new.deletedflag is null
and :new.suspendedflag != 'Y' then
l_isoffline := 'Y';
end if;
insert into listing_lastupdated (listingid, is_offline, lastupdated_timestamp)
values (:new.listingid, l_isoffline, :new.lastupdated);
end listinglast_updated;