Binding Variables in PL/SQL - oracle

I read this script that assigns of a data column info into 2 binding variables.
something like this:
EXEC SQL SELECT
var1
into :v.v1:v2
from table
Shouldn't there be a comma in there? Or is this like assigning var1 into v.v1 and also into v2 with the same values?

The above script would give error only. if you want to assign value comma is required for the same.
The syntax would be :- Ex if you want to fetch Empno,Ename,Deptno,salary from
EMPLOYEES.The plsql block would be as given below.
DECLARE
L_EMPNO NUMBER;
L_ENAME VARCHAR2(1000);
L_DEPTNO NUMBER;
L_SALARY NUMBER;
BEGIN
SELECT EMPNO, ENAME, DEPTNO, SALARY
INTO L_EMPNO, L_ENAME, L_DEPTNO, L_SALARY
FROM EMPLOYEES
WHERE EMPNO=100;
END;

This code is a snippet from a PRO*C program, a C program with embedded SQL.
v2 is an indicator variable. See here for info: https://docs.oracle.com/cd/B28359_01/appdev.111/b28427/pc_04dat.htm#i12463
An indicator variable will contain a value that relates to it's associated variable which in this case is v.v1 and is set after the operation in which it is used. In this case, after the select, you can test v2 and based on it's value it will tell you info about v.v1:
From the link above, if v2 equals:
0 - The operation was successful
-1 - A NULL was returned, inserted, or updated.
-2 - Output to a character host variable from a "long" type was truncated, but the original column length cannot be determined.
>0 - The result of a SELECT or FETCH into a character host variable was truncated. In this case, if the host variable is a multibyte character variable, the indicator value is the original column length in characters. If the host variable is not a multibye character variable, then the indicator length is the original column length in bytes.
I would suggest using it's other form, which would make things clear for the person that will maintain this after you (at least do that person a favor and comment this when you get your head around it). Always code for the person that will maintain after you. Don't you wish the person before you did that?!:
EXEC SQL SELECT
var1
into :v.v1 INDICATOR :v2
from table

Related

Writing a Version Number Function in PL/SQL

I want to write a function that will give me the next version number for a table. The table stores the existing version on each record. For example,
I have the cat table
cats
seqid 1
name Mr Smith
version number 1.2b.3.4
How can I write a program that will be able to increment these values based on various conditions?
This is my first attempt
if v_username is not null
then v_new_nbr = substr(v_cur_nbr, 1,7)||to_number(substr(v_cur_nbr, 8,1))+1
should be 1.2b.3.5
substr(v_cur_nbr, 1,7)||to_number(substr(v_cur_nbr, 8,1))+1
This hurls ORA-01722: invalid number. The reason is a subtle one. It seems Oracle applies the concatenation operator before the additions, so effectively you're adding one to the string '1.2b.3.4'.
One solution is using a TO_CHAR function to bracket the addition with the second substring before concatenating the result with the first substring:
substr(v_cur_nbr, 1,7) || to_char(to_number(substr(v_cur_nbr, 8,1))+1)
Working demo on db<>fiddle.
Incidentally, a key like this is a bad piece of data modelling. Smart keys are dumb. They always lead to horrible SQL (as you're finding) and risk data corruption. A proper model would have separate columns for each element of the version number. We can use virtual columns to concatenate the version number for display circumstances.
create table cats(
seqid number
,name varchar2(32)
,major_ver_no1 number
,major_ver_no2 number
,variant varchar2(1)
,minor_ver_no1 number
,minor_ver_no2 number
,v_cur_nbr varchar2(16) generated always as (to_char(major_ver_no1,'FM9') ||'.'||
to_char(major_ver_no2,'FM9') ||'.'||
variant ||'.'||
to_char(minor_ver_no1,'FM9') ||'.'||
to_char(minor_ver_no2,'FM9') ) );
So the set-up is a bit of a nause but incrementing the version numbers is a piece of cake.
update cats
set major_ver_no1 = major_ver_no1 +1
, major_ver_no2 = 0
, variant = 'a';
There's a db<>fiddle for that too.
Try searching mask for TO_NUMBER to be able to get the decimal number, this small example might help:
CREATE TABLE tmp_table (version varchar2(100));
INSERT INTO tmp_table(version) VALUES ('1.2b.3.4');
DECLARE
mainVersion NUMBER;
subVersion NUMBER;
currentVersion VARCHAR2(100);
BEGIN
SELECT version INTO currentVersion FROM tmp_table;
mainVersion := TO_NUMBER(SUBSTR(currentVersion,1,3),'9.9') + 0.1;
subVersion := TO_NUMBER(SUBSTR(currentVersion,6,3),'9.9') + 1.1;
UPDATE tmp_table SET version = (mainVersion||'b.'||subVersion);
END;

Call the retrieve procedure in Oracle with the primary key value

I have a table like this:
CST_ID CST_NAME CST_TYPE PYMENT_MODE TOTAL_PAYMENT
----------------------------------------------------
105 Jyothy Regular Full 15900
101 Alveena Regular half 15800
102 Nizam Regular Full 15500
100 Hari Regular Full 14500
103 Sharath Partime Full 17500
104 Jipsy Shifted Full 18000
I created a stored procedure for getting the row that I called from the procedure with the primary key valued field (here cst_id) as
CREATE or replace PROCEDURE sp_slct (cst_id NUMBER) AS
tot_id NUMBER;
--ls_name varchar2,
--ls_type varchar2,
--ls_mode varchar2,
--ls_pymnt int;
BEGIN
select
cst_id, cst_name, cst_type, payment_mode, total_payment into
ls_name, ls_type, ls_mode, ls_pymnt from tbl_cst
where
tbl_cst.cst_id = sp_slct.cst_id;
END sp_slct;
but I'm getting the errors in compilation
How do I get the result to select a row with the ID that I given as the input to the procedure?
You miss, at least, the IN keyword in the procedure parameter list, here how it should be.
Maybe you should declare also the vars in the INTO clause
CREATE or replace PROCEDURE sp_slct (cst_id IN NUMBER) AS
It would be great if you tell us your error Messages, that would improve the possibility to help. Until now your procedure doesnt do anything than to start a query.
You have two ways to use the result of a query. You could use cursors (if you want to search in some rows) or you can use "into" if you needs only one value. (as you did)
And if you want to get a result that matches to your Parameter list you must put your Parameters into the where- clause (as you did).
The only mistake I can see is the order: SELECT ... INTO ... FROM ...
Oracle documentation
And, of course, you must declare the variables (undo your comments)

Determining input datatype Oracle/PLSQL

I am writing a PLSQL 'INSTEAD OF INSERT' Trigger whereby the ID field (GID) can be inserted as either a string or a number. If the GID value is a string I would like to attempt to convert that into the correct GID (number) otherwise if a number is input the script will continue.
The part I am struggling with here is determining the datatype of ':New.CHART_GID' - is this possible in PLSQL? I can't check for chars in the string as the string may only contain numbers in some instances.
Thanks.
You can use TRANSLATE to check if there is something other as numbers:
CREATE OR REPLACE TRIGGER trigger_name
INSTEAD OF INSERT
ON table_name
FOR EACH ROW
DECLARE
vGID INTEGER;
...... other things
BEGIN
IF :New.CHART_GID is not null AND TRANSLATE(:New.CHART_GID,'0123456789',' ') is null THEN
vGID := TO_NUMBER(:New.CHART_GID);
.... do what you want with number
ELSE
... do what you want with not number
END IF;
.... other things
END;
CHART_GID have to be varchar2 in the view
I realise what I was trying to achieve was actually not possible. The solution for me was actually to join the Chart_no into the view and insert into either that field of the GID. If I input a Chart_no the GID field would be automatically populated and the same for if I input a GID.

"Invalid Cursor" error while running overloaded PLSQL stored procedure

I am creating an overloaded PLSQL stored procedure which allows to display the names of schools, their corresponding category (elementary, etc), and neighbourhood they belong to.
The names of schools is taken from table OTTAWASCHOOLS from the field NAME. The category is taken from the table OTTAWASCHOOLS from the field CATEGORY.
In addition, the user has the choice to input a particular neighbourhood to find the above information of the schools in that neighbourhood. The name of the neighbourhood is taken from the OTTAWANEIGHBOUR table from the field NAME.
However, if the user does NOT input a specific neighbourhood, the output will display the names ALL the schools in the OTTAWASCHOOLS table with their respective neighbourhoods and categories
(I have created only one procedure at the moment).
My code is as follows
SET SERVEROUTPUT ON;
SET VERIFY OFF
CREATE OR REPLACE PACKAGE schools_package
AS
PROCEDURE find_school
(neighbourhood_name IN OTTAWANEIGHBOUR.NAME%TYPE);
END schools_package;
/
CREATE OR REPLACE PACKAGE BODY schools_package
AS
PROCEDURE find_school
(neighbourhood_name IN OTTAWANEIGHBOUR.NAME%TYPE)
IS
school_category OTTAWASCHOOLS.CATEGORY%TYPE;
school_name OTTAWASCHOOLS.NAME%TYPE;
v_neighbourhood_name OTTAWANEIGHBOUR.NAME%TYPE;
CURSOR c_schools IS
SELECT NAME, CATEGORY
FROM eluliGDM.OTTAWASCHOOLS;
r_schools c_schools%ROWTYPE;
BEGIN
FOR r_schools IN c_schools
LOOP
SELECT c1.NAME, c2.NAME, c2.CATEGORY
INTO v_neighbourhood_name, school_name, school_category
FROM eluliGDM.OTTAWANEIGHBOUR c1, eluliGDM.OTTAWASCHOOLS c2
WHERE SDO_RELATE (c2.GEOMETRY, c1.GEOMETRY, 'MASK=INSIDE+COVEREDBY QUERYTYPE=JOIN') = 'TRUE'
AND c2.NAME=r_schools.NAME;
DBMS_OUTPUT.PUT_LINE ('NEIGHBOURHOOD ' || 'CATEGORY '|| 'SCHOOL NAME ');
DBMS_OUTPUT.PUT_LINE ('------------- ' || '-------- '|| '----------- ');
DBMS_OUTPUT.PUT_LINE (v_neighbourhood_name || school_category|| school_name);
END LOOP;
CLOSE c_schools;
END find_school;
END schools_package;
-----------TESTING STORED PROCEDURE---------------
Execute schools_package.find_school();
Execute schools_package.find_school('Mer Bleue');
But when I test the procedure, I get an error :01001. 00000 - "invalid cursor" then proceeds to show me ALL neighborhoods and their corresponding schools. What is wrong with my cursor?
Remove the CLOSE c_schools; statement. The Cursor For Loop already takes care of that. See Oracle Docs:
"The cursor FOR LOOP statement implicitly declares its loop index as a record variable of the row type that a specified cursor returns, and then opens a cursor. With each iteration, the cursor FOR LOOP statement fetches a row from the result set into the record. When there are no more rows to fetch, the cursor FOR LOOP statement closes the cursor."
According to your typing, OTTAWASCHOOLS contains both columns NAME and CATEGORY, so the cursor itself appears to be validly defined.
OTOH, does schema eluliGDM own both the tables and the package? If that is not the package owner, perhaps there are privilege issues? If the schema is the same, why specify the schema in the code? If not the same, consider the use of synonyms and removing the hard-coded schema from the code.
I'm not sure why you have an input parameter; you're not using it. So, I'm not surprised you're getting all the schools; the cursor has no predicate so it's the full table, and the SELECT inside the LOOP joins wherever the NAME column is the same in both tables. Without anything to limit based on input parameter, you have no filters at all beyond the join.
HTH

Inserting a record in PL/SQL block using for loop is yielding error

I am creating a table of inetegers called 'integer_properties' from 1 to 1000 the table columns are:
integer,isPrime,isOdd,isEven,digitCount;
I want to insert records using a for loop i tried following but the error says: 'missing SELECT keyword'
BEGIN
for k in 1..1000
loop
insert into integer_properties(integer,
isPrime,
isEven,
isOdd,
digitCount)
values(k,null,null,null,null);
end loop;
END;
It is tedious to enter 1000 numbers with DDL command without using PL/SQL block. I am trying to enter the loop variable values in the integer column. Is it possible to do that?
You can do it in a single query.
insert into integer_properties
select level, null, null, null, null
from dual
connect by level <= 1000;
commit;
LEVEL is a pseudocolumn used in hierarchical queries.
Your code seems fine, but using INTEGER as column name might be is causing problem. If you enclose it in double quotes, it will work fine. So, better to avoid using keywords while naming columns.

Resources