Using a variable in GROUP BY clause - sql

I want to use a single value variable defined in a stored procedure in a GROUP BY query as follows:
CREATE TABLE test_table
(
id uuid NOT NULL,
no int4 NOT NULL
);
CREATE TABLE bigger_table
(
id uuid NOT NULL,
no int4 NOT NULL,
count int4 NOT NULL,
time timestamp NOT NULL
);
CREATE OR REPLACE PROCEDURE test()
LANGUAGE plpgsql
AS
$$
DECLARE
v_now timestamp;
BEGIN
v_now = now();
INSERT INTO bigger_table (no, count, time)
SELECT no, COUNT(*), v_now FROM test_table
GROUP BY no;
END
$$;
Here, v_now is effectively a constant and I know it, but Postgres doesn't so it wants it be either included in GROUP BY clause or in an aggregation function. How can I convince it to use the variable here?

Have you tried to put an alias on v_now and use this alias on group by?
CREATE TABLE test_table
(
id uuid NOT NULL,
no int4 NOT NULL
);
CREATE OR REPLACE PROCEDURE test()
LANGUAGE plpgsql
AS
$$
DECLARE
v_now timestamp;
BEGIN
v_now = now();
SELECT no, COUNT(*), now() v_now FROM test_table
GROUP BY no, v_now;
END
$$;

Yech. People love constraint. But...
You cannot INSERT INTO bigger_table (no, count, time) because You already defined id uuid NOT NULL !
CREATE OR REPLACE PROCEDURE test()
LANGUAGE plpgsql
AS
$$
DECLARE
v_now timestamp;
BEGIN
v_now := now();
INSERT INTO bigger_table (id,no, count, time)
SELECT gen_random_uuid(),
no,
COUNT(*) over(partition by no ),
v_now FROM test_table;
END
$$;
Don't use timestamp: https://wiki.postgresql.org/wiki/Don%27t_Do_This#Don.27t_use_timestamp_.28without_time_zone.29
now() return timestamp with time zone !
select pg_typeof(now()); return timestamp with time zone

Related

PostgreSQL: Trigger INSERT INTO SELECT from other table

I'm trying to create a trigger that will add a new row processed entry to a destination table each time a new row is created in the source table.
Step 1 Create destination table:
CREATE TABLE public.destination_table (
id serial PRIMARY KEY,
created_at TIMESTAMP NOT NULL,
sale_id INTEGER NOT NULL,
product_id INTEGER NOT NULL,
product_name VARCHAR NOT NULL,
url VARCHAR NOT NULL,
shop_id VARCHAR NOT NULL,
user_id VARCHAR)
Step 2 Create trigger function:
CREATE OR REPLACE FUNCTION triger_function() RETURNS TRIGGER AS
$BODY$
BEGIN
INSERT INTO public.destination_table ( created_at, sale_id, product_id, product_name, url, shop_id, user_id)
SELECT created_at,
sale_id,
product_id,
product_name,
split_part(url::text, '?'::text, 1) AS url,
shop_id,
((((((((data #>> '{}'::text[])::jsonb) #>> '{}'::text[])::jsonb) -> 'local_storage'::text) -> 'data'::text) #>> '{}'::text[])::jsonb) ->> 'user_id'::varchar AS user_id
FROM source_table;
RETURN new;
END;
$BODY$
language plpgsql;
** The Select query inside function work normally when single running.
Step 3 Create trigger:
CREATE TRIGGER trigger_records
AFTER INSERT ON public.source_table
FOR EACH ROW
EXECUTE PROCEDURE triger_function();
The problem is that Trigger does not work, which means it does not record new entries in the target table. Can't figure out where the error is.
You should be using the NEW record in the trigger function to reference the newly inserted data instead of a select, i.e.:
CREATE OR REPLACE FUNCTION triger_function() RETURNS TRIGGER AS
$BODY$
BEGIN
INSERT INTO public.destination_table ( created_at, sale_id, product_id, product_name, url, shop_id, user_id)
VALUES(NEW.created_at,
NEW.sale_id,
NEW.product_id,
NEW.product_name,
split_part(NEW.url::text, '?'::text, 1),
NEW.shop_id,
((((((((NEW.data #>> '{}'::text[])::jsonb) #>> '{}'::text[])::jsonb) -> 'local_storage'::text) -> 'data'::text) #>> '{}'::text[])::jsonb) ->> 'user_id'::varchar)
RETURN new;
END;
$BODY$
language plpgsql;

How to return multiple inserted rows from a Stored function with insert select statement?

Sample Problem with queries Link
Not able to get any inserted row in the RETURNING statement with INSERT and SELECT command even after adding RETURN NEXT; RETURN ;
SCHEMA FOR USER TABLE
create table "user" (name text not null, updated_time TIMESTAMP NOT
NULL DEFAULT CURRENT_TIMESTAMP );
function which needs to be updated to return inserted rows
CREATE OR REPLACE FUNCTION testFn()
RETURNS table (name character varying , updated_time timestamp without time zone ) AS $$ DECLARE BEGIN
insert into "user" (name , updated_time)
select 'alex',now()
union
select 'alex2',now()
returning name, updated_time;
END;
$$ LANGUAGE plpgsql;
This function only inserts into DB but doesnt return the inserted rows with updated time on calling the function
it return no output on
select * from testFn()
Make it a language sql function:
CREATE OR REPLACE FUNCTION testFn()
RETURNS table (name character varying , updated_time timestamp without time zone )
AS
$$
insert into "user" (name , updated_time)
values
('alex', now()),
('alex2', now())
returning "user".name, "user".updated_time;
$$
LANGUAGE sql;
With a language plpgsql you would need a return query, rather then just putting the insert into it.
CREATE OR REPLACE FUNCTION testFn()
RETURNS table (name character varying , updated_time timestamp without time zone )
AS
$$
begin
return query
insert into "user" (name , updated_time)
values
('alex', now()),
('alex2', now())
returning "user".name, "user".updated_time;
end;
$$
LANGUAGE plpgsql;
To run the function use:
select *
from testfn();
Online example
It is much simpler to do with a language sql function. Try this (based on the previous version of your function):
CREATE OR REPLACE FUNCTION sp_post_items(i_data json)
RETURNS table (fulfiller_id text,item_id text, order_id text, status_id integer, item_updated_time timestamp)
AS $function$
insert into vw_item_status_detail
(fulfiller_id ,item_id ,order_id , status_id, sku_code, decoration_technology, quantity, item_updated_time)
select
i_data->>'fulfillerId',
t->>'itemId',
i_data->>'orderId',
1000,
t->>'skuCode',
t->>'decorationTechnology',
10,
now()
from json_array_elements(i_data -> 'items') t
returning fulfiller_id, item_id, order_id, status_id, item_updated_time;
$function$
LANGUAGE sql;
or for the updated function:
CREATE OR REPLACE FUNCTION testFn()
RETURNS table (name text, updated_time timestamp without time zone ) AS
$$
insert into "user" (name , updated_time)
select 'alex',now()
union
select 'alex2',now()
returning name, updated_time;
$$ LANGUAGE sql;

Informix SELECT INTO syntax error

I've been trying to use code which finds the count of elements in a table and stores it in a local variable. I basically just want to check the existence of a record, so if there is any easier way to do this.
Here is an example I found of storing the result of a query in a variable (link):
CREATE FUNCTION checklist( d SMALLINT )
RETURNING VARCHAR(30), VARCHAR(12), INTEGER;
DEFINE name VARCHAR(30);
DEFINE dept VARCHAR(12);
DEFINE num INTEGER;
SELECT mgr_name, department,
CARDINALITY(direct_reports)
FROM manager INTO name, dept, num
WHERE dept_no = d;
IF num > 20 THEN
EXECUTE FUNCTION add_mgr(dept);
ELIF num = 0 THEN
EXECUTE FUNCTION del_mgr(dept);
ELSE
RETURN name, dept, num;
END IF;
END FUNCTION;
But when I try to create my own version of this, I get a syntax error. I have no idea what the problem is.
CREATE FUNCTION test ()
RETURNING INTEGER AS num1;
DEFINE l_count INTEGER;
CREATE TEMP TABLE t_queued_calls (
session_id DEC(18,0) PRIMARY KEY,
calling_number NVARCHAR(50)
) WITH NO LOG;
INSERT INTO t_queued_calls VALUES (123456, '5555555555');
SELECT COUNT(*) FROM t_queued_calls INTO l_count WHERE session_id = 123456;
DROP TABLE t_queued_calls;
END FUNCTION;
The position of the INTO clause is wrong in both functions. The INTO clause goes after the select-list (the list of expressions after the keyword SELECT) and before the FROM clause (see the Informix "Guide to SQL: Syntax" manual on the SELECT statement), as in this code:
CREATE PROCEDURE test()
RETURNING INTEGER AS num1;
DEFINE l_count INTEGER;
CREATE TEMP TABLE t_queued_calls (
session_id DEC(18,0) PRIMARY KEY,
calling_number NVARCHAR(50)
) WITH NO LOG;
INSERT INTO t_queued_calls VALUES (123456, '5555555555');
SELECT COUNT(*) INTO l_count FROM t_queued_calls WHERE session_id = 123456;
DROP TABLE t_queued_calls;
RETURN l_count;
END PROCEDURE;
Also, the first function as shown in the question has the same problem with the ordering of the clauses. Also, it does not always RETURN a value, and the original version of the second function never returns a value (although it says it will).
The could be related to the fact the insert dont have the columns name
adapt your_column1, your_column2 to your table schema
INSERT INTO t_queued_calls (your_column1, your_column2) VALUES (123456, '5555555555');
SELECT COUNT(*) FROM t_queued_calls INTO l_count WHERE session_id = 123456;
And/Or the number of column from the select don't match the number and type in insertt ... you select un field only but insert two field
and select into is strange select format ...normally is insert into but select don't use into clause
I found the correct syntax from this question: [Use MERGE statement in a stored procedure with Informix
SELECT COUNT(*) INTO l_count FROM t_queued_calls WHERE session_id = 123456;

How to return PK from insert query?

I have a postgresql table with sequence:
CREATE TABLE A (
id integer NOT NULL DEFAULT nextval('a_seq'::regclass),
X integer,
Y integer,
Z boolean default false,
CONSTRAINT A_pkey PRIMARY KEY (id)
)
I have an insert statment in function as follows:
insert into A(x,y) select $1,getdig();
i want this insert to return the id the row was given to a function varabile called A_id
It should be something like:
CREATE OR REPLACE FUNCTION bbb(m integer)
RETURNS integer AS
$BODY$
declare
A_id int;
begin
insert into A(x,y) select $1,getdig() RETURNING id into A_id;
actions using A_id like:
update A set z=True where id=A_id;
end;
$BODY$
LANGUAGE plpgsql VOLATILE
How do I do that?
There is no need for the select:
CREATE OR REPLACE FUNCTION bbb(m integer)
RETURNS integer AS
$BODY$
declare
A_id int;
begin
insert into A(x,y)
values ($1,getdig())
RETURNING id into A_id;
-- actions using A_id like:
update A set z=True where id=A_id;
return a_id; -- don't forget to return something!
end;
$BODY$
LANGUAGE plpgsql VOLATILE
You use the returning clause:
with i as (
insert into A(x,y)
select $1, getdig()
returning id
)
select *
from i;
Technically, the CTE is not necessary. But I prefer that queries that return values start with SELECT.

ERROR: query has no destination for result data

i have created a function in PostgreSQL to insert to the following
CREATE TABLE gtab83
(
orderid integer NOT NULL DEFAULT nextval('seq_gtab83_id'::regclass),
acid integer,
slno integer,
orderdte date
)
and created Function is
CREATE OR REPLACE FUNCTION funcInsert(iacid int,islno int,idate date) RETURNS int AS
$$
declare id_val int;
BEGIN
INSERT INTO GTAB83 (acid,slno,orderdte) VALUES (iacid,islno,idate) RETURNING orderid
into id_val;
return id_val;
END;
$$
LANGUAGE plpgsql;
when a execute the above function
select funcInsert(666,13,'2014-06-06'
getting this error
ERROR: query has no destination for result data
CONTEXT: PL/pgSQL function procgtab83(integer,integer,date) line 3 at SQL statement
create or replace function funcinsert(iacid int, islno int, idate date)
returns int as $$
declare id_val int;
begin
with i as (
insert into gtab83 (acid,slno,orderdte)
values (iacid,islno,idate)
returning orderid
)
select orderid into id_val
from i;
return id_val;
end;
$$ language plpgsql;
It can be much simpler as plain sql
create or replace function funcinsert(iacid int, islno int, idate date)
returns int as $$
insert into gtab83 (acid,slno,orderdte)
values (iacid,islno,idate)
returning orderid
;
$$ language sql;
This code is working:
postgres=# create table xx(a int);
CREATE TABLE
postgres=# create or replace function bu(int) returns int as
$$declare x int;
begin
insert into xx values($1) returning a into x;
return x;
end $$ language plpgsql;
CREATE FUNCTION
postgres=# select bu(10);
bu
────
10
(1 row)
And because it is same code as your, I expect, so you use some very old Postgres. I remember similar bug in pg, but it is more than five years fixed.