Can't use LOOP PostgreSQL - sql

I'm facing an issue from yesterday and I can't understand why my SQL is not working..
This may be a simple error since i'm a beginner in SQL but I can't find where it is.
Here is what I try to do:
CREATE FUNCTION test() RETURN integer AS $$
BEGIN
FOR i IN 1..5 LOOP
SELECT * from result WHERE id=i;
end loop;
RETURN 1;
END;
$$ LANGUAGE plpgsql;
This is just a simple loop as I can find in the documentation but I have this error:
Error report -
ERROR: syntax error at or near "RETURN" (this is the first RETURN statement in the function)
The database is in PostgreSQL and the version is 9.4.5
Why it's not working ?

There are several problems, apart from the fact that the function isn't doing anything useful:
It must be RETURNS integer, not RETURN integer.
That't what causes the error.
The SELECT has no destination. Either add an INTO clause or discard the result with
PERFORM * from result WHERE id=i;
You should indent the code correctly, so that you can read and understand it.

Related

PLSQL construction a new function

I need a help
I'm make a function in plsql but I'm beginner
Challenge: Web a label has (imput) in less than 72hours it will not be able to leave stock
Could you help me or tell me if I'm on the right path?
create or replace FUNCTION CHECACODENTRADA
(
P_CODPRO IN VARCHAR2
)
RETURN VARCHAR2
AS
v_database DATE;
BEGIN
v_database := TRUNC (SYSDATE);
IF (p_CODPRO == '08932010','08932030','08942020','08942010','08942310','08932210')
THEN
SELECT SYSDATE+3/72 FROM DUAL
IF SYSDATE+3/72 >=
Welcome to stack overflow. I suggest you start by writing a function shell and then adding functionality to it.
create or replace FUNCTION CHECACODENTRADA
(
P_CODPRO IN VARCHAR2
) RETURN VARCHAR2
AS
BEGIN
RETURN 'ok';
END;
/
Then the next step. Add code and compile. Fix any errors and continue. If you're a complete novice then don't write large chunks of code because it could be hard to figure out what is wrong.
How are you creating these functions - are you using a proper tool like sqldeveloper ? If not... well you should. Those tools make developing pl/sql a lot easier.
Read documentation, look for examples. pl/sql has its own syntax, don't assume that you can just borrow the syntax javascript or java uses... that will cause numerous errors. In your code, for example:
-- 2 errors in following line.
-- 1. The "==" is not valid oracle syntax
-- 2. What is this ? '08932010','08932030'... is that a list of arguments - how would the operator "==" handle this ? What are you expecting ?
IF (p_CODPRO == '08932010','08932030','08942020','08942010','08942310','08932210') THEN
-- the code below will not compile. You cannot "just select" in pl/sql, you need to SELECT INTO a variable.
SELECT SYSDATE+3/72 FROM DUAL
Some places you can start:
the source of it all: https://docs.oracle.com/en/database/oracle/oracle-database/18/lnpls/plsql-language-fundamentals.html#GUID-640DB3AA-15AF-4825-BD6C-1D4EB5AB7715
Google "pl/sql basics" and read up on it.
write small blocks in pl/sql to try things out. Use the sample schema emp/dept to have sample data everyone knows. You can get a database schema on apex.oracle.com (well that is an apex workspace but in the sql workshop you can do all the pl/sql you want) or use livesql.oracle.com
Other than that, you wrote your first code and asked questions about it - so you're definitely on the right track :)

unterminated dollar-quoted string postgress for creating function

this question might ask many here but I have tried their advice, but it still doesn't work on me, so I am going to ask this
I am trying to create a function where to trigger update_at automatically when i update something on tables
here is my syntax on .sql file:
CREATE OR REPLACE FUNCTION trigger_set_timestamp()
RETURNS TRIGGER AS $BODY$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$BODY$ LANGUAGE plpgsql;
i always got error:
unterminated dollar-quoted string at or near "$BODY$
BEGIN
NEW.updated_at = NOW()"
I have tried to change it to $$ but still doesn't work,
I am using Go for this case with the migration tool called sql-migrate
can anyone has a solution for this? I have tried from yesterday and I get really stuck now for this
It might come from your client, that does not recognize dollar quoting.
A typical workaround is to use regular single quotes. This requires double quoting all embedded single quotes, but fortunately you code has none, so:
CREATE OR REPLACE FUNCTION trigger_set_timestamp()
RETURNS TRIGGER AS '
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
' LANGUAGE plpgsql;

PLS-00221: 'SALE_TOTAL_AMOUNT' is not a procedure or is undefined

i created a function called sale_total_amount and it showed my that my function was created. now when i try to pass a parameter to do some calculations i get the error "not a procedure or is undefined"
Please find the error image here
I think the key sentence in your question is "showed my that my function was created"
A function returns a value, your call as it is written has nowhere for that value to go, hence the "Not a procedure" (it is a function) error.
You would need something more like
Declare
V_variable number default null;
begin
v_variable := sale_total_amount(1);
end;
This has the following assumptions
1) That it really is a function
2) That it returns a number (guessed from the name of your function)
3) That you are aware that you will not see any output, it will just complete successfully. If you want to see output you will need to add a dbms_output.put_line(v_variable) or similar to actually display something.
search for the function in the schema. You can search for that in user_procedeures or you can browse using some tool like SQL developer or toad.
My guess is that that either function does not exist or its signature (input/output type) is little different that what you are expecting
Please post output of this query:
select * from ALL_PROCEDURES where object_name = 'YOUR FUNCTION NAME';

Selecting large sequence value into global variable not working in PL/SQL code

I have a script where I would like to have a global variable to store a transaction number for later use. The code is working fine on one schema where the sequence value that I'm fetching is relatively low. It is not working on another schema with a higher sequence value where I get a "Numeric Overflow". If I change that sequence value to a lower number it is working as well but that is not an option.
VAR TRANSACTIONNR NUMBER;
BEGIN
--Works with NEXTVAL being around 946713241
--Doesn't work with NEXTVAL being around 2961725541
SELECT MY_SEQUENCE.NEXTVAL INTO :TRANSACTIONNR FROM DUAL;
MY_PACKAGE.STARTTRANSACTION(:TRANSACTIONNR);
END;
/
-- SQL Statements
BEGIN
MY_PACKAGE.ENDTRANSACTION;
MY_PACKAGE.DO_SOMETHING(:TRANSACTIONNR);
END;
/
What is also working is selecting the sequence into a variable declared in the DECLARE block:
DECLARE
TRANSACTIONNR NUMBER;
BEGIN
SELECT MY_SEQUENCE.NEXTVAL INTO TRANSACTIONNR FROM DUAL;
MY_PACKAGE.STARTTRANSACTION(TRANSACTIONNR);
END;
/
But that means I won't be able to reuse it in the block at the end. Setting the size of the number is not possible.
VAR TRANSACTIONNR NUMBER(15)
is not valid.
Any ideas what I could try or other ways to store global state?
On further investigation this looks like it might be a SQL Developer bug (making assumptions about what you're doing again, of course...). I can get the same error with:
VAR TRANSACTIONNR NUMBER;
BEGIN
SELECT 2961725541 INTO :TRANSACTIONNR FROM DUAL;
END;
/
It appears that SQL Developer's NUMBER is limited to 2^31, which isn't the case normally for Oracle.
A possibly workaround is to use BINARY_FLOAT to store the value, but you'll run into precision problems eventually (not sure where, but looks OK up to 2^53-ish), and you'll need to cast() it back to NUMBER when using it.
VAR TRANSACTIONNR BINARY_DOUBLE;
BEGIN
SELECT 2961725541 INTO :TRANSACTIONNR FROM DUAL;
-- dbms_output.put_line(cast(:TRANSACTIONNR as NUMBER)); -- null for some reason
END;
/
...
BEGIN
dbms_output.put_line(cast(:TRANSACTIONNR as NUMBER));
END;
/
For some reason I don't seem to be able to refer to the bind variable again in the anonymous block I set it in - it's null in the commented-out code - which seems to be another SQL Developer quirk, regardless of the var type; but as you were doing so in your code, I may again have assumed too much...
Original answer for posterity, as it may still be relevant in other circumstances...
Presumably you're doing something to end the current transaction, e.g. a commit in endtransaction; otherwise you could just refer to my_sequence.currval in the do_something call. A number variable is fine for this size of number though, it won't have a problem with a sequence that size, and it won't make any difference that it is from a sequence rather than manually assigned. I don't think the problem is with the storage or the sequence.
It seems rather more likely that the error is coming from one of the package procedures you're calling, though I can't quite imagine what you might be doing with it; something like this will cause the same error though:
create sequence my_sequence start with 2961725541;
create package my_package as
procedure starttransaction(v_num number);
procedure endtransaction;
procedure do_something(v_num number);
end my_package;
/
create package body my_package as
procedure starttransaction(v_num number) is
begin
dbms_output.put_line('starttransaction(): ' || v_num);
for i in 1..v_num loop
null;
end loop;
end starttransaction;
procedure endtransaction is
begin
dbms_output.put_line('endtransaction()');
end endtransaction;
procedure do_something(v_num number) is
begin
dbms_output.put_line('do_something(): ' || v_num);
end do_something;
end my_package;
/
When your code is run against that it throws your error:
BEGIN
*
ERROR at line 1:
ORA-01426: numeric overflow
ORA-06512: at "STACKOVERFLOW.MY_PACKAGE", line 6
ORA-06512: at line 5
endtransaction()
do_something():
Note the error is reported against line 6 in the package, which is the for ... loop line, not from the assignment in your anonymous block.
Looping like that would be an odd thing to do of course, but there are probably other ways to generate that error. The breakpoint for it working is if the nextval is above 2^31. If I start the sequence with 2147483647 it works, with 2147483648 it errors.
I'm assuming you are actually getting an ORA-01426 from the original question; if it's actually a ORA-1438 or ORA-06502 then it's easier to reproduce, by trying to assign the value to a number(9) column or variable. 'Numeric overflow' is pretty specific though.

Postgres: define a default value for CAST failures?

Is it possible to define a default value that will be returned in case a CAST operation fails?
For example, so that:
SELECT CAST('foo' AS INTEGER)
Will return a default value instead of throwing an error?
There is no default value for a CAST:
A type cast specifies a conversion from one data type to another. PostgreSQL accepts two equivalent syntaxes for type casts:
CAST ( expression AS type )
expression::type
There is no room in the syntax for anything other than the expression to be casted and the desired target type.
However, you can do it by hand with a simple function:
create or replace function cast_to_int(text, integer) returns integer as $$
begin
return cast($1 as integer);
exception
when invalid_text_representation then
return $2;
end;
$$ language plpgsql immutable;
Then you can say things like cast_to_int('pancakes', 0) and get 0.
PostgreSQL also lets you create your own casts so you could do things like this:
create or replace function cast_to_int(text) returns integer as $$
begin
-- Note the double casting to avoid infinite recursion.
return cast($1::varchar as integer);
exception
when invalid_text_representation then
return 0;
end;
$$ language plpgsql immutable;
create cast (text as integer) with function cast_to_int(text);
Then you could say
select cast('pancakes'::text as integer)
and get 0 or you could say
select cast(some_text_column as integer) from t
and get 0 for the some_text_column values that aren't valid integers. If you wanted to cast varchars using this auto-defaulting cast then you'd have to double cast:
select cast(some_varchar::text as integer) from t
Just because you can do this doesn't make it a good idea. I don't think replacing the standard text to integer cast is the best idea ever. The above approach also requires you to leave the standard varchar to integer cast alone, you could get around that if you wanted to do the whole conversion yourself rather than lazily punting to the built in casting.
NULL handling is left as an (easy) exercise for the reader.
Trap the error as described in documentation and then specify an action to do instead.
Documentation on error trapping for PostgreSQL Snippet included below.
35.7.5. Trapping Errors
By default, any error occurring in a PL/pgSQL function aborts execution of the function, and indeed of the surrounding transaction as well. You can trap errors and recover from them by using a BEGIN block with an EXCEPTION clause. The syntax is an extension of the normal syntax for a BEGIN block:
[ <<label>> ]
[ DECLARE
declarations ]
BEGIN
statements
EXCEPTION
WHEN condition [ OR condition ... ] THEN
handler_statements
[ WHEN condition [ OR condition ... ] THEN
handler_statements
... ]
END;
If no error occurs, this form of block simply executes all the statements, and then control passes to the next statement after END. But if an error occurs within the statements, further processing of the statements is abandoned, and control passes to the EXCEPTION list. The list is searched for the first condition matching the error that occurred. If a match is found, the corresponding handler_statements are executed, and then control passes to the next statement after END. If no match is found, the error propagates out as though the EXCEPTION clause were not there at all: the error can be caught by an enclosing block with EXCEPTION, or if there is none it aborts processing of the function.