Create Sqlite Table with a Column having Autocalculation - android-sqlite

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.

Related

Get row number of row to be inserted in Postgres trigger that gives no collisions when inserting multiple rows

Given the following (simplified) schema:
CREATE TABLE period (
id UUID NOT NULL DEFAULT uuid_generate_v4(),
name TEXT
);
CREATE TABLE course (
id UUID NOT NULL DEFAULT uuid_generate_v4(),
name TEXT
);
CREATE TABLE registration (
id UUID NOT NULL DEFAULT uuid_generate_v4(),
period_id UUID NOT NULL REFERENCES period(id),
course_id UUID NOT NULL REFERENCES course(id),
inserted_at timestamptz NOT NULL DEFAULT now()
);
I now want to add a new column client_ref, which identifies a registration unique within a period, but consists of only a 4-character string. I want to use pg_hashids - which requires a unique integer input - to base the column value on.
I was thinking of setting up a trigger on the registration table that runs on inserting a new row. I came up with the following:
CREATE OR REPLACE FUNCTION set_client_ref()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
DECLARE
next_row_number integer;
BEGIN
WITH rank AS (
SELECT
period.id AS period_id,
row_number() OVER (PARTITION BY period.id ORDER BY registration.inserted_at)
FROM
registration
JOIN period ON registration.period_id = period.id ORDER BY
period.id,
row_number
)
SELECT
COALESCE(rank.row_number, 0) + 1 INTO next_row_number
FROM
period
LEFT JOIN rank ON (rank.period_id = period.id)
WHERE
period.id = NEW.period_id
ORDER BY
rank.row_number DESC
LIMIT 1;
NEW.client_ref = id_encode (next_row_number);
RETURN NEW;
END
$function$
;
The trigger is set-up like: CREATE TRIGGER set_client_ref BEFORE INSERT ON registration FOR EACH ROW EXECUTE FUNCTION set_client_ref();
This works as expected when inserting a single row to registration, but if I insert multiple within one statement, they end up having the same client_ref. I can reason about why this happens (the rows don't know about each other's existence, so they assume they're all just next in line when retrieving their row_order), but I am not sure what a way is to prevent this. I tried setting up the trigger as an AFTER trigger, but it resulted in the same (duplicated) behaviour.
What would be a better way to get the lowest possible, unique integer for the rows to be inserted (to base the hash function on) that also works when inserting multiple rows?

Postgres partitioned table query scans all partitions instead of one

I have a table
CREATE TABLE IF NOT EXISTS prices
(shop_id integer not null,
good_id varchar(24) not null,
eff_date timestamp with time zone not null,
price_wholesale numeric(20,2) not null default 0 constraint chk_price_ws check (price_wholesale >= 0),
price_retail numeric(20,2) not null default 0 constraint chk_price_rtl check (price_retail >= 0),
constraint pk_prices primary key (shop_id, good_id, eff_date)
)partition by list (shop_id);
CREATE TABLE IF NOT EXISTS prices_1 partition of prices for values in (1);
CREATE TABLE IF NOT EXISTS prices_3 partition of prices for values in (2);
CREATE TABLE IF NOT EXISTS prices_4 partition of prices for values in (3);
CREATE TABLE IF NOT EXISTS prices_4 partition of prices for values in (4);
...
CREATE TABLE IF NOT EXISTS prices_6 partition of prices for values in (100);
I'd like to delete outdated prices. The table is huge , so I try to delete small portions of records.
If I use loop and the variable v_shop_id then after 6 times Postgres starts scanning all partitions. I simplified the code, the real code has inner loop by shop_id.
If I use loop without the variable (I explicitly specify the value) Postgres doesn't scan all partitions
here code with the variable
do $$
declare
v_shop_id integer;
v_date_time timestamp with time zone := now();
begin
v_shop_id := 8;
for step in 1..10 loop
delete from prices p
using (select pd.good_id, max(pd.eff_date) as mxef_dt
from prices pd
where pd.eff_date < v_date_time - interval '30 days'
and pd.shop_id = v_shop_id
group by ppd.good_id
having count(1)>1
limit 40000) pfd
where p.eff_date <= pfd.mxef_dt
and p.shop_id = v_shop_id
and p.good_id = pfd.good_id;
end loop;
end;$$LANGUAGE plpgsql
How can I force Postrges to scan one desired partition only?

Insert a column to an existing table but with null values

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.

PostgreSQL Update TRIGGER Fires Multiple Times When Just 1 Row Updated

Source Table :-
CREATE TABLE schema1.Source_Table
(
Source_Table_id serial NOT NULL,
current_status_id smallint NOT NULL,
current_status_reason varchar(200) NULL,
requestor_id integer NOT NULL,
approver_id integer NULL,
last_upd_user_id integer NOT NULL,
last_upd_date_time timestamp without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
CONSTRAINT PK_Source_Table PRIMARY KEY (Source_Table_id)
)
WITH OIDS;
Destination Table (Audit History Purpose) :-
CREATE TABLE schema2.Destination_Table
(
type_id smallint NOT NULL,
id integer NOT NULL,
state_id smallint NOT NULL,
state_reason varchar(200) NULL,
requestor_id integer NOT NULL,
approver_id integer NULL,
upd_by_user_id integer NOT NULL,
upd_by_user_type smallint NOT NULL,
upd_date_time timestamp without time zone NOT NULL
)
WITH OIDS;
After Update for each Row Trigger on the Source Table :-
CREATE TRIGGER trg_upd_Source_Table
AFTER UPDATE of current_status_id
ON schema1.Source_Table
FOR EACH ROW
WHEN (OLD.current_status_id IS DISTINCT FROM NEW.current_status_id)
EXECUTE PROCEDURE schema1.Source_Table_hist();
Trigger Function for the After Update for each Row Trigger above :-
CREATE OR REPLACE FUNCTION schema1.Source_Table_hist()
RETURNS TRIGGER
LANGUAGE PLPGSQL
AS $$
BEGIN
INSERT INTO schema2.Destination_Table
(type_id, id, state_id, state_reason, requestor_id, approver_id, upd_by_user_id,
upd_by_user_type, upd_date_time)
SELECT 1, OLD.Source_Table_id, OLD.current_status_id, OLD.current_status_reason,
OLD.requestor_id, OLD.approver_id, OLD.last_upd_user_id, 1, OLD.last_upd_date_time
from schema1.Source_Table
where OLD.current_status_id IS DISTINCT FROM NEW.current_status_id;
RETURN NULL;
END;
$$
There are already 8 rows in schema1.Source_Table table with the unique primary key Source_Table_id.
When I update just 1 row of this table as below using the primary key, it inserts 8 rows (1 original and 7 duplicates) into the schema2.Destination_Table table instead of just 1 row.
update schema1.Source_Table
set current_status_id = 4
where Source_Table_id = 9;
The issue here is :-
Why the trigger is firing for 8 times (which is equals to the total number of rows in the table on which this trigger is created) when only 1 row of that table is updated.
Expected Behavior :-
The Trigger should fire only once followed by inserting 1 row in the destination audit table when just 1 row is updated in the source table on which the trigger is created.
How to solve this issue ?
The trigger isn't firing multiple times, your query is inserting a row into the hist table for every row in the source table:
INSERT INTO schema2.Destination_Table
(type_id, id, state_id, state_reason, requestor_id, approver_id, upd_by_user_id,
upd_by_user_type, upd_date_time)
SELECT 1, OLD.Source_Table_id, OLD.current_status_id, OLD.current_status_reason,
OLD.requestor_id, OLD.approver_id, OLD.last_upd_user_id, 1, OLD.last_upd_date_time
from schema1.Source_Table
where OLD.current_status_id IS DISTINCT FROM NEW.current_status_id;
RETURN NULL;
I don't think you need that from clause.
The problem is in the WHERE condition:
where OLD.current_status_id IS DISTINCT FROM NEW.current_status_id
This condition is known to be true from the WHEN condition on the trigger. As it is the only WHERE effective it is the same no WHERE condition at all, there fore all roes are processed by the insert. Suggest
where current_status_id = OLD.current_status_id

How to select last value insert in column ( like function LAST() for OracleDB)

I'm actually sutend and I'm setting up DB PostgreSQL for my AirsoftShop and some request on it. I need to find similar function as SELECT LAST(xx) FROM yy usable on SQL server and OracleDB i think. For return the last insert values in the column target by LAST().
I have this table :
CREATE TABLE munition.suivi_ammo (
type_ammo integer NOT NULL,
calibre integer NOT NULL,
event integer NOT NULL,
date_event date NOT NULL,
entrance integer NOT NULL,
exit integer NOT NULL,
inventory integer NOT NULL,
FOREIGN KEY (calibre) REFERENCES munition.index(numero),
FOREIGN KEY (event) REFERENCES munition.index(numero),
FOREIGN KEY (type_ammo) REFERENCES munition.index(numero)
);
and index for definition by number id :
CREATE TABLE munition.index (
numero integer NOT NULL,
definition text NOT NULL,
PRIMARY KEY (numero)
);
I want to select the last inventory insert in the table and calculate the current inventory according to the inflow and outflow made after my inventory
It's works when i do this type of request with specific date to be sure to only have the last one inventory, but I do not want to have to do it
SELECT index.definition,
Sum(suivi_ammo.inventory) + Sum(suivi_ammo.entrance) - Sum(suivi_ammo.exit) AS Stock
FROM munition.suivi_ammo
INNER JOIN munition.index ON suivi_ammo.type_ammo = index.numero
WHERE date_event < '03/05/2019' AND date_event >= '2019-04-10'
GROUP BY index.definition;
I also tried to used last_value() window function but doesn't work.
Thx !