Database users loop with password generator - oracle

I have created some kind of code block till now, and I'm stuck. I'm selecting from table called FND_ORACLE_USERID for EBS database user schemas. Idea is to have those accounts printed in format like this (password is placeholder):
FNDCPASS user/password 0 Y system/test11 ALLORACLE password
Till now I have done this:
set serveroutput on;
DECLARE
c1 SYS_REFCURSOR;
l_pass varchar(16);
l_count number(3);
BEGIN
select count (*) into l_count from FND_ORACLE_USERID
where READ_ONLY_FLAG='A';
OPEN c1 for
select xmlagg(xmlelement("r", ch)).extract('//text()').getstringval() ch
from
(
select distinct first_value(ch) over (partition by lower(ch)) as ch
from (
select substr('abcd$efghijklmn#pqrstuvw#xyzABC$DEFGHIJK$LMNPQR!STUVWXYZ1!23456789',
level, 1) as ch
from dual
connect by level <= 59
order by dbms_random.value
)
where rownum <= dbms_random.value(17,17)
);
LOOP
FETCH c1
INTO l_pass;
EXIT WHEN c1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('FNDCPASS user/'||l_pass||' 0 Y system/test11 ALLORACLE '||l_pass||'');
END LOOP;
CLOSE c1;
END;
/
So I have started to count schemas, and I would like to stop printing new lines from above, when it reach end of count variable.
I'm not so good with plsql so please could you give some advice?
Thanks

Ok, i have created password function which returns string value for my password generator code now, like this :
create or replace function generic_passwd
return varchar2 is passwd VARCHAR2(17);
begin
select xmlagg(xmlelement("r", ch)).extract('//text()').getstringval() ch into passwd
from
(
select distinct first_value(ch) over (partition by lower(ch)) as ch
from (
select substr('abcd$efghijklmn#pqrstuvw#xyzABC$DEFGHIJK$LMNPQR!STUVWXYZ1!23456789',
level, 1) as ch
from dual
connect by level <= 59
order by dbms_random.value
)
where rownum <= dbms_random.value(17,17)
);
return passwd;
end;
/
Then i called this function from my code block and it is fine to me, like this:
set serveroutput on size 1000000
DECLARE
c1 SYS_REFCURSOR;
l_user varchar(10);
l_passd varchar(17);
BEGIN
OPEN c1 for
select oracle_username, generic_passwd() from FND_ORACLE_USERID where READ_ONLY_FLAG='A';
LOOP
FETCH c1
INTO l_user,l_passd;
EXIT WHEN c1%NOTFOUND ;
DBMS_OUTPUT.PUT_LINE('FNDCPASS '||l_user||'/'||l_passd||' 0 Y system/oracle11 ALLORACLE '||l_passd||'');
END LOOP;
CLOSE c1;
END;
/
Thank you for your time Jeff.

Found your own solution. Good on you. There are perhaps a couple inconsistencies, which may actually be intentional. But I think at least one or two are not:
The digits 3-9 can never appear in the output. You sub string
individual characters up to the 59th in the base string, however
that string is 66 characters in length. Perhaps the original base
string changed but the hard coded value did not. Intentional?
The output cannot have a repeated character, seems intentional, but
neither can it have a letter and the corresponding alternate case of
that letter. I.E cannot have both "a" and "A" in the output. Intentional?
The Expression: dbms_random.value(17,17) will always return 17, at least in a piratical since as rownum is always as integer.
So there is no reason to pay the function call overhead. Just use
the equivalent rownum <= 17;
Just a side note: This seems to be a limited use function. If that
is not the case then creating a standalone function is the way to
go. If, however this limited and it is version 12.1 or higher then
you may want to build the function directly into query using WITH
FUNCTION .... Doing so eliminate context switch between the
PL/SQL and SQL thus gaining performance.
The following addresses all these: See Demo But i reiterate. Except for #1 and #2 above what you have will work well.
with function generic_passwd --<<< define function directly as part of the query
return varchar2
is
l_generic_password varchar2(17);
begin
with base (stg) as
( select 'abcd$efghijklmn#pqrstuvw#xyzABC$DEFGHIJK$LMNPQR!STUVWXYZ1!23456789' from dual)
select xmlagg(xmlelement("r", ch)).extract('//text()').getstringval()
into l_generic_password
from
(
select distinct ch as ch --<< do not allow duplicate letters, but allow lower and upper of same letter
from (
select substr(stg,level, 1) as ch
from base
connect by level <= length(stg) --<< determine stop point from base string itself.
order by dbms_random.value
)
where rownum <= 17 --<< avoid subroutine overhead that always returns same value.
);
return l_generic_password;
end;
select username, generic_passwd() password
from fnd_oracle_userid;

Related

Flag duplicate characters in a string

I'm using oracle apex and i'm trying to write a pl/sql statement that will flag duplicates in a string. For example the string 'P,T,P,C' has two occurrences of the letter 'P' so it should raise an error. After all my digging the closest I got to achieving this was by using REGEXP_LIKE, but my regular expression skills are sub par. If anyone can assist that would be much appreciated.
DECLARE
v_seccode varchar2(10) := 'P,T,P,C';
BEGIN
if regexp_like(v_seccode, '(P{2,}?|C{2,}?|W{2,}?|T{2,}?)') then
raise_application_error(-20001,'You can not have the same SEC code listed more than once!');
end if;
END;
If your apex version is relatively recent you have access to the APEX_STRING API. Here is some code using that API, using same logic #Littlefoot showed in the other answer:
DECLARE
l_arr1 apex_t_varchar2;
l_arr2 apex_t_varchar2;
BEGIN
-- convert string to array with "," delimiter
l_arr1 := apex_string.split(:P1_SECCODE,',');
l_arr2 := l_arr1;
-- remove dupes from l_arr2
l_arr2 := l_arr2 MULTISET UNION DISTINCT l_arr2;
IF l_arr2 != l_arr1 THEN
return 'You can not have the same SEC code listed more than once!';
END IF;
END;
/
As you're using Apex, I suggest you create a validation within it. Code you posted suggests that you'd want to handle that elsewhere (process? Database trigger?).
Presuming that page item name is P1_SECCODE, validation - a PL/SQL function that returns error text - might look like this (read comments within code):
declare
l_seccode varchar2(20);
begin
-- Split P1_SECCODE into rows, fetch distinct values and aggregate them back.
-- If P1_SECCODE = 'P,T,P,C' then the L_SECCODE = 'P,T,C'
select listagg(distinct regexp_substr(:P1_SECCODE, '[^,]+', 1, level), ',')
within group (order by null)
into l_seccode
from dual
connect by level <= regexp_count(:P1_SECCODE, ',') + 1;
-- Now compare whether P1_SECCODE has the same number of commas as L_SECCODE.
-- If not, it means that P1_SECCODE contained duplicates so - return an error message
if regexp_count(:P1_SECCODE, ',') <> regexp_count(l_seccode, ',') then
return 'You can not have the same SEC code listed more than once!';
end if;
end;
If your database version doesn't support distinct within listagg, then first fetch distinct values and then aggregate them:
declare
l_seccode varchar2(20);
begin
-- Split P1_SECCODE into rows, fetch distinct values and aggregate them back.
-- If P1_SECCODE = 'P,T,P,C' then the L_SECCODE = 'P,T,C'
with temp as
(select distinct regexp_substr(:P1_SECCODE, '[^,]+', 1, level), ',') val
from dual
connect by level <= regexp_count(:P1_SECCODE, ',') + 1
)
select listagg(val, ',') within group (order by null)
into l_seccode
from temp;
-- Now compare whether P1_SECCODE has the same number of commas as L_SECCODE.
-- If not, it means that P1_SECCODE contained duplicates so - return an error message
if regexp_count(:P1_SECCODE, ',') <> regexp_count(l_seccode, ',') then
return 'You can not have the same SEC code listed more than once!';
end if;
end;
Given that your string can only have 4 possible letters PCWT, you could just use LIKE expressions along with a regular query:
SELECT *
FROM yourTable
WHERE string NOT LIKE '%P%P%' AND
string NOT LIKE '%C%C%' AND
string NOT LIKE '%W%W%' AND
string NOT LIKE '%T%T%';

Procedure to update column from function

I have a problem.
I need to create a procedure that fetches or_id loop from table1.
This id from table1 is a parameter calling function that returns two values d1 and d2.
The values d1 and d2 must be updated in table1.
How to do it? The function works correctly but I do not know how to loop it.
CURSOR rec_cur IS
SELECT s.or_id from table1 s;
id number;
a_BASKET_ID varchar(20);
a_ORDER_ID varchar (20);
BEGIN open rec_cur; loop
fetch rec_cur into number;
EXIT WHEN rec_cur%NOTFOUND;
SELECT cut(v_param,1,'#') a_BASKET_ID,
cut(v_param,2,'#') a_ORDER_ID
FROM (SELECT function1(or_id) v_param FROM dual);
UPDATE table1 b
SET BASKET_ID = a_BASKETID,
ORDER_ID = a_ORDERTYPE
WHERE b.or_id = s.or_id;
END LOOP;
This is one of the ugliest codes I've recently seen. Please, for your own sake (as well as ours), learn how to properly format & indent code and make it easier to read and follow.
Furthermore, it is invalid - lacks in DECLARE, there's no END, you declared some variables (a_basket_id, a_order_id) but used another ones (a_basketid, a_ordertype) ... quite a mess.
As of your question: as far as I understood, everything can be done in a single UPDATE statement, no PL/SQL is needed:
update table1 set
basket_id = cut(function1(or_id), 1, '#'),
order_id = cut(function1(or_id), 2, '#');
If you insist on PL/SQL, have a look at this: I've used cursor FOR loop as it is simpler to maintain than explicit cursor (as you don't have to create cursor variable(s), open the cursor, worry about exiting the loop, close the cursor - Oracle does it for you). Although you don't need local variables at all (nor PL/SQL, as I've already said), I let them be.
declare
a_basket_id table1.basket_id%type;
a_order_id table1.order_id%type;
begin
for cur_r in (select or_id from table1) loop
a_basket_id := cut(function1(cur_r.or_id), 1, '#');
a_order_id := cut(function1(cur_r.or_id), 2, '#');
update table1 set
basket_id = a_basket_id
order_id = a_order_id
where or_id = cur_r.or_id;
end loop;
end;

Oracle single row into variable

I need to get all result in one row... it is working but when i want to see it in dbms there is nothink.. why ?
CREATE OR REPLACE PROCEDURE NXMESEP.SP_IN_CHECK_AND_SEND_SMS
( RC_TABLE0 OUT SYS_REFCURSOR,
RS_CODE OUT VARCHAR2, -- RETURN 코드
RS_MSG OUT VARCHAR2
) IS ERROR_EXCEPTION EXCEPTION;
BEGIN
begin
DECLARE
promena varchar2(32767);
BEGIN
OPEN RC_TABLE0 FOR
SELECT listagg(ITEM_ID,', ') within group(order by ITEM_ID)
INTO promena
FROM TB_PL_M_WRKORD WRKOD
WHERE 1 = 1
AND WO_DATE = '20181012'
AND WRKOD.ITEM_ID NOT IN (SELECT ITEM_ID FROM TB_CM_M_FERT_COST_CHK FERT)
AND WC_ID = 'U';
LOOP
FETCH rc_table0 INTO promena;
EXIT WHEN rc_table0%NOTFOUND;
dbms_output.put_line(promena);
END LOOP;
CLOSE rc_table0;
end;
EXCEPTION
.... END;
RS_CODE := 'S'; RS_MSG := 'Complete successfully!';
RETURN; END SP_CHECK_AND_SEND_SMS; /
This should be promena that i expected..
" 12993NXUA, 13595NXUA, 14495NXUA, 16589NX, 16589NX, 16590NX, 16590NX, 16622NX, 16622NX "
Now it is working but im getting unknown error ORA-65535 every time when i execute. But after this I can see dbms result is ok.
Assuming your real code has RC_TABLE0 declared, as a ref cursor, then your variable ends up null because opening the cursor into something doesn't really do anything. You can't open a cursor and select something from the cursor query into a separate variable at the same time, whichever way round you try to do it. You need either a cursor, or a simple select ... into:
DECLARE
promena varchar2(32767);
BEGIN
SELECT listagg(ITEM_ID,', ') within group (order by ITEM_ID)
INTO promena
FROM TB_PL_M_WRKORD WRKOD
WHERE 1 = 1
AND WO_DATE = '20181012'
AND WRKOD.ITEM_ID NOT IN (SELECT ITEM_ID FROM TB_CM_M_FERT_COST_CHK FERT)
AND WC_ID = 'U';
dbms_output.put_line('test: '||promena);
END;
/
test: 12993NXUA, 13595NXUA, 14495NXUA ...
PL/SQL procedure successfully completed.
You also have to set serveroutput on or equivalent to actually see the results, of course.
I've also removed the redundant distinct, the unnecessary select .. from dual - which seemed to be part of the odd cursor construct - and the extra level of begin/end.
Incidentally, your code implies that wo_date is a string, which seems unlikely, or at least not ideal. If it is actually a real date then you should not be using a string for the comparison as you're forcing implicit conversions; use an actual date instead, maybe as an ANSI date literal:
AND WO_DATE = DATE '2018-10-12'
If you did really want to use an explicit cursor approach you would need to use a loop to populate the string variable:
DECLARE
promena varchar2(32767);
rc_table0 sys_refcursor;
BEGIN
OPEN rc_table0 FOR
SELECT DISTINCT listagg(ITEM_ID,', ') within group (order by ITEM_ID)
FROM TB_PL_M_WRKORD WRKOD
WHERE 1 = 1
AND WO_DATE = '20181012'
AND WRKOD.ITEM_ID NOT IN (SELECT ITEM_ID FROM TB_CM_M_FERT_COST_CHK FERT)
AND WC_ID = 'U';
LOOP
FETCH rc_table0 INTO promena;
EXIT WHEN rc_table0%NOTFOUND;
dbms_output.put_line('test: '||promena);
END LOOP;
CLOSE rc_table0;
END;
/
As you're only expecting a single row back there isn't much point doing that; and if you expected multiple rows (from a modified query, e.g. getting several days data and grouping by day) then an implicit cursor would be simpler anyway:
BEGIN
FOR r IN (
SELECT DISTINCT listagg(ITEM_ID,', ') within group (order by ITEM_ID) AS promena
FROM TB_PL_M_WRKORD WRKOD
WHERE 1 = 1
AND WO_DATE = '20181012'
AND WRKOD.ITEM_ID NOT IN (SELECT ITEM_ID FROM TB_CM_M_FERT_COST_CHK FERT)
AND WC_ID = 'U'
)
LOOP
dbms_output.put_line('test: '||r.promena);
END LOOP;
END;
/
If this is really part of a procedure and the rc_table0 is an OUT parameter then you just can't do this. In code you posted as an answer you tried:
OPEN RC_TABLE0 FOR
SELECT listagg(ITEM_ID,', ') within group(order by ITEM_ID)
INTO promena
FROM TB_PL_M_WRKORD WRKOD
...
In that construct the into is still ignored, because the open doesn't fetch anything. And if you loop and fetch inside your procedure to display the results as I did above then you are consuming the result set, so the caller will get no results (or "ORA-01001: invalid cursor" if you close it inside the procedure).
You just can't do both, unless you re-open the cursor, which seems like overhead you probably don't want...

Retrieve a select count(*) in Oracle inside a cursor

I need to retrieve the number of rows in a SELECT COUNT(*) statement that is inside a cursor (in Oracle).
The following code should explain it clearly:
PROCEDURE Save(CF_CURSOR OUT "VPA"."CF_#Runtime".CF_CURSOR_TYPE) AS
V_CF_CURSOR "VPA"."CF_#Runtime".CF_CURSOR_TYPE;
CF_ROWCOUNT NUMBER;
BEGIN
OPEN V_CF_CURSOR FOR
SELECT COUNT(*) INTO CF_ROWCOUNT FROM (
SELECT * FROM "VPA"."Employee" -- returns 1 row
) WHERE ROWNUM <= 1;
IF(CF_ROWCOUNT = 0) THEN
-- DO SOMETHING BUT NEVER GOES HERE
END IF;
COMMIT;
CF_CURSOR := V_CF_CURSOR;
END;
Here, the value of CF_ROWCOUNT is never set. If I remove the cursor, everything works as expected. I have tried to use SQL%ROWCOUNT, but it does not work either.
And, I cannot remove the cursor...
Thanks in advance!
Have you tried opening the cursor - which does a COUNT(*), then fetching that into the CF_ROWCOUNT variable instead of doing it as an INTO within the ref-cursor statement.
For example:
OPEN V_CF_CURSOR FOR SELECT COUNT(*) FROM "VPA"."Employee"; -- returns 1 row
FETCH V_CF_CURSOR INTO CF_ROWCOUNT;

SELECT COUNT(*) vs. fetching twice with an explicit cursor

I have read a book whose title is "Oracle PL SQL Programming" (2nd ed.) by Steven Feuerstein & Bill Pribyl. On page 99, there is a point suggested that
Do not "SELECT COUNT(*)" from a table unless you really need to know the total number of "hits." If you only need to know whether there is more than one match, simply fetch twice with an explicit cursor.
Could you anyone explain this point more to me by providing example? Thank you.
Update:
As Steven Feuerstein & Bill Pribyl recommends us not to use SELECT COUNT() to check whether records in a table exist or not, could anyone help me edit the code below in order to avoid using SELECT COUNT(*) by using explicit cursor instead? This code is written in the Oracle stored procedure.
I have a table emp(emp_id, emp_name, ...), so to check the provided employee ID corret or not:
CREATE OR REPLACE PROCEDURE do_sth ( emp_id_in IN emp.emp_id%TYPE )
IS
v_rows INTEGER;
BEGIN
...
SELECT COUNT(*) INTO v_rows
FROM emp
WHERE emp_id = emp_id_in;
IF v_rows > 0 THEN
/* do sth */
END;
/* more statements */
...
END do_sth;
There are a number of reasons why developers might perform select COUNT(*) from a table in a PL/SQL program:
1) They genuinely need to know how many rows there are in the table.
In this case there is no choice: select COUNT(*) and wait for the result. This will be pretty fast on many tables, but could take a while on a big table.
2) They just need to know whether a row exists or not.
This doesn't warrant counting all the rows in the table. A number of techniques are possible:
a) Explicit cursor method:
DECLARE
CURSOR c IS SELECT '1' dummy FROM mytable WHERE ...;
v VARCHAR2(1);
BEGIN
OPEN c;
FETCH c INTO v;
IF c%FOUND THEN
-- A row exists
...
ELSE
-- No row exists
...
END IF;
END;
b) SELECT INTO method
DECLARE
v VARCHAR2(1);
BEGIN
SELECT '1' INTO v FROM mytable
WHERE ...
AND ROWNUM=1; -- Stop fetching if 1 found
-- At least one row exists
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- No row exists
END;
c) SELECT COUNT(*) with ROWNUM method
DECLARE
cnt INTEGER;
BEGIN
SELECT COUNT(*) INTO cnt FROM mytable
WHERE ...
AND ROWNUM=1; -- Stop counting if 1 found
IF cnt = 0 THEN
-- No row found
ELSE
-- Row found
END IF;
END;
3) They need to know whether more than 1 row exists.
Variations on the techniques for (2) work:
a) Explicit cursor method:
DECLARE
CURSOR c IS SELECT '1' dummy FROM mytable WHERE ...;
v VARCHAR2(1);
BEGIN
OPEN c;
FETCH c INTO v;
FETCH c INTO v;
IF c%FOUND THEN
-- 2 or more rows exists
...
ELSE
-- 1 or 0 rows exist
...
END IF;
END;
b) SELECT INTO method
DECLARE
v VARCHAR2(1);
BEGIN
SELECT '1' INTO v FROM mytable
WHERE ... ;
-- Exactly 1 row exists
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- No row exists
WHEN TOO_MANY_ROWS THEN
-- More than 1 row exists
END;
c) SELECT COUNT(*) with ROWNUM method
DECLARE
cnt INTEGER;
BEGIN
SELECT COUNT(*) INTO cnt FROM mytable
WHERE ...
AND ROWNUM <= 2; -- Stop counting if 2 found
IF cnt = 0 THEN
-- No row found
IF cnt = 1 THEN
-- 1 row found
ELSE
-- More than 1 row found
END IF;
END;
Which method you use is largely a matter of preference (and some religious zealotry!) Steven Feuerstein has always favoured explicit cursors over implicit (SELECT INTO and cursor FOR loops); Tom Kyte favours implicit cursors (and I agree with him).
The important point is that to select COUNT(*) without restricting the ROWCOUNT is expensive and should therefore only be done when a count is trully needed.
As for your supplementary question about how to re-write this with an explicit cursor:
CREATE OR REPLACE PROCEDURE do_sth ( emp_id_in IN emp.emp_id%TYPE )
IS
v_rows INTEGER;
BEGIN
...
SELECT COUNT(*) INTO v_rows
FROM emp
WHERE emp_id = emp_id_in;
IF v_rows > 0 THEN
/* do sth */
END;
/* more statements */
...
END do_sth;
That would be:
CREATE OR REPLACE PROCEDURE do_sth ( emp_id_in IN emp.emp_id%TYPE )
IS
CURSOR c IS SELECT 1
FROM emp
WHERE emp_id = emp_id_in;
v_dummy INTEGER;
BEGIN
...
OPEN c;
FETCH c INTO v_dummy;
IF c%FOUND > 0 THEN
/* do sth */
END;
CLOSE c;
/* more statements */
...
END do_sth;
But really, in your example it is no better or worse, since you are selecting the primary key and Oracle is clever enough to know that it only needs to fetch once.
If two is all you are interested in, try
SELECT 'THERE ARE AT LEAST TWO ROWS IN THE TABLE'
FROM DUAL
WHERE 2 =
(
SELECT COUNT(*)
FROM TABLE
WHERE ROWNUM < 3
)
It will take less code than doing the manual cursor method,
and it is likely to be faster.
The rownum trick means to stop fetching rows once it has two of them.
If you don't put some sort of limit on the count(*), it could take a long while to finish, depending on the number of rows you have. In that case, using a cursor loop, to read 2 rows from the table manually, would be faster.
This comes from programmers writing code similar to the following (this is psuedo code!).
You want to check to see if the customer has more than one order:
if ((select count(*) from orders where customerid = :customerid) > 1)
{
....
}
That is a terribly inefficient way to do things. As Mark Brady would say, if you want to know if a jar contains pennies, would you count all the pennies in the jar, or just make sure there is 1 (or 2 in your example)?
This could be better written as:
if ((select 1 from (select 1 from orders where customerid = :customerid) where rownum = 2) == 1)
{
....
}
This prevents the "counting all of the coins" dilemma since Oracle will fetch 2 rows, then finish. The previous example would cause oracle to scan (an index or table) for ALL rows, then finish.
He means open a cursor and fetch not only the first record but the second, and then you will know there is more than one.
Since I never seem to need to know that SELECT COUNT(*) is >= 2, I have no idea why this is a useful idiom in any SQL variant. Either no records or at least one, sure, but not two or more. And anyway, there's always EXISTS.
That, and the fact that Oracle's optimizer seems to be pretty poor... - I would question the relevance of the technique.
To address TheSoftwareJedi's comments:
WITH CustomersWith2OrMoreOrders AS (
SELECT CustomerID
FROM Orders
GROUP BY CustomerID
HAVING COUNT(*) >= 2
)
SELECT Customer.*
FROM Customer
INNER JOIN CustomersWith2OrMoreOrders
ON Customer.CustomerID = CustomersWith2OrMoreOrders.CustomerID
Appropriately indexed, I've never had performance problems even with whole universe queries like this in SQL Server. However, I have consistently run into comments about Oracle optimizer problems here and on other sites.
My own experience with Oracle has not been good.
The comment from the OP appears to be saying that full COUNT(*) from tables are not well handled by the optimizer. i.e.:
IF EXISTS (SELECT COUNT(*) FROM table_name HAVING COUNT(*) >= 2)
BEGIN
END
(which, when a primary key exists, can be reduced to a simple index scan - in a case of extreme optimization, one can simply query the index metadata in sysindexes.rowcnt - to find the number of entries - all without a cursor) is to be generally avoided in favor of:
DECLARE CURSOR c IS SELECT something FROM table_name;
BEGIN
OPEN c
FETCH c INTO etc. x 2 and count rows and handle exceptions
END;
IF rc >= 2 THEN BEGIN
END
That, to me would result in less readable, less portable, and less maintainable code.
Before you take Steven Feuerstein's suggestions too serious, just do a little benchmark. Is count(*) noticeably slower than the explicit cursor in your case? No? Then better use the construct that allows for simple, readable code. Which, in most cases, would be "select count(*) into v_cnt ... if v_cnt>0 then ..."
PL/SQL allows for very readable programs. Don't waste that just to nano-optimize.
Depending on the DB, there may be a sys table which stores an approximate count and can be queried in constant time. Useful if you want to know whether the table has 20 rows or 20,000 or 20,000,000.
SQL Server:
if 2 = (
select count(*) from (
select top 2 * from (
select T = 1 union
select T = 2 union
select T = 3 ) t) t)
print 'At least two'
Also, don't ever use cursors. If you think you really really need them, beat yourself with a shovel until you change your mind. Let relics from an ancient past remain relics from an ancient past.
If you want to get number of rows in a table, please don't used count(*), I would suggest count(0) that 0 is the column index of your primary key column.

Resources