parameter convert from sql server to oracle - sql

This parameter in SQL Server query - I copy to the Oracle query I am writing but it does not compile:
Compilation errors for PROCEDURE OGEN.DBD_NOT_GET_NOTES_DETAIL
Error: PLS-00103: Encountered the symbol "(" when expecting one of the
following:
:= . ) , # % default character
The symbol ":=" was substituted for "(" to continue. Line: 6 Text: , NOTETYPE NUMERIC(1) = 1
How can I code this in Oracle?
The complete SQL Server T-SQL query:
ALTER PROCEDURE [OEN].[DB_NOT_GET_NOTES_DETAIL]
(
#FACILITYKEY CHAR(4),
#DATEFROM DATETIME,
#DATETHRU DATETIME,
#UNITSTR VARCHAR(250),
#NOTETYPE NUMERIC(1) = 1
)
AS
BEGIN
SELECT P.FACILITY_KEY, P.PAT_NUMBER, P.PATIENT_ID,
OEN.DATEONLY(N.CREATED_ON) CREATED_ON, N.NOTE_HEADER,
N.CREATED_BY, P.LAST_NAME, P.FIRST_NAME, P.MIDDLE_NAME, P.UNIT_CODE
FROM OEN.EN_M_PATIENT_MAST P INNER JOIN OPTC.NOT_M_MAST N
ON (P.PAT_NUMBER = N.PAT_NUMBER AND N.FACILITY_KEY = #FACILITYKEY)
WHERE N.NOTE_STATUS = 0
AND (OEN.DATEONLY(N.CREATED_ON) BETWEEN OEN.DATEONLY(#DATEFROM) AND OEN.DATEONLY(#DATETHRU))
AND (#UNITSTR IS NULL OR #UNITSTR = '' OR CHARINDEX(P.UNIT_CODE, #UNITSTR) % 2 = 1)
AND #NOTETYPE = 1
END
The Oracle version:
CREATE OR REPLACE PROCEDURE OEN.DBD_NOT_GET_NOTES_DETAIL (
FACILITYKEY varchar2
, DATEFROM DATE
, DATETHRU DATE
, UNITSTR varchar2
, NOTETYPE NUMERIC(1) = 1
, OCURSOR OUT SYS_REFCURSOR
) as
BEGIN
OPEN OCURSOR FOR
SELECT P.FACILITY_KEY,
P.PAT_NUMBER,
P.PATIENT_ID,
OEN.DATEONLY(N.CREATED_ON) CREATED_ON, N.NOTE_HEADER,
N.CREATED_BY, P.LAST_NAME, P.FIRST_NAME, P.MIDDLE_NAME, P.UNIT_CODE
FROM OEN.EN_M_PATIENT_MAST P
INNER JOIN OPTC.NOT_M_MAST N ON (P.PAT_NUMBER = N.PAT_NUMBER AND N.FACILITY_KEY = FACILITYKEY)
WHERE N.NOTE_STATUS = 0
AND (OEN.DATEONLY(N.CREATED_ON) BETWEEN OEN.DATEONLY(DATEFROM) AND OEN.DATEONLY(DATETHRU))
AND CREATED_ON BETWEEN DATEFROM AND DATETHRU
AND (UNITSTR IS NULL OR P.UNIT_CODE = UNITSTR);
END;

Parameters to functions should not have length, scale, or precision. So the NOTETYPE parameter would need to be declared
NOTETYPE NUMERIC
If you want to assign a default value for a parameter, the syntax is
<<parameter declaration>> DEFAULT <<default value>>
Putting it together, your parameter declaration should be
, NOTETYPE NUMERIC DEFAULT 1
As a general stylistic matter, though this probably won't cause any errors
I would strongly suggest that parameters to procedures be anchored to an appropriate type in the data model. So, for example, FACILITYKEY OPTC.NOT_M_MAST.FACILITY_KEY%TYPE. That allows the parameter to adjust if in the future you need to do something like increase the length of a column.
I would strongly suggest adopting a naming convention for parameters that differentiates them from database columns. Prefixing the parameter names is a popular one (i.e. p_facility_key optc.not_m_mast.facility_key%type). Since you're not using the # prefix like you do in SQL Server, it is very easy to inadvertently have a parameter name that matches the name of a column in a table. Since name resolution gives preference to column names over local variables, that makes it very easy to write code that is inadvertently using a column rather than a local variable.
For example, this function will return every row in the EMP table.
CREATE OR REPLACE FUNCTION get_emps( empno IN emp.empno%type )
RETURN sys_refcursor
IS
l_rc sys_refcursor;
BEGIN
OPEN l_rc
FOR SELECT *
FROM emp e
WHERE e.empno = empno;
RETURN l_rc;
END;
Since the goal of this method is to return something to the caller, not to do a computation, it really ought to be declared as a FUNCTION rather than a PROCEDURE and ought to RETURN the sys_refcursor rather than having an OUT parameter.

My guess is you need
NOTETYPE NUMBER(1) := 1;

Related

Stored procedure can not be called due to syntax error

We migrated from SQL Server to Postgres and I am trying to rewrite a stored procedure. The procedure is created correctly, but I can not call it.
This is my procedure:
CREATE OR REPLACE PROCEDURE spr_getItems (
p_kind int = NULL,
p_customerId varchar(256) = NULL,
p_resourceIds varchar(2048) = NULL,
p_referenceIds varchar(2048) = NULL
)
AS $$
BEGIN
SELECT
c.kind,
c.name AS customerName,
c.oid AS customerId,
r.name AS resourceName,
r.oid AS resourceId
o.fullObject AS fullObjectString
FROM m_customer c
JOIN m_resource r
ON r.oid = c.resourceOid
LEFT JOIN m_object o
ON o.customerOid = c.oid
AND o.customerOid = p_customerId
WHERE (c.kind = p_kind OR p_kind is NULL)
AND (c.referenceOid IN (SELECT refTemp.oid FROM tvf_commaSeperatedStringToTable(p_referenceIds) refTemp) OR p_referenceIds is NULL)
AND (r.oid IN (SELECT resTemp.oid FROM tvf_commaSeperatedStringToTable(p_resourceIds) resTemp) OR p_resourceIds is NULL);
END;
$$
LANGUAGE 'plpgsql';
the table-valued-function tvf_commaSeperatedStringToTable just takes a string, splits it and returns a table with all of the different ids and a rownumber. It works just fine and is tested, no errors inside here.
Now when I try to execute it like this
CALL public.spr_getItems (0, null, null, null)
I get this output:
ERROR: query has no destination for result data
HINT: If you want to discard the results of a SELECT, use PERFORM instead.
CONTEXT: PL/pgSQL function spr_getItems(integer,character varying,character varying,character varying) line 3 at SQL statement
SQL state: 42601
But I do NOT want to discard the result, I want to see them.
So I tried calling it with select
SELECT *
FROM CALL spr_getItems (0, null, null, null)
and then I get this syntax error:
ERROR: syntax error at or near "0"
LINE 2: 0,
^
SQL state: 42601
Character: 40
I also tried executing it in several other way eg by adding the "public." before the procedures name, but then there has been a syntax error at the ".". Or with just using select spr_getItems(0, null, null, null) or select spr_getItems(0), select * from call spr_getItems (0) and so on and so forth.
Am I doing something completely wrong and overlooked something in the documentation?
Thanks for any help!
Edit: clarification that I want to see the results
Edit2: accidentally copied a wrong function name
Edit3: added complete body as suggested
That's not how Postgres works. Procedures aren't meant to return result sets.
If you want that use a set returning function:
CREATE OR REPLACE function spr_getItems (
p_kind int = NULL,
p_customerId varchar(256) = NULL,
p_resourceIds varchar(2048) = NULL,
p_referenceIds varchar(2048) = NULL
)
returns table (kind text, customername text, customerid integer, resourcename text, resourceid integer, fullobjectstring text)
AS $$
SELECT
c.kind,
c.name AS customerName,
c.oid AS customerId,
r.name AS resourceName,
r.oid AS resourceId
o.fullObject AS fullObjectString
FROM m_customer c
JOIN m_resource r
ON r.oid = c.resourceOid
LEFT JOIN m_object o
ON o.customerOid = c.oid
AND o.customerOid = p_customerId
WHERE (c.kind = p_kind OR p_kind is NULL)
AND (c.referenceOid IN (SELECT refTemp.oid FROM tvf_commaSeperatedStringToTable(p_referenceIds) refTemp) OR p_referenceIds is NULL)
AND (r.oid IN (SELECT resTemp.oid FROM tvf_commaSeperatedStringToTable(p_resourceIds) resTemp) OR p_resourceIds is NULL);
$$
LANGUAGE sql;
You also don't need PL/pgSQL for a simple query encapsulation, language sql will do just fine.
Then use it like a table:
select *
from spr_getitems(....);
Note that I guessed the data types in the returns table (...) part, you will have to adjust that to the real types used in your tables.
You don't need the sub-selects to handle the comma separated values either.
E.g. this:
AND (c.referenceOid IN (SELECT refTemp.oid FROM tvf_commaSeperatedStringToTable(p_referenceIds) refTemp) OR p_referenceIds is NULL)
can be simplified to
AND (c.referenceOid = any (string_to_array(p_referenceIds, ',') OR p_referenceIds is NULL)
But passing multiple values as a comma separated string is bad coding style to begin with. You should declare those parameters as array and pass proper arrays to the function.
The error refers to a function call (spr_getshadowrefs) inside the public.spr_getItems procedure. Perhaps you're trying to execute the spr_getshadowrefs function without putting the result in any variable.
Try to use PERFORM when you execute the spr_getshadowrefs function inside the public.spr_getItems procedure.
Have you tried
EXEC spr_getItems p_kind = 0,
p_customerId = NULL,
p_resourceIds = NULL,
p_referenceIds = NULL

stored procedure, handle possible null value

I have a very simple stored procedure which currently works perfectly when both parameters are sent values from form inputs.
However, I need to figure out what to do for IN_NUMBER if the value is empty because that column in the destination table is set to be nullable. It seems like the procedure itself is simply failing because it's waiting for a value.
What should I change?
IN parameters:
IN_NAME
IN_NUMBER
Routine:
P1 : BEGIN ATOMIC
INSERT INTO schema . postings
( name
, postNumber)
VALUES
( IN_NAME
, IN_NUMBER) ;
END P1
Example:
create table postings (name varchar(100), postNumber int) in userspace1#
create or replace procedure postings (
in_name varchar(100)
, in_number int
)
P1 : BEGIN ATOMIC
INSERT INTO postings
( name
, postNumber)
VALUES
( IN_NAME
, IN_NUMBER) ;
END P1#
call postings('myname', null)#
select * from postings#
NAME POSTNUMBER
---- ----------
myname <null>
There is no any problem here as you see.
What db2 error do you have exactly on a case similar to this?
If you want to handle NULL and replace it with some other value, use NVL(IN_NUMBER, 0) - you can exchange 0 for any other number of course (I'm assuming this is an integer).

Dynamic SQL (where) in Firebird stored procedure

I Have an SP that receive 2 parameters, P1 and P2, like this:
CREATE OR ALTER PROCEDURE MY_PROC (P1 varchar(10), P2 smallint = 1)
RETURNS (
code VARCHAR(10),
name VARCHAR(70),
state VARCHAR(2),
situation VARCHAR(20)
AS
...
...
And I need to generate the where clause based on the P2 parameter, like this:
if (P2=1) then
where (state='SP' and situation='stopped')
elseif (P2=2)
where (state='MG' and situation='moving')
How to use this type of if statement in where clause?
To me your question translates as a simple OR condition in the WHERE clause of a SQL query:
WHERE
(:P2 = 1 AND state='SP' and situation='stopped')
OR (:P2 = 2 AND state='MG' and situation='moving')
The answer of GMB will work fine for most situations, but in more complex cases it may have less desirable performance. An alternative solution would be to build a query string dynamically and execute it with execute statement:
CREATE OR ALTER PROCEDURE MY_PROC (P1 varchar(10), P2 smallint = 1)
RETURNS (
code VARCHAR(10),
name VARCHAR(70),
state VARCHAR(2),
situation VARCHAR(20)
AS
declare query varchar(2048);
begin
query = 'select ......';
if (p2 = 1) then
query = query || ' where (state=''SP'' and situation=''stopped'')';
else if (p2 = 2) then
query = query || ' where (state=''MG'' and situation=''moving'')';
-- if you expect a single result
execute statement query into code, name, state, situation;
-- OR
-- for multiple results
for execute statement query into code, name, state, situation do
suspend;
end

How to check only the first value of the row returned from stored procedure in a SQL query

If I have a stored procedure like this:
get_my_dep(empNum)
and it returns one row ex:
call get_my_dep(567);
dep_code dep_year dep_name
66 2017 HR
How can I check only the first value of the row (dep_code) in my query like this:
SELECT *
FROM rmcandidate a INNER JOIN task b
ON a.task_code = b.task_code
WHERE get_my_dep(emp_num) != 0 -- here I want to check only the dep_code
AND b.active_flag = 1
Presumably the stored procedure is defined as returning multiple values as in:
create procedure get_my_dep(emp_num int)
returning int as dep_code, int as dep_year, char(8) as dep_name;
In this case you could create a wrapper procedure that returns only one of the values and then use that in the WHERE clause. For example:
create procedure get_my_dep_code(emp_num int)
returning int as dep_code;
define dc, dy int;
define dn char(8);
execute procedure get_my_dep(emp_num) into dc, dy, dn;
return dc;
end procedure;
An alternative could be to define the procedure to return a row type. For example:
create row type dep_code_t(dep_code int, dep_year int, dep_name char(8));
create procedure get_my_dep(emp_num int)
returning dep_code_t;
define dc, dy int;
define dn char(8);
...
return row(dc, dy, dn)::dep_code_t;
end procedure;
It is then possible to directly reference an element of the returned row type in a WHERE clause as in:
WHERE get_my_dep(emp_num).dep_code != 0
You can try using a virtual table:
SELECT
*
FROM
rmcandidate AS a
INNER JOIN task AS b
ON
a.task_code = b.task_code
WHERE
b.active_flag = 1
AND 0 !=
(
SELECT
vt1.dep_code
FROM
TABLE (get_my_dep(emp_num)) AS vt1 (dep_code, dep_year, dep_name)
)
;
This was tested on Informix 12.10 .

How do I make a procedure that sums values stored in nested tables?

I have the following tables:
CREATE TABLE bodega ( --winery
id_bod INTEGER NOT NULL,
prod_an_bod nt_tipo_valor , --annual production
)
CREATE TABLE marca ( --wine
id_marca INTEGER NOT NULL,
prod_an_marca nt_tipo_valor , --annual production
)
CREATE TABLE presentacion ( --n:m relation table
id_pres INTEGER NOT NULL,
bodega_fk INTEGER NOT NULL,
marca_fk INTEGER NOT NULL
)
Both prod_an_marca in the table marca and prod_an_bod in the table bodega are nested tables of the following type:
CREATE OR REPLACE TYPE tipo_valor AS OBJECT (
ano DATE, --year
cantidad INTEGER --ammount of wine produced
)
I've made the following procedure which is supposed to retrieve the ammount of wine produced by any given winery in any given year, the purpose of this is to later insert that ammount into the nested table for production values in the winery, the way this works is through the n:m relationship table (presentacion), which stores a foreign key for (bodega) and a foreign key for (marca) wine.
I'm using a cursor that retrieves the production ammounts for a given year and sums them using SUM in the select, the problem is it sums every single production value that meets the search criteria, this means, it does retrieve the production values for all of the wines belonging to a winery but sums values from every year and not the specified year.
I've tried using GROUP BY to group the sum by year, which stores the proper value for each year stored in the cursor, this could work but I need a way to insert these into the nested table for the wineries' production figures, I'm not sure how I can do this, any help would be appreciated.
Create or replace procedure prueba(idbod INTEGER, ano DATE)
CURSOR prodbod IS
SELECT
sum(nt.cantidad)
FROM bodega b,
presentacion p,
marca m,
TABLE(m.prod_an_marca) nt
WHERE m.id_marca = p.marca_fk
AND b.id_bod = p.bodega_fk
AND b.id_bod = idbod
AND nt.ano = ano;
tempvar INTEGER;
BEGIN
OPEN prodbod;
LOOP
FETCH prodbod INTO tempvar;
EXIT WHEN prodbod%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('tempvar:'||to_char(tempvar));
END LOOP;
CLOSE prodbod;
END;
The issue is actually much simpler than it might appear from your description. Your query has:
AND nt.ano = ano
As stated in the documentation:
If a SQL statement references a name that belongs to both a column and either a local variable or formal parameter, then the column name takes precedence.
so you are effectively doing:
AND nt.ano = nt.ano
which is more obviously always true. You seem to have avoided the same issue by using slightly different names for id_bod and idbod, possibly accidentally. You can either explicitly state the second reference is the PL/SQL variable by prefixing it with the procedure name:
AND nt.ano = prueba.ano
or change your formal argument name. It's common to use prefixes to avoid this sort of confusion, e.g.:
Create or replace procedure prueba(p_id_bod INTEGER, p_ano DATE) as
...
AND b.id_bod = p_id_bod
AND nt.ano = p_ano;
...
As mentioned in comments, you should really be using explicit join syntax, and your example doesn't really need an explicit cursor, or a loop (or even any PL/SQL really - though I understand you will be expanding it); you can get the total with just:
Create or replace procedure prueba(p_idbod INTEGER, p_ano DATE) as
tempvar INTEGER;
BEGIN
SELECT sum(nt.cantidad)
INTO tempvar
FROM bodega b
JOIN presentacion p on p.bodega_fk = b.id_bod
JOIN marca m on m.id_marca = p.marca_fk
CROSS JOIN TABLE(m.prod_an_marca) nt
WHERE b.id_bod = p_id_bod
AND nt.ano = p_ano;
DBMS_OUTPUT.PUT_LINE('tempvar:'||to_char(tempvar));
END;
/