procedure return True or False - firebird

I need a stored procedure in firebird 3.0 to return True, False or 1,0 result
to check if this person or the record exists in database. If exists the procedure should return true, if not return False, or return 1, if not return 0
This is my procedure but it gives me an error:
Invalid token.
Dynamic SQL Error.
SQL error code = -104
Token unknown - line 10, column 1.
end.
I want to check if the record is exist or not by checking the first name last name and day of birth.
create procedure aa(
v varchar(20),
g varchar(20),
dd date)
as
begin
select fname,lname,bday from STUDENT
where not exists (select fname,lname,bday from STUDENT where fname=:v and lname=:g and bday=:dd)
end

Your stored procedure doesn't work because 1) it doesn't have a RETURNS clause so it cannot return true or false, and 2) a select in PSQL requires an INTO clause to put the values into a variable or return-parameter.
Based on your code, a stored procedure that does what you want would be:
create procedure aa(
v varchar(20),
g varchar(20),
dd date)
returns (student_exists boolean)
as
begin
student_exists = exists (select * from STUDENT where fname=:v and lname=:g and bday=:dd);
end
Depending on what you're need to achieve, a function might be more appropriate:
create function aa(
v varchar(20),
g varchar(20),
dd date)
returns boolean
as
begin
return exists (select * from STUDENT where fname=:v and lname=:g and bday=:dd);
end

Related

Duplication constraint in postgreSQL using (using GIST and or EXCLUDE)

How to write a constraint (table level unique, using GIST and or EXCLUDE ) to perform duplication record validation using the following rule:
Entered from_date and to_date values should not be equal or within the range, and
Employee_id should not be equal, and
After the validation, an error message should be return saying 'Duplicate Entry'.
This is in postgreSQL.
Note: I am new to postgreSQL (worked in MS SQL Server and MySQL).
Thanks in advance.
As stated by #Laurenz Albe, it sounds like impossible to do with a constraint. You can implement either a trigger function or a rule instead :
Trigger function :
CREATE OR REPLACE FUNCTION test_insert_table()
RETURNS trigger LANGUAGE plpgsql IMMUTABLE AS
$$
BEGIN
IF NEW.status = 'pending'
AND EXISTS
( SELECT 1
FROM your_table
WHERE Employee_id = NEW.Employee_id
AND range #> daterange(NEW.from_date, NEW.to_date)
)
THEN
RAISE EXCEPTION 'Duplicate Entry' ;
RETURN NULL ;
ELSE
RETURN NEW ;
END IF ;
END ;
$$
CREATE OR REPLACE TRIGGER test_insert_table
BEFORE INSERT ON your_table
FOR EACH ROW EXECUTE FUNCTION test_insert_table() ;
Rule :
CREATE OR REPLACE RULE test_insert AS
ON INSERT TO your_table
WHERE NEW.status = 'pending'
AND EXISTS
( SELECT 1
FROM your_table
WHERE Employee_id = NEW.Employee_id
AND range #> daterange(NEW.from_date, NEW.to_date)
)
DO INSTEAD NOTHING ;

CREATE TABLE AS is not allowed in a non-volatile function in Postgresql

I have wrote this method on postgresql 10 :
create or replace function get_users()
returns TABLE(user_idd uuid, device_idd text, shapee text , datee timestamp) AS
$$
begin
create temp table lines as
SELECT DISTINCT user_id, device_id from olocations;
select uuid_generate_v4(),
o1.user_id,
o1.device_id,
st_astext(ST_Collect(o1.shape)),
date(o1.creation_date_time) as date
from olocations o1
inner join lines on o1. device_id = lines.device_id and o1.user_id = lines.user_id
where o1.user_id = 'd0edfc59-9923-44c3-9c34-ef5aad3cb810'
and o1.device_id = '89984320001811791540'
group by o1.user_id, o1.device_id, date
order by date ASC ;
DROP TABLE lines;
end
$$
LANGUAGE 'plpgsql'
IMMUTABLE
SECURITY DEFINER
COST 100;
After create method without any problem, when i call my method:
select * from get_users();
I got this error:
sql> select from get_users()
[2018-09-30 17:23:23] [0A000] ERROR: CREATE TABLE AS is not allowed in a non-volatile function
[2018-09-30 17:23:23] Where: SQL statement "create temp table lines as
[2018-09-30 17:23:23] SELECT DISTINCT user_id, device_id from olocations"
[2018-09-30 17:23:23] PL/pgSQL function get_users() line 3 at SQL statement
I think i can not create table in method? right?
The function cannot be IMMUTABLE, define it as VOLATILE.
Per the documentation:
Any function with side-effects must be labeled VOLATILE, so that calls to it cannot be optimized away.
In this case this side-effect is the table creation.
Update.
Use return query to return rows generated by the query:
...
return query
select uuid_generate_v4(),
o1.user_id,
o1.device_id,
st_astext(ST_Collect(o1.shape)),
date(o1.creation_date_time) as date
...

PostgreSQL 11 - Procedures

With the latest update of PostgreSQL supporting procedures. The official blog, quoted that "As opposed to functions, procedures are not required to return a value." (https://blog.2ndquadrant.com/postgresql-11-server-side-procedures-part-1/)
So my question is, is there actually any way for me to return error code or response in a procedure? (Procedures is rather new in Postgres thus there were very little resources online.)
Here is an example of what I meant by returning these "error codes"
create or replace PROCEDURE multislot_Update_v1
(
p_id in varchar2,
p_name in varchar2,
p_enname in varchar2,
results out SYS_REFCURSOR
) AS
rowNumber int;
defaultNumber int;
BEGIN
select count(1) into rowNumber from MULTISLOTSGAME where fid=P_id;
if (rowNumber = 0) then
open results for
select '1' as result from dual;
return;
end if;
update MULTISLOTSGAME set
name = P_name,
enname = P_enname
where fid = P_id ;
commit;
open results for
select '0' as result, t1.* from MULTISLOTSGAME t1 where fid = p_id;
END multislot_Update_v1;
The above script is an Oracle procedure, as u can see if the returned result is "1" it meant that the update wasn't successful.
Is there any way I can write the above script (with error code) as a PostgresSQL Procedure ? Maybe an example of using the "INOUT" argument would be great!
You can have INOUT parameters in a procedure.
You call a procedure with the CALL statement; if there are any INOUT parameters, the statement will return a result row just like SELECT.
Here is an example that uses a procedure that returns a refcursor:
CREATE PROCEDURE testproc(INOUT r refcursor) LANGUAGE plpgsql AS
$$BEGIN
r := 'cur';
OPEN r FOR VALUES (1), (42), (12321);
END;$$;
BEGIN;
CALL testproc(NULL);
r
-----
cur
(1 row)
FETCH ALL FROM cur;
column1
---------
1
42
12321
(3 rows)
COMMIT;
How about using the RAISE statement?
https://www.postgresql.org/docs/10/static/plpgsql-errors-and-messages.html

Postgres stored procedure/function

New to Stored Procedures , have a requirement where I need to execute multiple queries inside stored procedure and return results. I would like to know whether this is possible or not ..
Ex :
Query 1 returns a list of userid ..
Select userid from user where username = ?
For each userid from the above query , I need to execute three different queries like
Query 2 select session_details from session where userid = ?
Query 3 select location from location where userid = ?
The return value should be a collection of , session_details and location.
Is this possible,can you provide some hints?
You can loop through query results like so:
FOR id IN Select userid from user where username = ?
LOOP
...
END LOOP;
As #Fahad Anjum says in his comment, its better if you can do it in a query. But if that's not posible, you have tree posibilities to achive what you want.
SETOF
TABLE
refcursor
1. SETOF
You can return a set of values. The set can be an existing table, a temporal table, or a TYPE you define.
TYPE example:
-- In your case the type could be (userid integer, session integer, location text)
CREATE TYPE tester AS (id integer);
-- The pl returns a SETOF the created type.
CREATE OR REPLACE FUNCTION test() RETURNS SETOF tester
AS $$
BEGIN
RETURN QUERY SELECT generate_series(1, 3) as id;
END;
$$ LANGUAGE plpgsql
-- Then, you get the set by selecting the PL as if it were a table.
SELECT * FROM test();
Table and Temp Table examples:
-- Create a temporal table o a regular table:
CREATE TEMP TABLE test_table(id integer);
-- or CREATE TABLE test_table(id integer);
-- or use an existing table in your schema(s);
-- The pl returns a SETOF the table you need
CREATE OR REPLACE FUNCTION test() RETURNS SETOF test_table
AS $$
BEGIN
RETURN QUERY SELECT generate_series(1, 3) as id;
END;
$$ LANGUAGE plpgsql
-- Then, you get the set by selecting the PL as if it were a table.
SELECT * FROM test();
-- NOTE: Since you are only returning a SETOF the table,
-- you don't insert any data into the table.
-- So, if you select the 'temp' table you won't see any changes.
SELECT * FROM test_table
2. TABLE
A PL can return a table, it would be similar to create a temporal table and then return a SETOF, but, in this case you declare de 'temp' table on the 'returns' sentence of the PL.
-- Next to TABLE you define the columns of the table the PL will return
CREATE OR REPLACE FUNCTION test() RETURNS TABLE (id integer)
AS $$
BEGIN
RETURN QUERY SELECT generate_series(1, 3) as id;
END;
$$ LANGUAGE plpgsql
-- As the other examples, you select the PL to get the data.
SELECT * FROM test();
3. refcursor
This one is the more complex solution. You return a cursor, not the actual data. If you need 'dynamic' values for your returning set, this is the solution.
But since you need static data, you won't need this option.
The use of any of these ways depends on any specific case, if you use regularly the userid,session,location in different ways and PLs, it would be better to Use the SETOF with a type.
If you have a table that has the userid,session,location columns, it's better to return a SETOF table.
If you just use the userid,session,location for one case, then it would be better to use a 'RETURNS TABLE' approach.
If you need to return a dynamic set you would have to use cursors... but that solution is really more advanced.
Based solely on your example, here's probably the easiest way to do it:
CREATE FUNCTION my_func(user_id INTEGER)
RETURNS TABLE (userid INTEGER, session INTEGER, location TEXT) AS
$$
SELECT u.userid, s.session, l.location
FROM -- etc... your query here
$$
LANGUAGE SQL STABLE;
Addressing comment:
That's a bit of a different question. One question is how to return multiple records containing multiple fields in a stored procedure. One way is as above.
The other question is how to write a query that gets data from multiple tables. Again, there are many ways to do it. One way is (again, based on my interpretation of your requirements in the examples):
SELECT userid
, ARRAY_AGG(SELECT session_details FROM session s WHERE s.userid = u.userid)
, ARRAY_AGG(SELECT l.location FROM location l WHERE l.userid = u.userid)
FROM user u
WHERE username = user_name
This will return one record containing the user_id, an array of session_details for that user, and an array of locations for that user.
Then the function can be changed to:
CREATE FUNCTION my_func(user_name TEXT, OUT userid INTEGER, OUT session_details TEXT[], OUT locations TEXT[])
AS $$
SELECT userid
, ARRAY(SELECT session_details FROM session s WHERE s.userid = u.userid)
, ARRAY(SELECT l.location FROM location l WHERE l.userid = u.userid)
FROM user u
WHERE username = user_name;
$$ LANGUAGE SQL STABLE;

Loop through the list of tables and check for a value in a field (DB2)

In DB2, I can get a list of tables with the following sql statement:
select tabname from syscat.tables where `tabschema = 'DBO'
Assuming that each table has a field named a1, how can I
loop through the tables and check for a value in that field
in every table?
There are two general ways. One would be to write a program that processes each file to check that column. The program could use embedded SQL to retrieve the count of the chosen value from each table. Or you could create a stored proc that accepts a table and schema name as inputs and sets an output value as essentially a boolean indicator of whether or not that table had the chosen value.
Potentially, you could perhaps create an outer proc to loop through the list of tables. And for each table it would call the inner proc that tests presence of the value.
This is a test proc that I used to verify the basic principle. It checks a column for APFILE='ACCPTH'. It returns either (1) or (0) depending on whether any row has that value or not.
-- Generate SQL
-- Version: V6R1M0 080215
-- Generated on: 03/22/14 02:59:07
-- Relational Database: TISI
-- Standards Option: DB2 for i
DROP SPECIFIC PROCEDURE SQLEXAMPLE.CHKFLDVAL ;
SET PATH "QSYS","QSYS2","SYSPROC","SYSIBMADM","mylib" ;
CREATE PROCEDURE SQLEXAMPLE.CHKFLDVAL (
IN TABLENAME VARCHAR(128) ,
IN SCHEMANAME VARCHAR(128) ,
OUT VALFOUND SMALLINT )
LANGUAGE SQL
SPECIFIC SQLEXAMPLE.CHKFLDVAL
NOT DETERMINISTIC
READS SQL DATA
CALLED ON NULL INPUT
SET OPTION ALWBLK = *ALLREAD ,
ALWCPYDTA = *OPTIMIZE ,
COMMIT = *NONE ,
CLOSQLCSR = *ENDMOD ,
DECRESULT = (31, 31, 00) ,
DFTRDBCOL = *NONE ,
DLYPRP = *NO ,
DYNDFTCOL = *NO ,
DYNUSRPRF = *USER ,
RDBCNNMTH = *RUW ,
SRTSEQ = *HEX
P1 : BEGIN
DECLARE STMTSQL VARCHAR ( 256 ) ;
DECLARE RTNRESULT SMALLINT ;
SET STMTSQL = 'VALUES (select CASE WHEN count(*) = 0 THEN 0 ELSE 1 END as chkVal from ' CONCAT SCHEMANAME CONCAT '.' CONCAT TABLENAME CONCAT ' where APFILE=''ACCPTH'' group by APFILE) INTO ?' ;
PREPARE STMT_NAME FROM STMTSQL ;
EXECUTE STMT_NAME USING RTNRESULT ;
SET VALFOUND = RTNRESULT ;
END P1 ;
COMMENT ON SPECIFIC PROCEDURE SQLEXAMPLE.CHKFLDVAL
IS 'Check field value in some table' ;
If I call it with a different TableName or SchemaName parameter value, I can get different values returned in rtnResult.
SQL is all that's actually needed. It's not a particularly good thing for SQL to do.
You cannot do this using just SQL statements. You will have to do a bit of scripting or programming of some sort to create new queries based on the table names you find and run them.