Execution of a query in a function plpgsql (POSTGIS) - sql

I'm looking for all the points that are inside a polygon. The query works for me, but when I add them to a function plggsql I get an error.
Query Ok
SELECT id, the_geom
FROM vialidad
WHERE ST_Contains('POLYGON((-99.1981315612793 19.357945,-99.1981315612793 19.376003,-99.161634 19.376003,-99.161634 19.357945,-99.1981315612793 19.357945))', ST_AsText(the_geom));
When I add the query to the function plpgsql and the polygon that was created is in polygon variable.
EXECUTE 'SELECT id,the_geom FROM vialidad WHERE ST_Contains('||polygon||',ST_AsText(vialidad_cdmx_vertices_pgr.the_geom));'
INTO nodes
USING polygon;
Console Error
QUERY: SELECT id, the_geom FROM vialidad WHERE ST_Contains(POLYGON((-99.1981315612793 19.357945,-99.1981315612793 19.376003,-99.161634 19.376003,-99.161634 19.357945,-99.1981315612793 19.357945)),ST_AsText(the_geom));
CONTEXT: PL/pgSQL function get_nodes_between_two_lines(integer,integer) line 37 at EXECUTE
********** Error **********
ERROR: syntax error at or near "19.357945"
SQL state: 42601
Many thanks in advance for any possible help.

Do not concatenate strings into your SQL query. You are already passing the variable to the execute command with the using keyword, but your query doesn't use a place holder.
EXECUTE 'SELECT id,the_geom FROM vialidad WHERE ST_Contains($1,ST_AsText(vialidad_cdmx_vertices_pgr.the_geom))'
-- ^ here
INTO nodes
USING polygon;
The fact that you are selecting two columns but only supply a single target variable with the INTO clause is highly suspicious.
But I don't think you need dynamic SQL at all, assuming polygon is a variable, the following should work just fine:
SELECT id, the_geom
into id_var, geom_var
FROM vialidad
WHERE ST_Contains(polygon,ST_AsText(vialidad_cdmx_vertices_pgr.the_geom))
Note that if you have a column named polygon in the table vialidad this is going to give you problems. You should rename your variable polygon then to have a different name. Many people simply put e.g. a l_ in front of the variable names, l_polygon or a p_ for parameters.

Seems like when run query separately, there is single quote with the first argument of st_contains
ST_Contains('POLYGON((-99.1981315612793 19.357945,-99.1981315612793 19.376003,-99.161634 19.376003,-99.161634 19.357945,-99.1981315612793 19.357945))', ST_AsText(the_geom));
But in console error there are no single quote around the polygon. You can try function with single quote inserted as
EXECUTE 'SELECT id,the_geom FROM vialidad WHERE ST_Contains('''||polygon||''',ST_AsText(vialidad_cdmx_vertices_pgr.the_geom));'
INTO nodes
USING polygon;

Related

Postgresql 11 - Create Procedure to Execute COPY function

I'm currently trying to create a procedure to automatically copy data into my database when I call the procedure. However, every time I call it I get the following error:
ERROR: column "name" does not exist
LINE 1: SELECT format('COPY test(%L) FROM %s CSV HEADER', name, '/Us...
How does the column not exist? Here's everything I've written out:
CREATE PROCEDURE
test_insert() AS
$$
BEGIN
EXECUTE format('COPY test(%L) FROM %s CSV HEADER', name, '/Users/Receiving.csv');
END;
$$ LANGUAGE plpgsql;
If you use name without single quotes, it is interpreted as a column name in the (tacit) SELECT statement
SELECT format('...', name, '...')
that PL/pgSQL runs when you execute your function.
Since this SELECT statement does not have a FROM clause, you get the observed error.
The solution is to use a string literal instead, i.e. write 'name' instead of 'name'.

Oracle UNISTR in SQL and PL/SQL

When I execute below query in SQL and in PL/SQL (stored procedure), both gives different results. Why is that?
Query: select UNISTR('\03A6') from dual;
Sql outputs Φ where as PL/SQL outputs F
I am using sql developer to execute both.
How can I make pl/sql return Φ
Function UNISTR expects a string as input parameter. Usually you should get error
ORA-00911: invalid character
when you run UNISTR(\03A6)
Try select UNISTR('\03A6') from dual; instead.

ORACLE PL/SQL Variable Function Scope - Need Explanation

I just tripped over an answer to a problem I was having with a PL/SQL variable not being recognized by a function and I was hoping someone could explain to me why my solution worked and what is happening "underneath the hood".
Background
As part of an optimization project, I am trying to collect metrics on individual SQL scripts within a Stored Procedure. The Stored Proc that I am dissecting has an In-type date parameter that I need to define in order to run each individual SQL Script:
CREATE OR REPLACE myStoredProc (DATE_IN DATE, ERROR_OUT OUT VARCHAR2)
IS
BEGIN
--Truncate Temp Tables
--6 Individual SQL Scripts
EXCEPTION
--Error Handling
END;
To run each script individually, I decided to just drop each SQL statement into a PL/SQL block and feed the DATE_IN parameter in as a variable:
DECLARE
DATE_IN DATE := TO_DATE('16-JUL-2014','DD-MON-RR');
BEGIN
--Place individual script here
END;
The Problem
This approach worked fine for a couple of the queries that referred to this DATE_IN variable but one query with a reference to an outside function which takes DATE_IN as a parameter started throwing an ORA-00904 error:
DECLARE
DATE_IN DATE := TO_DATE('16-JUL-2014','DD-MON-RR');
BEGIN
insert into temp_table
SELECT table1.field1,
table1.field2,
table2.fieldA,
MyFunction(table1.field1, DATE_IN) --This was the problem line
FROM
table1,
table2
WHERE EXISTS (inner query)
AND table1.keys = table2.keys
AND table2.date <= DATE_IN
END;
Solution
At the advice of another Developer, I was able to get around this error by adding a colon (:) in front of the DATE_IN variable that I was passing into the function so that the problem line read MyFunction(table1.field1, :DATE_IN). As soon as I did that, my error disappeared and I was able to run the query without issue.
I was happy for the result but the other Developer wasn't able to explain why it was needed, only that it was necessary to call any functions or other stored procs from a PL/SQL statement. I assume this has something to do with scope but I would like to get a better explanation as to why this colon was necessary for the function to see the variable.
Questions
I've tried to do a little research looking over Oracle documentation on parameters, variables, binding/declaring and constants but my research has only given me more questions:
After reading up on variables, I now question if that is the correct term for what I have been using (since I didn't actually use the VARIABLE command and I'm passing in a date - which is not an allowable data type). If my DATE_IN DATE := statement is not a variable, then what is it?
Why were the rest of my references to DATE_IN recognized by the compiler but passing the value to the function was out of scope?
What exactly is the colon (:) doing here? Is this turning that into a bind variable?
Thanks in advance. I appreciate any guidance you can provide!
----------------------------------EDIT--------------------------------------
I was asked to provide additional information. My Db version is 11G, 11.2.0.2.0. The query that I was able to reproduce this error is below.
DECLARE
EXTRACT_DT_IN DATE := TO_DATE('16-JUL-2014','DD-MON-RR');
BEGIN
--This begins the pre-optimized query that I'm testing
insert into AELI_COV_TMP_2_OPT
SELECT /*+ ordered use_nl(CM MAMT) INDEX (CM CSMB_CSMB2_UK) INDEX (MAMT (MBAM_CSMB_FK_I) */
CM.CASE_MBR_KEY
,CM.pyrl_no
,MAMT.AMT
,MAMT.FREQ_CD
,MAMT.HOURS
,aeli$cov_pdtodt(CM.CASE_MBR_KEY, EXTRACT_DT_IN)
FROM
CASE_MEMBERS CM
,MEMBER_AMOUNTS MAMT
WHERE EXISTS (select /*+ INDEX(SDEF SLRY_BCAT_FK_I) */
'x'
from SALARY_DEF SDEF
where SDEF.CASE_KEY = CM.CASE_KEY
AND SDEF.TYP_CD = '04'
AND SDEF.SLRY_KEY = MAMT.SLRY_KEY)
AND CM.CASE_MBR_KEY = MAMT.CASE_MBR_KEY
AND MAMT.STAT_CD = '00'
AND (MAMT.xpir_dt is null or MAMT.xpir_dt > EXTRACT_DT_IN)
AND MAMT.eff_dt <= EXTRACT_DT_IN;
--This ends the pre-optimized query that I'm testing
END;
Here is the error I'm encountering when trying to run an Explain Plan on this statement. I am able to get past this error if I remove reference to line 13 or I add a colon (:) to the EXTRACT_DT_IN on that line.
----------------------EDIT 2-------------------
Here is the function signature of aeli$.cov_pdtodt. (I've replaced the owner for security reasons).
CREATE OR REPLACE function __owner__.aeli$cov_pdtodt
(CASE_MBR_KEY_IN IN NUMBER, EXTRACT_EFF_DT_IN DATE)
RETURN DATE IS
PDTODT DATE;
Your anonymous block is fine as it is, as long as you execute the whole block. If you try to execute just the insert, or its select, as a standalone command then it will indeed fail with ORA-00904.
That isn't quite a scope problem, it's a context problem. You're trying to refer to a PL/SQL variable in an SQL context, and that is never going to work.
In a PL/SQL context this would work:
declare
some_var dual.dummy%type := 'X';
begin
insert into some_table
select dummy from dual where dummy = some_var;
end;
/
... because the insert has access to the PL/SQL some_var.
In an SQL context this will error:
select * from dual where dummy = some_var;
... because it's looking for a column called SOME_VAR, and there isn't one.
If you do this instead:
select * from dual where dummy = :some_var;
... the some_var is now a client-managed bind variable. If you execute that you'll either be prompted for the bind value, or given a not-all-variables-bound error, or bind-variable-not-declared, or similar, depending on your client.
If you only do an explain plan of that though, e.g. with
set auto trace traceonly explain
select * from dual where dummy = :some_var;
... then the bind variable doesn't necessarily have to be populated for the plan to be calculated. Some clients may still complain and want a bind value, but the parser would be OK with it - enough to produce a plan anyway. Though not able to take advantage of bind variable peeking or histograms etc.
For example, SQL Developer happily produces a plan for your original sample query if both references are turned into bind variables, just the insert ... part of the block is selected, and you press Explain Plan (F10).
I'm not sure what you read, but you're mixed up on a few things here.
Your DATE_IN is a variable. You don't need to type 'VARIABLE' anywhere to declare a variable, all you need is the name of the variable and the datatype.
All of the below are legitimate variables in PL/SQL (although poorly named).
variable_1 NUMBER;
variable_2 VARCHAR2(100);
variable_3 DATE;
It's hard to tell what you're doing in your code without seeing it all. Do you have two DATE_IN variables declared within the same block? Is DATE_IN the name of a column in your table?
If you have a column named DATE_IN in table1 or table2, that's likely your problem. Oracle doesn't know if you want to use your variable or your column, and it will always default to the column name. Your function would be expecting a DATE and receiving a column, hence the error.

PostgreSQL increase a table's sequence with one query

I want to unite the following two queries into one:
SELECT pg_get_serial_sequence('purchase_orders', 'id');
SELECT setval('purchase_orders_id_seq', 30000);
But if I place the upper SELECT into the setval's first parameter I get:
SELECT setval(SELECT pg_get_serial_sequence('purchase_orders', 'id'), 30000);
ERROR: syntax error at or near "SELECT"
SQL state: 42601
Character: 15
How can I pass on the select's result ("purchase_orders_id_seq") for setval?
EDIT: The reason for this is that; I want to use it like a function where a user only have to enter the table's name and a number to where sequence will be set.
FUNCTION set_id_sequence(TEXT table_name, INTEGER sequence);
If you want to pass a subquery result as a function argument, you need parentheses around it:
SELECT setval((SELECT pg_get_serial_sequence('purchase_orders', 'id')), 30000);
But in this case, the SELECT is redundant; you can invoke the function directly:
SELECT setval(pg_get_serial_sequence('purchase_orders', 'id'), 30000);
General function and automation
All "migration" or "starting database" SQL-script files have some controlled INSERT sequence before to use serial automation, so it need a simple command to say "ok, back to standard operation".
This generic operation is SELECT MAX(id)+1 FROM schemaName.tableName...
and, as #NickBarnes showed above, the basic setval() operation is setval(pg_get_serial_sequence('schemaName.tableName', 'idName'), NEWVAL), so putting all together we automate the task.
2018's standardization proposal
CREATE FUNCTION std_setmaxval(
p_tname text,
p_id_name text DEFAULT 'id'
) RETURNS SETOF bigint AS $f$
BEGIN
RETURN QUERY EXECUTE format(
'SELECT setval(pg_get_serial_sequence(%L,%L), COALESCE((SELECT MAX(%s)+1 FROM %s), 1) , false)'
,p_tname, p_id_name, p_id_name, p_tname
);
END
$f$ LANGUAGE PLpgSQL;
See quotation problem/solution to optimize. Please review this answer, it is a Wiki (!). And update the standard snippet proposal.
PS: I not understand why postgreSQL not offer a native function for this task... Well, I not see at info's Guide or sequence's Guide.
Change Sequence owner:
ALTER SEQUENCE purchase_orders_id_seq OWNED BY purchase_orders.id;
Set sequence value:
SELECT pg_catalog.setval('purchase_orders_id_seq', 30000, true);
ALTER TABLE ONLY purchase_orders ALTER COLUMN id SET DEFAULT
nextval('purchase_orders_id_seq'::regclass);

Oracle error : ORA-00905: Missing keyword

Excuting the line of SQL:
SELECT *
INTO assignment_20081120
FROM assignment ;
against a database in oracle to back up a table called assignment gives me the following ORACLE error:
ORA-00905: Missing keyword
Unless there is a single row in the ASSIGNMENT table and ASSIGNMENT_20081120 is a local PL/SQL variable of type ASSIGNMENT%ROWTYPE, this is not what you want.
Assuming you are trying to create a new table and copy the existing data to that new table
CREATE TABLE assignment_20081120
AS
SELECT *
FROM assignment
First, I thought:
"...In Microsoft SQL Server the
SELECT...INTO automatically creates
the new table whereas Oracle seems to
require you to manually create it
before executing the SELECT...INTO
statement..."
But after manually generating a table, it still did not work, still showing the "missing keyword" error.
So I gave up this time and solved it by first manually creating the table, then using the "classic" SELECT statement:
INSERT INTO assignment_20081120 SELECT * FROM assignment;
Which worked as expected. If anyone come up with an explanaition on how to use the SELECT...INTO in a correct way, I would be happy!
You can use select into inside of a PLSQL block such as below.
Declare
l_variable assignment%rowtype
begin
select *
into l_variable
from assignment;
exception
when no_data_found then
dbms_output.put_line('No record avialable')
when too_many_rows then
dbms_output.put_line('Too many rows')
end;
This code will only work when there is exactly 1 row in assignment. Usually you will use this kind of code to select a specific row identified by a key number.
Declare
l_variable assignment%rowtype
begin
select *
into l_variable
from assignment
where ID=<my id number>;
exception
when no_data_found then
dbms_output.put_line('No record avialable')
when too_many_rows then
dbms_output.put_line('Too many rows')
end;
Though this is not directly related to the OP's exact question but I just found out that using a Oracle reserved word in your query (in my case the alias IN) can cause the same error.
Example:
SELECT * FROM TBL_INDEPENTS IN
JOIN TBL_VOTERS VO on IN.VOTERID = VO.VOTERID
Or if its in the query itself as a field name
SELECT ..., ...., IN, ..., .... FROM SOMETABLE
That would also throw that error. I hope this helps someone.
If you backup a table in Oracle Database. You try the statement below.
CREATE TABLE name_table_bk
AS
SELECT *
FROM name_table;
I am using Oracle Database 12c.
Late answer, but I just came on this list today!
CREATE TABLE assignment_20101120 AS SELECT * FROM assignment;
Does the same.