How to assign variable value from a table in Postgresql Procedure - postgresql

I have written this procedure in PostgreSQL.
I have below variable values stored in a separate lookup table(schema1.instance_lookup). How can I assign these values into a loop in the procedure instead of hardcoding them in the procedure itself? -
instance-Endpoint
Pwd
username
dbinstanceidentifier
Appname
Procedure -
CREATE OR REPLACE PROCEDURE schema1.pg_parameters(
)
LANGUAGE 'plpgsql'
AS $BODY$
DECLARE
x text;
BEGIN
INSERT INTO schema1.PG_PARAMETERS Select * FROM DBLINK('host=instance-Endpoint dbname=databasename password=Pwd user=username', 'SELECT ''dbinstanceidentifier'' as instance, name,setting,''Appname'' as application, current_timestamp as timestamp,pending_restart FROM pg_settings') AS T1(INSTANCE text, NAME text, SETTING text,application text,timestamp timestamp,pending_restart boolean);
INSERT INTO schema1.PG_PARAMETERS Select * FROM DBLINK('host=instance-Endpoint dbname=databasename password=Pwd user=username', 'SELECT ''dbinstanceidentifier'' as instance,name,setting,''Appname'' as application, current_timestamp as timestamp,pending_restart FROM pg_settings') AS T1(INSTANCE text, NAME text, SETTING text,application text,timestamp timestamp,pending_restart boolean);
INSERT INTO schema1.PG_PARAMETERS Select * FROM DBLINK('host=instance-Endpoint dbname=databasename password=Pwd user=market', 'SELECT ''dbinstanceidentifier'' as instance, name,setting, ''Appname'' as application, current_timestamp as timestamp,pending_restart FROM pg_settings') AS T1(INSTANCE text, NAME text, SETTING text,application text,timestamp timestamp,pending_restart boolean);
INSERT INTO schema1.PG_PARAMETERS Select * FROM DBLINK('host=instance-Endpoint dbname=databasename password=Pwd user=username', 'SELECT ''dbinstanceidentifier'' as instance, name,setting,''Appname'' as application, current_timestamp as timestamp,pending_restart FROM pg_settings') AS T1(INSTANCE text, NAME text, SETTING text,application text,timestamp timestamp,pending_restart boolean);
INSERT INTO schema1.PG_PARAMETERS Select * FROM DBLINK('host=instance-Endpoint dbname=databasename password=Pwd user=username', 'SELECT ''dbinstanceidentifier'' as instance,name,setting,''Appname'' as application, current_timestamp as timestamp,pending_restart FROM pg_settings') AS T1(INSTANCE text, NAME text, SETTING text,application text,timestamp timestamp,pending_restart boolean);
end;
$BODY$;

Related

Calling the column in call method in PostgreSQL

Why is not working for executing the alter table in stored procedure? I tried everything but nothing is working
CREATE OR REPLACE PROCEDURE change_datatypes(columnname VARCHAR(255), datatype VARCHAR(255))
language plpgsql
as $$
BEGIN
EXECUTE
ALTER TABLE public.Sales_2019 ALTER COLUMN columnname TYPE datatype USING columnname :: datatype;
END $$;
CALL change_datatypes(Sales_2019.orderid, 'INTEGER') FROM Sales_2019;
UPDATED
CREATE OR REPLACE PROCEDURE change_datatypes(column_name VARCHAR(255), datatype VARCHAR(255))
language plpgsql
as $$
BEGIN
EXECUTE('ALTER TABLE Sales_2019 ALTER COLUMN'
|| quote_ident(column_name)
||'TYPE' || quote_literal(datatype) || 'USING '
||quote_ident(column_name) || '::'
||quote_literal(datatype));
END $$;
CALL change_datatypes(OrderID, INTEGER);

Stored procedure in pgsql to create materialized views

I'm trying to create a stored procedure in pgsql that creates a materialized view with data from a specific year from a table. The parameters will be the table name, the column that contains the year, and the year.
I know that in SQL Server it would be something like this:
CREATE PROCEDURE createMaterializedView
#tablename varchar(100),
#column varchar(100),
#year integer
AS
BEGIN
DECLARE #return varchar(1000) = 'CREATE MATERIALIZED VIEW view_' + #tablename + '_' + #year + 'AS SELECT * from' + #tablename + 'where'
+ #column + ' = ' + #year
EXECUTE sp_executesql #return
END
In pgsql, what I got right now is this:
CREATE PROCEDURE createMaterializedView(tablename varchar(100), column varchar (100), year integer)
LANGUAGE 'plpgsql'
AS $$
BEGIN
CREATE MATERIALIZED VIEW "view_" + $tablename + "_" + $year
AS
SELECT * from tablename
WHERE column = year
END;
$$;
Appreciate any help.
Consider a dynamic query, formatted for your identifiers (view name, table, column) and literal value (year) to be run using EXECUTE.
CREATE OR REPLACE PROCEDURE create_materialized_view (
_tablename varchar(100),
_column varchar(100),
_year integer
)
LANGUAGE 'plpgsql' AS
$proc$
DECLARE
_vw text := 'view_' || _tablename || '_' || _year;
_sql text :=
'CREATE MATERIALIZED VIEW %I AS
SELECT * FROM %I WHERE %I = %L';
BEGIN
EXECUTE format(_sql, _vw, _tablename, _column, _year);
END
$proc$

JSONB to record update using for loop - postgres

I am trying to update multiple fields from JSONB but getting error like cannot call_populate composite on an array.
I have written below code:-
do $$
<<myjsonb>>
declare
spec jsonb:=('[
{"schema_name":"public",
"table_name":"temp",
"nw_schema":public,
"nw_table": "temp",
"nw_col":"id"},
{"schema_name":"public",
"table_name":"temp",
"nw_schema":public,
"nw_table": "temp",
"nw_col":"name"}
]');
i record;
BEGIN
for i in SELECT * from jsonb_to_record(spec) as (schema_name text, table_name text, nw_schema text, nw_table text, nw_col text)
LOOP
update my_table set schema_name=i->>schema_name, table_name=i->>table_name where nw_schema=i->>nw_schema and nw_table=i->>nw_table and nw_col=i->>nw_col;
end loop;
end myjsonb $$;
There are three things to touch.
Your JSON syntax is invalid, "nw_schema":public must be quoted;
jsonb_to_record shall become jsonb_to_recordset;
Expressions like i->>schema_name shall become i.schema_name.
So here it is corrected:
do $$
declare
spec jsonb:='[
{
"schema_name":"public",
"table_name":"temp",
"nw_schema":"public",
"nw_table": "temp",
"nw_col":"id"
},
{
"schema_name":"public",
"table_name":"temp",
"nw_schema":"public",
"nw_table": "temp",
"nw_col":"name"
}
]';
i record;
begin
for i in select * from jsonb_to_recordset(spec) as (schema_name text, table_name text, nw_schema text, nw_table text, nw_col text)
loop
update my_table
set schema_name = i.schema_name, table_name = i.table_name
where nw_schema = i.nw_schema and nw_table = i.nw_table and nw_col = i.nw_col;
end loop;
end $$;

PL/pgSQL: How to use IF NEW.<variable_column_name> <> OLD.<variable_column_name>

I am pretty new to PL/pgSQL programming. I have a requirement of audit logging updated columns in my table
Table
create table sample_table(name varchar(15),city varchar(15),age int,mail varchar(20) primary key);
Audit table
create table sample_table__audits_dynamicols(mail varchar(20), columnchanged varchar(10), oldvalue varchar(10), changed_on timestamp(6) NOT NULL)
Trigger Function
CREATE FUNCTION public.log_sample_table_allchanges() RETURNS trigger AS $BODY$DECLARE
_colname text;
_tablename varchar(15) := 'sample_table';
_schema varchar(15) := 'public';
_changed_on time := now();
BEGIN
FOR _colname IN SELECT column_name FROM information_schema.Columns WHERE table_schema = _schema AND table_name = _tablename LOOP
IF NEW._colname <> OLD._colname THEN
INSERT INTO sample_table__audits_dynamicols(mail,columnchanged, oldvalue ,changed_on)
VALUES(OLD.mail,_colname,OLD.:_colname,_changed_on);
END IF;
END LOOP;
RETURN NEW;
END$BODY$
LANGUAGE plpgsql VOLATILE NOT LEAKPROOF;
Trigger
create TRIGGER log_sample_table_allchanges
BEFORE UPDATE
ON SAMPLE_TABLE
FOR EACH ROW
EXECUTE PROCEDURE log_sample_table_allchanges();
Requirement: Whenever a column value is changed i want to log it as
(mail, columnname, columnvalue, date)
E.g:
insert into sample_table (name, mail, city, age) values('kanta','mk#foo.com','hyd',23);
insert into sample_table (name, mail, city, age) values('kmk','mk#gmail.com','hyd',23);
So when i update like the following
update sample_table set age=24 where mail='mk#foo.com';
update sample_table set city='bza' where mail='mk#gmail.com'
I want audit table to record like
(mk#foo.com,age,23, timestamp)
(mk#gmail.com, city, hyd, timestamp)
Right now I am facing issue with column comparison in my Trigger function. Please help me rectifying my Trigger function to meet my requirement.
You may use EXECUTE to get the values of columns dynamically and do the comparison.
CREATE OR REPLACE FUNCTION public.log_sample_table_allchanges() RETURNS trigger AS
$BODY$
DECLARE
_colname text;
_tablename varchar(15) := 'sample_table';
_schema varchar(15) := 'public';
_changed_on timestamp := now();
_old_val text;
_new_val text;
BEGIN
FOR _colname IN SELECT column_name FROM information_schema.Columns WHERE table_schema = _schema AND table_name = _tablename
LOOP
EXECUTE 'SELECT $1.' || _colname || ', $2.' || _colname
USING OLD,NEW
INTO _old_val, _new_val; --get the old and new values for the column.
IF _new_val <> _old_val THEN
INSERT INTO sample_table__audits_dynamicols(mail,columnchanged, oldvalue ,changed_on)
VALUES(OLD.mail,_colname,_old_val,_changed_on);
END IF;
END LOOP;
RETURN NEW;
END$BODY$
LANGUAGE plpgsql VOLATILE NOT LEAKPROOF;
I'm not sure why you have defined mail as a PRIMARY KEY in the audits table, it will cause unique constraint violation if the same mail gets updated twice.

How to Return a Table That Was Created Within Function?

I have a PL/pgSQL function that creates a table. I would like to return the table that was created as part of a "Return Table" function. Here is an example of the type of function I'm talking about:
CREATE OR REPLACE FUNCTION my_schema.new_foo_table(character varying)
RETURNS table(row_id bigint,
catalog_id varchar,
value numeric(5,4)) AS
$BODY$
DECLARE
v_table_name ALIAS FOR $1;
cmd text;
BEGIN
cmd := 'CREATE TABLE '||v_table_name||'
(row_id bigint,
catalog_id varchar,
value numeric(5,4))';
EXECUTE cmd;
cmd := 'INSERT INTO '||v_table_name||'
SELECT row_id, catalog_id, sum(value)
FROM other_table
GROUP BY row_id, catalog_id';
EXECUTE cmd;
RETURN --results of select * from v_table_name;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
How would I return the results of the new table I created? I'm guess the return would have to use some type of dynamic SQL "execute" statement since the name of the newly created table is in a variable.
Yes this can be done using RETURN QUERY EXECUTE 'command string'. For your example it would look like this:
CREATE OR REPLACE FUNCTION my_schema.new_foo_table(character varying)
RETURNS table(row_id bigint,
catalog_id varchar,
value numeric(5,4)) AS
$BODY$
DECLARE
arg_table_name ALIAS FOR $1;
v_table_name varchar;
cmd text;
BEGIN
v_table_name := quote_ident(arg_table_name);
cmd := 'CREATE TABLE '||v_table_name||'
(row_id bigint,
catalog_id varchar,
value numeric(5,4))';
EXECUTE cmd;
cmd := 'INSERT INTO '||v_table_name||'
SELECT row_id, catalog_id, sum(value)
FROM other_table
GROUP BY row_id, catalog_id';
EXECUTE cmd;
RETURN QUERY EXECUTE 'select * from v_table_name';
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
For additional information see section 40.6.1.2. in the POSTGRESQL documentation: http://www.postgresql.org/docs/9.3/static/plpgsql-control-structures.html#PLPGSQL-STATEMENTS-RETURNING