Postgresql sequencer not found - postgresql

I'm creating an item record number generator. The goal is to have a table to house all record number/sequencers for a variety of different types. For example, for a "Part" you may want a number like "110-00001-00". The seqItem table would hold the definition of this number generator (SeqName, preFix, postFix, padding).
InventorySys=# SELECT * FROM information_schema.sequences;
sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option
------------------+-----------------+---------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------+-----------+--------------
(0 rows)
InventorySys=# \d "SeqItem"
Table "public.SeqItem"
Column | Type | Collation | Nullable | Default
---------+---------+-----------+----------+---------
SeqName | text | | not null |
prefix | text | | |
postfix | text | | |
padding | integer | | not null | 5
Indexes:
"SeqItem_pkey" PRIMARY KEY, btree ("SeqName")
"SeqName" UNIQUE CONSTRAINT, btree ("SeqName")
Triggers:
dropsqeitem AFTER DELETE ON "SeqItem" FOR EACH ROW EXECUTE FUNCTION "RemoveSeq"()
inssqeitem AFTER INSERT ON "SeqItem" FOR EACH ROW EXECUTE FUNCTION "CreateSeq"()
InventorySys=#
When a new record is added to this table, I want to create a new Sequence with the "SeqName". So, I've created the following Trigger/Function:
CREATE OR REPLACE FUNCTION public."CreateSeq"() RETURNS TRIGGER as $CreateSeq$
BEGIN
EXECUTE format('CREATE SEQUENCE %I INCREMENT BY 1 MINVALUE 1 NO MAXVALUE START WITH 1 NO CYCLE', NEW."SeqName");
RETURN NEW;
END
$CreateSeq$ LANGUAGE plpgsql;
CREATE TRIGGER insSqeItem AFTER INSERT ON "SeqItem"
FOR EACH ROW EXECUTE FUNCTION "CreateSeq"();
This works perfect, and with each new record, I get a new sequencer created. I've also created a another function/trigger to delete the sequencer if the row is deleted.
CREATE OR REPLACE FUNCTION public."RemoveSeq"() RETURNS TRIGGER as $RemoveSeq$
BEGIN
EXECUTE format('DROP SEQUENCE IF EXISTS %I', OLD."SeqName");
RETURN NEW;
END
$RemoveSeq$ LANGUAGE plpgsql;
CREATE TRIGGER dropSqeItem AFTER DELETE ON "SeqItem"
FOR EACH ROW EXECUTE FUNCTION "RemoveSeq"();
So far so good! So, Let's add a new record and see that the Sequencer was added:
InventorySys=# INSERT into "SeqItem" ("SeqName", prefix, padding) Values ('testItem1', '115-',6);
INSERT 0 1
InventorySys=# SELECT * FROM "SeqItem";
SeqName | prefix | postfix | padding
-----------+--------+---------+---------
testItem1 | 115- | | 6
(1 row)
InventorySys=# SELECT * FROM information_schema.sequences;
sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option
------------------+-----------------+---------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+--------------
InventorySys | public | testItem1 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO
(1 row)
InventorySys=#
However, when I try to use the newly created sequencer from the trigger I get the following error that the sequencer is not found.
InventorySys=# select CONCAT("prefix", LPAD((select nextval("SeqItem"."SeqName"))::text, "padding", '0') , "postfix") from "SeqItem" where "SeqName" = 'testItem1' ;
ERROR: relation "testitem1" does not exist
InventorySys=#
ERROR: relation "testitem1" does not exist
If I create a new Sequencer without the Trigger, it works fine:
InventorySys=# CREATE SEQUENCE test1;
CREATE SEQUENCE
InventorySys=# SELECT NEXTVAL ('test1');
nextval
---------
1
(1 row)
InventorySys=#
And if I add that sequencer to my query, it works fine:
InventorySys=# select CONCAT("prefix", LPAD((select nextval('test1'))::text, "padding", '0') , "postfix") from "SeqItem" where "SeqName" = 'testItem1' ;
concat
------------
115-000002
(1 row)
InventorySys=#
Both sequencers look fine to me, but the one created by the Trigger I cannot get to work...
InventorySys=# SELECT * FROM information_schema.sequences;
sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option
------------------+-----------------+---------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+--------------
InventorySys | public | testItem1 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO
InventorySys | public | test1 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO
(2 rows)
InventorySys=#
Any help would be greatly appreciated!

Ok, I think I figured out my problem. It appears that the sequencer name needs to be all lower case? Or, I should say that if I use all lower case it works just fine...
InventorySys=# INSERT into "SeqItem" ("SeqName", prefix, padding) Values ('testitem3', '110-',4);
INSERT 0 1
InventorySys=# select CONCAT("prefix", LPAD((select nextval("SeqItem"."SeqName"))::text, "padding", '0') , "postfix") from "SeqItem" where "SeqName" = 'testitem3' ;
concat
----------
110-0001
(1 row)
InventorySys=# select CONCAT("prefix", LPAD((select nextval("SeqItem"."SeqName"))::text, "padding", '0') , "postfix") from "SeqItem" where "SeqName" = 'testitem3' ;
concat
----------
110-0002
(1 row)
InventorySys=#
I'm not sure why it will not accept upper and lower case characters...

Related

Could I make a constraint on inserting 'if the record is not contained in another table'?

I have 3 tables.
Table 'number':
| x |
|---|
| 1 |
| 2 |
| 3 |
Table 'group':
| group_id |
|----------|
| 1 |
| 2 |
Table 'number_in_group':
| group_id | x |
|----------|---|
| 1 | 4 |
| 1 | 5 |
| 2 | 4 |
| 2 | 5 |
| 2 | 7 |
Could I make a constraint on inserting into table number_in_group that x does not exist in table number?
If yes, is it a good approach, or better to put this business logic on the backend?
There is no constraint in Postgres that say make sure something does not exist, but you can create one - a trigger. In this case you select from numbers for the new value of x in number_in_group. If it exists then raise an exception. (see demo)
create or replace function not_if_in_numbers()
returns trigger
language plpgsql
as $$
begin
if exists
(select null
from numbers
where x = new.x
)
then
raise exception 'Invalid value for x (%). Found in numbers table.',new.x::text;
end if;
return new;
end;
$$;
create trigger check_restricted_x_biur
before insert or update of x
on number_in_group
for each row
execute function not_if_in_numbers();
You should create a similar trigger for numbers selecting from number_in_group and throws a corresponding exception. Do not build around the concept of an immutable table. It will not happen.

POSTGRESQL Subquery with a Order by Function does not return values

I have the following code that does not return values to the select because of my order by (Function)
Select sub.enrollmentseqnumemp ,sub.membercodedep,a.Subscriber_ID
From elan_staging.check_register_1 a
Left join (
Select enrollmentseqnumemp ,membercodedep
from elan.elig
ORDER BY public.idx(array['e','s','1','2','3','4','5'],membercodedep) Limit 1) sub
On sub.enrollmentseqnumemp=a.Subscriber_ID
| enrollmentseqnumemp | membercodedep | Subscriber_ID |
|:----------------------|:----------------|:----------------|
| [null] | [null] | "462852" |
| [null] | [null] | "462852" |
| [null] | [null] | "407742" |
If I run it without the Order By function, it works correctly
Select sub.enrollmentseqnumemp
,sub.membercodedep,a.Subscriber_ID
From elan_staging.check_register_1 a
Left join (
Select enrollmentseqnumemp ,membercodedep
from elan.elig
ORDER BY 1) sub
On sub.enrollmentseqnumemp=a.Subscriber_ID
Limit 1
| enrollmentseqnumemp | membercodedep | Subscriber_ID |
|:----------------------|:----------------|:----------------|
| 111111 | e | "462852" |
| 222222 | 3 | "462852" |
| 333333 | s | "407742" |
Code for the function from the Postgres snippets repository:
CREATE FUNCTION idx(anyarray varchar (1) ARRAY[4], anyelement varchar (1))
RETURNS int AS
$$
SELECT i FROM (
SELECT generate_series(array_lower($1,1),array_upper($1,1))
) g(i)
WHERE $1[i] = $2
LIMIT 1;
$$ LANGUAGE sql IMMUTABLE;
Is there a way to fix it so that it returns the values?
The first query as you have written it can return non-NULLs out of elig only for the one row with globally smallest value of public.idx(...). If you want values for the smallest public.idx(...) within each enrollmentseqnumemp, you could use distinct on, like :
Select sub.enrollmentseqnumemp ,sub.membercodedep,a.Subscriber_ID
From check_register_1 a
Left join (
Select distinct(enrollmentseqnumemp) enrollmentseqnumemp, membercodedep, public.idx(array['e','s','1','2','3','4','5'],membercodedep)
from elig
ORDER BY enrollmentseqnumemp, public.idx(array['e','s','1','2','3','4','5'],membercodedep)) sub
On sub.enrollmentseqnumemp=a.Subscriber_ID;

Issue while passing arrays to stored Function

trying to delete records by passing arrays into the stored function.
testing=# select * from links;
id | url | name | description | last_update
----+------------------------------------+---------------------+-------------+-------------
1 | https://www.postgresqltutorial.com | PostgreSQL Tutorial | |
2 | http://www.oreilly.com | O'Reilly Media | |
7 | http://www.postgresql.org | PostgreSQL | |
8 | https://www.google.com | Google | | 2013-06-01
(4 rows)
My Function
CREATE OR REPLACE FUNCTION testing(TEXT[])
RETURNS INTEGER AS
$BODY$
DECLARE emp_id INTEGER;
BEGIN
SELECT id into emp_id from links e where name = ANY($1);
DELETE FROM links WHERE id = emp_id;
return emp_id;
END
$BODY$
LANGUAGE plpgsql;
Query to call function
SELECT * from testing(ARRAY['PostgreSQL','Google']::TEXT[]);
We pass two records to delete from the links table, But instead of that only one record is deleting.
testing=# select * from links;
id | url | name | description | last_update
----+------------------------------------+---------------------+-------------+-------------
1 | https://www.postgresqltutorial.com | PostgreSQL Tutorial | |
2 | http://www.oreilly.com | O'Reilly Media | |
7 | http://www.postgresql.org | PostgreSQL | |
(3 rows)
SELECT ... INTO only stores the first row in the variable and ignores the rest.
You should declare the function as RETURNS SETOF integer, omit the SELECT and run only the DELETE as detailed in my other answer. Then you can use an SQL function rather than a PL/pgSQL one, which will simplify everything.

How to retrive Column name and datatype from \d in postgresql

I am new to Postgresql and working on a project which takes snapshot of relation.I want to retrive the first 2 column name and datatype from \d+ command in postgresql and then use this result to create a another table with only first 2 column
I am stuck on this . Can someone guide me on this ?
Column | Type | Modifiers | Storage | Stats target | Description
--------------+-----------------------------+------------------------------------------------------------+----------+--------------+-------------
i | integer | | plain | |
updated_time | timestamp without time zone | default '2000-01-01 00:00:00'::timestamp without time zone | plain | |
version | numeric | default '0'::numeric | main | |
is_updated | boolean | default false | plain | |
name | character varying(20) | | extended | |
I would just use plPgSql here, eg:
t=# do
$$
begin
execute format('create table so as select %s from pg_database',(select string_agg(column_name,',') from information_schema.columns where table_name = 'pg_database' and ordinal_position <=2));
end;
$$
;
DO
t=# \d so
Table "public.so"
Column | Type | Modifiers
---------+------+-----------
datname | name |
datdba | oid |
t=# \d pg_database
Table "pg_catalog.pg_database"
Column | Type | Modifiers
---------------+-----------+-----------
datname | name | not null
datdba | oid | not null
encoding | integer | not null
datcollate | name | not null
datctype | name | not null
datistemplate | boolean | not null
datallowconn | boolean | not null
datconnlimit | integer | not null
datlastsysoid | oid | not null
datfrozenxid | xid | not null
datminmxid | xid | not null
dattablespace | oid | not null
datacl | aclitem[] |
Indexes:
"pg_database_datname_index" UNIQUE, btree (datname), tablespace "pg_global"
"pg_database_oid_index" UNIQUE, btree (oid), tablespace "pg_global"
Tablespace: "pg_global"
update
the above is easily modifiable for other options if needed,eg:
t=# drop table so;
DROP TABLE
t=# do
$$
begin
execute format('create table so (%s) ',(select string_agg(column_name||' '||data_type||' '||case when is_nullable = 'NO' then 'NOT NULL' else '' end,',') from information_schema.columns where table_name = 'pg_database' and ordinal_position <=2));
end;
$$
;
DO
t=# \d so
Table "public.so"
Column | Type | Modifiers
---------+------+-----------
datname | name | not null
datdba | oid | not null
to include some modifiers...
update2
lastly if you want to use exact result from \d meta command - you can build you dinamic query from the one used by psql for \d:
-bash-4.2$ psql -E -c "\d pg_database"
********* QUERY **********
SELECT c.oid,
n.nspname,
...
and so forth

ERROR: missing FROM-clause entry for table when running function

I want to create a trigger and a function that do the following: Each time a new row is inserted in table SalesOrderDetail the function finds the corresponding CustomerID from table SalesOrderHeader and then it adds +1 to the corresponding number_of_sales in the Customer table.
SalesOrderDetail
+---------+-------------------------+
| SalesOrderID | SalesOrderDetailID |
+---------+-------------------------+
| value1 | value4 |
| value1 | value5 |
| value2 | value6 |
| value3 | value7 |
| value3 | value8 |
| value4 | value9 |
+---------+-------------------------+
SalesOrderHeader
+---------+-----------------+
| SalesOrderID | CustomerID |
+---------+-----------------+
| value1 | value10 |
| value2 | value11 |
| value3 | value12 |
| value4 | value13 |
+---------+-----------------+
Customer
+---------+--------------------+
| CustomerID | Number_of_sales |
+---------+--------------------+
| value10 | value14 |
| value11 | value15 |
| value12 | value16 |
| value13 | value17 |
+---------+--------------------+
Code is as follows:
CREATE OR REPLACE FUNCTION new_order_detail()
RETURNS trigger AS
$BODY$
BEGIN
DROP TABLE IF EXISTS CustomerInfo;
CREATE TEMP TABLE CustomerInfo AS
SELECT* FROM(SELECT CustomerID FROM(
SELECT * from SalesOrderHeader
WHERE SalesOrderHeader.SalesOrderID = (SELECT SalesOrderID FROM SalesOrderDetail ORDER BY SalesOrderID DESC limit 1))AS Last_Entry) AS Common_Element;
IF CustomerInfo.CustomerID = Customer.CustomerID THEN
UPDATE Customer
SET number_of_items = number_of_items + 1;
END IF;
END;
$BODY$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS new_order ON SalesOrderDetail;
CREATE TRIGGER new_order
AFTER INSERT OR UPDATE ON SalesOrderDetail
FOR EACH ROW EXECUTE PROCEDURE new_order_detail();
When I insert something into the SalesOrderDetail table I get the following error:
PL/pgSQL function new_order_detail() line 3 at SQL statement ERROR:
missing FROM-clause entry for table "customerinfo" LINE 1: SELECT
CustomerInfo.CustomerID = Customer.CustomerID
^ QUERY: SELECT CustomerInfo.CustomerID = Customer.CustomerID CONTEXT: PL/pgSQL function new_order_detail()
line 12 at IF
********** Error **********
ERROR: missing FROM-clause entry for table "customerinfo" SQL state:
42P01 Context: PL/pgSQL function new_order_detail() line 12 at IF
What I am doing wrong? Sorry for the poor explanation English is not my native language.
Firstly you need to take care about the case-sensitive pgsql. If you do not explicitly use "", it converts all to lower case.
Next, your procedure should be somewhat like this:
select CustomerID from SalesOrderHeader where SalesOrderDetail.SalesOrderID = SalesOrderHeader.SalesOrderID into custID;
UPDATE Customer SET number_of_items = number_of_items + 1 where CustomerID = custID ;