Can PostgreSQL join to a stored procedure? - postgresql

In SQL Server and Oracle, joining to (or selecting from) stored procedures that return a resultset is not supported.
Is it possible in PostgreSQL?
If so, a followup question is: is it possible, perhaps via foreign data wrappers, to join to a stored procedure within an MS SQL Server database??

PostgreSQL can join to a stored procedure (function). Tables and rows to get us started.
create schema test;
create table test.test_a (
id integer primary key
);
insert into test.test_a values
(1), (2);
create table test.test_b (
id integer references test.test_a,
n integer not null,
primary key (id, n)
);
insert into test.test_b values
(1, 1), (1, 2), (1, 3), (2, 1), (2, 2);
A simple function.
create or replace function test.return_test_b ()
returns table (
id integer,
n integer
) as
$$
select * from test.test_b
$$ language sql;
Now you can join directly.
select A.id, B.n from test.test_a A
inner join test.return_test_b() B on A.id = B.id;
id n
--
1 1
1 2
1 3
2 1
2 2

Related

How to pull out records based on array of values

Suppose the following structure:
CREATE SCHEMA IF NOT EXISTS my_schema;
CREATE TABLE IF NOT EXISTS my_schema.user (
id SERIAL PRIMARY KEY,
tag_id BIGINT NOT NULL
);
CREATE TABLE IF NOT EXISTS my_schema.conversation (
id SERIAL PRIMARY KEY,
user_ids BIGINT[] NOT NULL
);
INSERT INTO my_schema.user VALUES
(1, 55555),
(2, 77777);
INSERT INTO my_schema.conversation VALUES
(1, '{1,2}');
I can pull out the my_schema.conversation records if I know the my_schema.user.id values:
SELECT *
FROM my_schema.conversation
WHERE user_ids #> '{1}'
The above works, but I need to use my_schema.user.tag_id instead of my_schema.user.id:
How can I do this?
Fiddle
You would have to join the two tables on the array values
SELECT *
FROM my_schema.user u
JOIN my_schema.conversation c
ON u.id = any(c.chat_ids)
WHERE u.tag_id=55555;

Using cte with update in db2

I have set of queries which are getting repeated in update query for db2
I need to get rid of these getting repeated
So what best I can do. Cte is not working in update query in db2.
Not able to find solution. For select query cte is working but not for update
You may use so called "SELECT from data-change-table-reference" Db2 functionality using CTE.
CREATE TABLE MYTAB (ID INT, DATA INT);
INSERT INTO MYTAB (ID, DATA) VALUES (1, NULL);
WITH
UPD (ID, DATA) AS (VALUES (1, 1), (2, 2))
SELECT COUNT (1)
FROM NEW TABLE
(
UPDATE MYTAB T
SET DATA = U.DATA
FROM UPD U
WHERE T.ID = U.ID
);
SELECT * FROM MYTAB;
ID
DATA
1
1
fiddle

Is it possible to use nextval of a sequence as default value of a field in a table in infomrix database?

I have a table named T1 with a field id(int8) and a sequence named seq_id.
Can I use seq_id.nextval as a default value for id in informix database?
Or another way, can I use a trigger to update id before insert with seq_id.nextval?
In Informix you cannot use a sequence NEXTVAL as a default value for a column.
One option is to turn the column into a BIGSERIAL.
Other option is with an insert trigger. I did not find a way to directly assign the sequence NEXTVAL in the trigger definition, but it can be done by using a stored procedure.
CREATE SEQUENCE seq_id
INCREMENT BY 1 START WITH 1
MINVALUE 0
NOCYCLE CACHE 10
ORDER;
CREATE TABLE t1
(
id BIGINT NOT NULL
, val1 CHAR(4)
);
I found 2 ways to use the procedure. A generic procedure that returns the sequence NEXTVAL and a trigger procedure, that assigns the sequence NEXTVAL to the id.
Using a generic procedure:
CREATE FUNCTION spl_get_seq_id()
RETURNING BIGINT AS seq_id_next;
DEFINE seq_id_next BIGINT;
LET seq_id_next = seq_id.NEXTVAL;
RETURN seq_id_next;
END FUNCTION;
CREATE TRIGGER t1_ti
INSERT ON t1 REFERENCING NEW AS new_ins
FOR EACH ROW
(
EXECUTE FUNCTION spl_get_seq_id() INTO id
);
Inserting a few values into the table and checking the result:
INSERT INTO t1( id, val1 ) VALUES ( 1000, 'AAAA' );
INSERT INTO t1( val1 ) VALUES ( 'AAAB' );
INSERT INTO t1( id ) VALUES ( 1 );
INSERT INTO t1( id, val1 ) VALUES ( NULL::BIGINT, 'AAAD' );
SELECT * FROM t1;
id val1
1 AAAA
2 AAAB
3
4 AAAD
Using a trigger procedure:
DROP TRIGGER t1_ti;
CREATE PROCEDURE t1_ti_spl_get_seq_id()
REFERENCING NEW AS new_values FOR t1;
LET new_values.id = seq_id.NEXTVAL;
END PROCEDURE;
CREATE TRIGGER t1_ti
INSERT ON t1
FOR EACH ROW
(
EXECUTE PROCEDURE t1_ti_spl_get_seq_id() WITH TRIGGER REFERENCES
);
Inserting a few values into the table and checking the result:
INSERT INTO t1( id, val1 ) VALUES ( 1000, 'AAAE' );
INSERT INTO t1( val1 ) VALUES ( 'AAAF' );
INSERT INTO t1( id ) VALUES ( 1 );
INSERT INTO t1( id, val1 ) VALUES ( NULL::BIGINT, 'AAAH' );
SELECT * FROM t1;
id val1
1 AAAA
2 AAAB
3
4 AAAD
5 AAAE
6 AAAF
7
8 AAAH
I used BIGINT for the id column, but it should work for INT8 (as to why did I use it, seems there is some benefits: Counters and codes: BIGINT, INT8, INTEGER, and SMALLINT).
EDIT 1:
In response to your comment, you can try a condition on the trigger, based on the session user. This will only work if the cdc software is using a dedicated user. In this example, cdc_agent is the user that the cdc software uses in Informix.
DATABASE db1;
GRANT CONNECT TO cdc_agent;
GRANT CONNECT TO myuser;
CREATE SEQUENCE seq_id
INCREMENT BY 2 START WITH 2
MINVALUE 0
NOCYCLE CACHE 10
ORDER;
GRANT SELECT ON seq_id TO cdc_agent;
GRANT SELECT ON seq_id TO myuser;
CREATE TABLE t1
(
id BIGINT NOT NULL
, val1 CHAR(4)
);
GRANT ALL ON t1 TO cdc_agent;
GRANT ALL ON t1 TO myuser;
CREATE PROCEDURE t1_ti_spl_get_seq_id()
REFERENCING NEW AS new_values FOR t1;
LET new_values.id = seq_id.NEXTVAL;
END PROCEDURE;
GRANT EXECUTE ON t1_ti_spl_get_seq_id TO cdc_agent;
GRANT EXECUTE ON t1_ti_spl_get_seq_id TO myuser;
CREATE TRIGGER t1_ti
INSERT ON t1 REFERENCING NEW AS new_ins
FOR EACH ROW WHEN ( USER <> "cdc_agent" )
(
EXECUTE PROCEDURE t1_ti_spl_get_seq_id() WITH TRIGGER REFERENCES
);
Inserting a few values into the table and checking the result:
-- with user "cdc_agent"
INSERT INTO t1( id, val1 ) VALUES ( 11, 'AAAA' );
INSERT INTO t1( id, val1 ) VALUES ( 13, 'AAAC' );
-- with user "myuser"
INSERT INTO t1( id, val1 ) VALUES ( 1, 'AAAB' );
INSERT INTO t1( id, val1 ) VALUES ( 3, 'AAAD' );
SELECT * FROM t1;
id val1
11 AAAA
13 AAAC
2 AAAB
4 AAAD

Copy master and detail table in stored procedure

I have two tables master / detail their DDL are:
Table1:
tbl1_id integer,
tbl1_sm_id integer,
tbl1_name varchar(30)
table2:
tbl2_id integer,
tbl1_id integer,
tbl2_name varchar(30)
table2.tbl1_id is the foreign key between the two tables, tbl1_id and tbl2_id are generated automatically from triggers.
I need to copy all records from these two tables where tbl1_sm_id = 2 into new value where tbl1_sm_id = 3.
I tried this SQL inside stored procedure:
For
select tbl1_id from table1
where tbl1_sm_id = 2
into :v_tbl1_id
do begin
insert into table1 (tbl1_sm_id, tbl1_name)
select 3, tbl1_name returning tbl1_id into :v_tbl1_id;
insert into table2 (tbl1_id, tbl2_name)
select :v_tbl1_id, tbl2_name
from table2 where tbl1_id = :v_tbl1_id;
end
Is this correct? Is there another way to copy the two tables?

Select value from an enumerated list in PostgreSQL

I want to select from an enumaration that is not in database.
E.g. SELECT id FROM my_table returns values like 1, 2, 3
I want to display 1 -> 'chocolate', 2 -> 'coconut', 3 -> 'pizza' etc. SELECT CASE works but is too complicated and hard to overview for many values. I think of something like
SELECT id, array['chocolate','coconut','pizza'][id] FROM my_table
But I couldn't succeed with arrays. Is there an easy solution? So this is a simple query, not a plpgsql script or something like that.
with food (fid, name) as (
values
(1, 'chocolate'),
(2, 'coconut'),
(3, 'pizza')
)
select t.id, f.name
from my_table t
join food f on f.fid = t.id;
or without a CTE (but using the same idea):
select t.id, f.name
from my_table t
join (
values
(1, 'chocolate'),
(2, 'coconut'),
(3, 'pizza')
) f (fid, name) on f.fid = t.id;
This is the correct syntax:
SELECT id, (array['chocolate','coconut','pizza'])[id] FROM my_table
But you should create a referenced table with those values.
What about creating another table that enumerate all cases, and do join ?
CREATE TABLE table_case
(
case_id bigserial NOT NULL,
case_name character varying,
CONSTRAINT table_case_pkey PRIMARY KEY (case_id)
)
WITH (
OIDS=FALSE
);
and when you select from your table:
SELECT id, case_name FROM my_table
inner join table_case on case_id=my_table_id;