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

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 ;

Related

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;

Postgresql sequencer not found

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...

Transpose rows to columns where transposed column changes based on another column

I want to transpose the rows to columns using Pivot function in Oracle and/or SQL Server using Pivot function. My use case is very similar to this Efficiently convert rows to columns in sql server
However, I am organizing data by specific data type (below StringValue and NumericValue is shown).
This is my example:
----------------------------------------------------------------------
| Id | Person_ID | ColumnName | StringValue | NumericValue |
----------------------------------------------------------------------
| 1 | 1 | FirstName | John | (null) |
| 2 | 1 | Amount | (null) | 100 |
| 3 | 1 | PostalCode | (null) | 112334 |
| 4 | 1 | LastName | Smith | (null) |
| 5 | 1 | AccountNumber | (null) | 123456 |
----------------------------------------------------------------------
This is my result:
---------------------------------------------------------------------
| FirstName |Amount| PostalCode | LastName | AccountNumber |
---------------------------------------------------------------------
| John | 100 | 112334 | Smith | 123456 |
---------------------------------------------------------------------
How can I build the SQL Query?
I have already tried using MAX(DECODE()) and CASE statement in Oracle. However the performance is very poor. Looking to see if Pivot function in Oracle and/or SQL server can do this faster. Or should I go to single column value?
Below code will satisfy your requirement
Create table #test
(id int,
person_id int,
ColumnName varchar(50),
StringValue varchar(50),
numericValue varchar(50)
)
insert into #test values (1,1,'FirstName','John',null)
insert into #test values (2,1,'Amount',null,'100')
insert into #test values (3,1,'PostalCode',null,'112334')
insert into #test values (4,1,'LastName','Smith',null)
insert into #test values (5,1,'AccountNumber',null,'123456')
--select * from #test
Declare #Para varchar(max)='',
#Para1 varchar(max)='',
#main varchar(max)=''
select #Para += ','+QUOTENAME(ColumnName)
from (select distinct ColumnName from #test) as P
set #Para1= stuff(#para ,1,1,'')
print #Para1
set #main ='select * from (
select coalesce(StringValue,numericValue) as Val,ColumnName from #test) as Main
pivot
(
min(val) for ColumnName in ('+#Para1+')
) as pvt'
Exec(#main)

Postgresql - How to select multiple tables by specific columns and append them

I would like to select a number of tables and select the geometry (geom) and Name columns in each of the tables and append below each other. I have gotten as far as selecting the tables and their columns as shown below:
SELECT TABLE_NAME COLUMN_NAME
FROM INFORMATION_SCHEMA.columns
WHERE (TABLE_NAME LIKE '%HESA' OR
TABLE_NAME LIKE '%HEWH') AND
(COLUMN_NAME = 'geom' AND
COLUMN_NAME = 'Name');
How do you then take the tables:
id | geom | Name | id | geom | Name |
____________________ ____________________
1 | geom1 | Name1 | 1 | geom4 | Name4 |
2 | geom2 | Name2 | 2 | geom5 | Name5 |
3 | geom3 | Name3 | 3 | geom6 | Name6 |
And append the second table below the first, like this:
id | geom | Name |
____________________
1 | geom1 | Name1 |
2 | geom2 | Name2 |
3 | geom3 | Name3 |
1 | geom4 | Name4 |
2 | geom5 | Name5 |
3 | geom6 | Name6 |
Do I use UNION ALL or something else?
https://www.db-fiddle.com/f/75fgQMEWf9LvPj4xYMGWvA/0
based on your sample data:
do
'
declare
r record;
begin
for r in (
SELECT a.TABLE_NAME
FROM INFORMATION_SCHEMA.columns a
JOIN INFORMATION_SCHEMA.columns b on a.TABLE_NAME = b.TABLE_NAME and a.COLUMN_NAME = ''geom'' and b.COLUMN_NAME = ''name''
WHERE (a.TABLE_NAME LIKE ''oranges%'' OR a.TABLE_NAME LIKE ''%_db'')
) loop
execute format(''insert into rslt select geom, name from %I'',r.table_name);
end loop;
end;
'
;
Union All will do the job just fine:
SELECT
*
FROM (
(SELECT * FROM table_one)
UNION ALL
(SELECT * FROM table_two)
) AS tmp
ORDER BY name ASC;
I have added the external SELECT, to show you how you can order the whole result.
DB Fiddle can be found here

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