How to print a particular select statement three times in oracle? - oracle

This is the code
declare
a integer;
begin
select to_char(to_date('1/1/2017 ','mm/dd/yyyy') + level -1) into a from dual
connect by level <=365;
for a in 1..3 loop
dbms_output.put_line(a);
end loop;
end;
Error occurring is exact fetch requested more no of rows
Please help

You are trying to get multiple value in a scaler variable.
Try this:
begin
for i in (select to_char(to_date('1/1/2017 ','mm/dd/yyyy') + level -1) col
from dual
connect by level <=365) loop
for j in 1..3 loop
dbms_output.put_line(i.col);
end loop;
end loop;
end;
/

1st of all, please be aware about the fact that you declared the "a" variable as integer but then you assign a varchar2 to it.
2nd, I was testing your code and it seems that there is a problem with level <= 365.
I changed it into level = 365 and it seemed to return the expected result.
declare
a integer;
begin
select to_char(to_date('1/1/2017 ','mm/dd/yyyy') + level -1) into a from dual
-- connect by level <= 365;
connect by level = 365;
for a in 1..3 loop
dbms_output.put_line(a);
end loop;
end;

Related

Looping in Oracle

I need to loop in Oracle on monthly basis for 3 years. I am trying for daily basis first and below is my query but not sure about the error.
can you please help with this. I am fairly new to oracle.
DECLARE #StartDT DATE;
SET #StartDT = '20090101'
WHILE #StartDT <= '20090131'
BEGIN
PRINT CONVERT(VARCHAR,#StartDT) + '---' + convert(varchar,DATEADD(DAY,1,#StartDT))
END
What you posted is completely SQL Server syntax. In Oracle, you'd probably do this in SQL
select date '2009-01-01' + level
from dual
connect by level <= 31
If you want to use PL/SQL (and assuming you are using a tool that will display the output from dbms_output which you shouldn't assume will happen in prod), you could do something like this.
declare
l_dt date := date '2009-01-01';
begin
while( l_dt <= date '2009-01-31' )
loop
dbms_output.put_line( to_char( l_dt, 'yyyy-mm-dd' ) );
l_dt := l_dt + 1;
end loop;
end;
In oracle you can use for loop as following:
Begin
For d in (select date '2009-01-01' + level - 1 as dt
from dual
Connect by level <= add_months(date '2009-01-01', 3) - date '2009-01-01')
Loop
dbms_output.put_line(to_char(d.dt, 'yyyy-mm-dd'));
End loop;
End;
/
Cheers!!

ORA-01007 "variable not in select list" from dbms_sql.column_value call

I am trying to use dynamic SQL to sample all the data in a schema with a pattern:
DECLARE
xsql varchar2(5000);
c NUMBER;
d NUMBER;
col_cnt INTEGER;
f BOOLEAN;
rec_tab DBMS_SQL.DESC_TAB;
col_num NUMBER;
varvar varchar2(500);
PROCEDURE print_rec(rec in DBMS_SQL.DESC_REC) IS
BEGIN
DBMS_OUTPUT.ENABLE(1000000);
DBMS_OUTPUT.NEW_LINE;
DBMS_OUTPUT.PUT_LINE('col_type = '
|| rec.col_type);
DBMS_OUTPUT.PUT_LINE('col_maxlen = '
|| rec.col_max_len);
DBMS_OUTPUT.PUT_LINE('col_name = '
|| rec.col_name);
DBMS_OUTPUT.PUT_LINE('col_name_len = '
|| rec.col_name_len);
DBMS_OUTPUT.PUT_LINE('col_schema_name = '
|| rec.col_schema_name);
DBMS_OUTPUT.PUT_LINE('col_schema_name_len = '
|| rec.col_schema_name_len);
DBMS_OUTPUT.PUT_LINE('col_precision = '
|| rec.col_precision);
DBMS_OUTPUT.PUT_LINE('col_scale = '
|| rec.col_scale);
DBMS_OUTPUT.PUT('col_null_ok = ');
IF (rec.col_null_ok) THEN
DBMS_OUTPUT.PUT_LINE('true');
ELSE
DBMS_OUTPUT.PUT_LINE('false');
END IF;
END;
BEGIN
c := DBMS_SQL.OPEN_CURSOR;
xsql:='
WITH got_r_num AS
(
SELECT e.* -- or whatever columns you want
, ROW_NUMBER () OVER (ORDER BY dbms_random.value) AS r_num
FROM dba_tab_columns e
)
SELECT * -- or list all columns except r_num
FROM got_r_num
WHERE r_num <= 10';
DBMS_SQL.PARSE(c, xsql, DBMS_SQL.NATIVE);
d := DBMS_SQL.EXECUTE(c);
DBMS_SQL.DESCRIBE_COLUMNS(c, col_cnt, rec_tab);
LOOP
IF DBMS_SQL.FETCH_ROWS(c)>0 THEN
NULL;
-- get column values of the row
DBMS_SQL.COLUMN_VALUE(c, 2, varvar);
--dbms_output.put_line('varvar=');
--DBMS_SQL.COLUMN_VALUE(source_cursor, 2, name_var);
--DBMS_SQL.COLUMN_VALUE(source_cursor, 3, birthdate_var);
-- Bind the row into the cursor that inserts into the destination table. You
-- could alter this example to require the use of dynamic SQL by inserting an
-- if condition before the bind.
--DBMS_SQL.BIND_VARIABLE(destination_cursor, ':id_bind', id_var);
--DBMS_SQL.BIND_VARIABLE(destination_cursor, ':name_bind', name_var);
--DBMS_SQL.BIND_VARIABLE(destination_cursor, ':birthdate_bind',
--birthdate_var);
--ignore := DBMS_SQL.EXECUTE(destination_cursor);
--ELSE
-- No more rows to copy:
--EXIT;
END IF;
END LOOP;
--EXIT WHEN d != 10;
--END LOOP;
col_num := rec_tab.first;
IF (col_num IS NOT NULL) THEN
LOOP
print_rec(rec_tab(col_num));
col_num := rec_tab.next(col_num);
EXIT WHEN (col_num IS NULL);
END LOOP;
END IF;
DBMS_SQL.CLOSE_CURSOR(c);
END;
/
When I run that it gives me this error from the line with the dbms_sql.column_value call:
ORA-01007: variable not in select list
If I comment out that dbms_sql.column_value call it still errors but now with:
ORA-01002: fetch out of sequence
What am I doing wrong?
You have two problems in the code you posted. Firstly you have skipped part of the execution flow because you haven't called the DEFINE_COLUMN procedure. That is what is causing the ORA-01007 error, as the dynamic SQL processing hasn't been told about the select list columns via that call. For your current code you only need to define column 2, but assuming you will actually want to refer to the others you can define them in a loop. To treat them all as string for display you could do:
...
DBMS_SQL.PARSE(c, xsql, DBMS_SQL.NATIVE);
d := DBMS_SQL.EXECUTE(c);
DBMS_SQL.DESCRIBE_COLUMNS(c, col_cnt, rec_tab);
FOR i IN 1..col_cnt
LOOP
-- dbms_output.put_line('col_name is ' || rec_tab(i).col_name);
DBMS_SQL.DEFINE_COLUMN(c, i, varvar, 500);
END LOOP;
LOOP
IF DBMS_SQL.FETCH_ROWS(c)>0 THEN
...
If you want to do anything that needs to treat the variables as the right types you could have a local variable of each type and use the data type from the rec_tab information you already have from describe_columns to use the appropriately typed variable for each column.
The second problem, which you were hitting when you commented the column_value call, is still there once that definbe issue has been fixed. Your loop doesn't ever exit, so after you fetch the last row from the cursor you do a further invalid fetch, which throws ORA-01002. You have the code to avoid that already but it's commented out:
...
LOOP
IF DBMS_SQL.FETCH_ROWS(c)>0 THEN
-- get column values of the row
DBMS_SQL.COLUMN_VALUE(c, 2, varvar);
...
ELSE
-- No more rows to copy:
EXIT;
END IF;
END LOOP;
...
With those two changes your code runs, and dumps the view structure:
PL/SQL procedure successfully completed.
col_type = 1
col_maxlen = 30
col_name = OWNER
col_name_len = 5
col_schema_name =
col_schema_name_len = 0
col_precision = 0
col_scale = 0
col_null_ok = false
col_type = 1
col_maxlen = 30
col_name = TABLE_NAME
...
To those who find this question when accessing Oracle through ODP.NET, as I did:
We started getting this error whenever we would add column to an existing table in our application. I'm not sure what all the conditions were to make it fail, but ours were:
Run a SELECT * FROM "table".
Include a ROWNUM restriction in the WHERE clause (WHERE ROWNUM < 10).
Run that through the ODP.NET dataReader.GetSchemaTable() call.
Running unrestricted queries or running queries directly on Oracle SQL Developer did not seem to cause the error.
I've hit some pretty weird stuff in the past with Oracle connection pooling, so I eventually thought that could be the problem. The solution was to restart the web service to force all the connections to be fully dropped and recreated.
The theory is that the ODP.NET connection from the connection pool still had no idea the column existed on the table, but the column was returned by the database.

PL/SQL If statement, cursors, and a record set. How to use an IF statement after I use the Cursor?

I am wondering if I am going about this the right way.
My main issue that compiler gives is for this line
IF SELECT 1 FROM works WHERE an_employee.employee_name IN works.manager_name THEN
Error(17,8): PLS-00103: Encountered the symbol "SELECT" when expecting one of the following: ( - + case mod new not null continue avg count current exists max min prior sql stddev sum variance execute forall merge time timestamp interval date pipe
What I am trying to do is use a cursor that detects the first condition if they work in a company that is in that city. In this example in my table it will give 2 companies, and about 7 or 8 employees. Then I put that into my variable an_employee. What I am trying to do is use that employee then to see if they are a manager from my manages table which has multiple rows/tuples. Not everyone is a manager. How can I see if my employee_name is in the list of manager_name?
Do I declare another cursor for a manager and then do a nested loop?
Can I use my existing cursor and do a select query to get the list of managers from the table manages.manager_name? If I do that, how can I use it in the IF statement as my condition?
-- Give all employees that work in a company located in city X
-- a Y percent raise if they are managers and
-- a Z percent raise if they are not a manager
-- X, Y, and Z will be the three parameters for the stored procedure.
-- Build / compile a stored procedure
CREATE OR REPLACE PROCEDURE give_raises(X company.city%TYPE, Y NUMBER, Z NUMBER) IS
an_employee works.employee_name%TYPE;
-- cursor declaration
cursor Cursor1 IS
select works.employee_name
from works
where works.company_name IN (select company_name from company where city = 'London');
BEGIN
SELECT manager_name INTO managers FROM manages;
OPEN Cursor1;
LOOP
FETCH Cursor1 INTO an_employee;
EXIT WHEN Cursor1%NOTFOUND;
-- is a manager give Y percent raise
IF SELECT 1 FROM works WHERE an_employee.employee_name IN works.manager_name THEN
update works
set works.salary = works.salary + (works.salary * Y)
where works.employee_name = an_employee.employee_name;
ELSE -- is not a manager give Z percent raise
update works
set works.salary = works.salary + (works.salary * Z)
where works.employee_name = an_employee.employee_name;
END IF;
END LOOP;
CLOSE Cursor1;
END;
Also you should know. I am using Oracle, Oracle Sql Developer and the IDE.
If works on a logical condition and does not support Select statements in the condition.
You need to rework your code like this
SELECT count(*)
INTO v_value
FROM works
WHERE an_employee.employee_name = works.manager_name;
IF v_value = 1 THEN
--do some stuff
ELSE
--do some other stuff
END IF;
If i understand your problem correctly you are trying to identify whether an employee is manager or not based on thatsalary is computed. Hope below snippet helps.
CREATE OR REPLACE PROCEDURE give_raises(
X company.city%TYPE,
Y NUMBER,
Z NUMBER)
AS
managers PLS_INTEGER;
BEGIN
FOR an_employee IN
(SELECT works.employee_name
FROM works
WHERE works.company_name IN
(SELECT company_name FROM company WHERE city = 'London'
)
)
LOOP
SELECT COUNT(1)
INTO managers
FROM manages m
WHERE m.manager_name = an_employee.employee_name;
-- is a manager give Y percent raise
IF managers <> 0 THEN
UPDATE works
SET works.salary = works.salary + (works.salary * Y)
WHERE works.employee_name = an_employee.employee_name;
ELSE -- is not a manager give Z percent raise
UPDATE works
SET works.salary = works.salary + (works.salary * Z)
WHERE works.employee_name = an_employee.employee_name;
END IF;
END LOOP;
END;
Your problem is essentially the same than e.g. in a question posted yesterday. PL/SQL if statement expects a boolean expression not a query result set.
A good practice is to encapsulate the query to a function returning a suitable value.
Example:
SQL> !cat so53.sql
declare
function is_foo_1(p_foo in varchar2) return boolean is
v_exists number;
begin
select count(*)
into v_exists
from dual
where dummy = p_foo
and rownum = 1
;
return
case v_exists
when 1 then true
else false
end;
end;
function is_foo_2(p_foo in varchar2) return number is
v_exists number;
begin
select count(*)
into v_exists
from dual
where dummy = p_foo
and rownum = 1
;
return v_exists;
end;
begin
-- is_foo_1 returns a boolean value than is a valid boolean expression
if is_foo_1('X')
then
dbms_output.put_line('1:X');
end if;
if not is_foo_1('Y')
then
dbms_output.put_line('1:not Y');
end if;
-- is_foo_2 returns a number that is not a valid boolean expression (PL/SQL
-- doesn't have implicit type conversions) so one have to use an operator
-- (in this example `=`-operator) to construct an explicit boolean
-- expression
if is_foo_2('X') = 1
then
dbms_output.put_line('2:X');
end if;
if is_foo_2('Y') = 0
then
dbms_output.put_line('2:not Y');
end if;
end;
/
Example run:
SQL> #so53.sql
1:X
1:not Y
2:X
2:not Y
PL/SQL procedure successfully completed.
SQL>

Set a variable outside loop and fetch it every item of loop

My oracle version : Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bi
I'm trying to set a variable outside loop and make its value change as the each item of loop. (Just like we usually do in jsp, php...)
create or replace PROCEDURE TEST2 AS
-- VARIABLE
v_variable number;
cursor c2 is
select round(dbms_random.value() * 8) + 1 AS temp_key from dual; -- temp_key is a random value
BEGIN
OPEN c2;
FOR SRC IN (SELECT * FROM TB_MASTER_TEMP2) -- it has many rows
LOOP
fetch c2 into v_variable; -- v_variable need to change for every row
Dbms_Output.Put_Line(SRC.MAS_ENTRY_NM || ', ' ||v_variable); --test
END LOOP;
END TEST2;
But the result is
aaa, 8
bbb, 8 --`v_variable` stays the same
...
v_variable doesn't change.
Please correnct my procedure.
Unless someone has played silly buggers with it, dual has a single row, so the result set returned by c2 also has a single row. Any attempt to fetch beyond the end of that result set will simply return the last (and only) row over and over again.
If you want to retrieve a different random value on each iteration of the loop, you need to execute your SELECT ... FROM dual each time you loop, as in #Utsav's code.
Try this
create or replace PROCEDURE TEST2 AS
-- VARIABLE
v_variable number;
--cursor c2 is
--select round(dbms_random.value() * 8) + 1 AS temp_key from dual; -- temp_key is a random value
BEGIN
--OPEN c2;
FOR SRC IN (SELECT * FROM TB_MASTER_TEMP2) -- it has many rows
LOOP
--fetch c2 into v_variable; -- v_variable need to change for every row
select round(dbms_random.value() * 8) + 1 into v_variable from dual;
Dbms_Output.Put_Line(SRC.MAS_ENTRY_NM || ', ' ||v_variable); --test
END LOOP;
END TEST2;
Hello i have slightly tweaked your code. It may help you. Since i dont have workspace with me so plz bear with any syntax errors.
CREATE OR REPLACE PROCEDURE TEST2
AS
-- VARIABLE
v_variable NUMBER;
BEGIN
-- OPEN c2; -- Not required
FOR SRC IN
(SELECT t2.*,
ROUND(dbms_random.value() * 8) + 1 AS temp_key
FROM TB_MASTER_TEMP2 t2
) -- it has many rows
LOOP
-- v_variable need to change for every row
Dbms_Output.Put_Line(SRC.MAS_ENTRY_NM || ', ' ||src.temp_key); --test
END LOOP;
END TEST2;

If condition in PL/SQL script with cursor and loop

I would like to ask for some help or advice in this particular case.
I have table called "Teams". The table contains 3 columns - Issue, Responsible_team and More_Info (all varchar2).
I have a PL/SQL script with cursor and loop for selecting as many teams as issue description you type (some random word you think it might help you find the responsible team). This part works great for me.
But I do not know how to compile the IF condition in there. If no team is found according to typed word description, I would like to get some basic output dbms_output.put_line('Responsible team is not found').
There are 2 ways how I wrote the script. Classic loop and while loop.
I would be happy for any advice.
1.script
set verify off
DECLARE
v_issue teams.issue%type; --variable for issue column from teams table
v_respteam teams.responsible_team%type; --variable for responsible_team column from teams table
v_info teams.more_info%type; --variable for more_info column from teams table
--cursor declaration
CURSOR c_respteam
RETURN teams%ROWTYPE
IS
SELECT issue, responsible_team, more_info
FROM teams
WHERE lower(issue) like '%&Describe_Issue%';
BEGIN
OPEN c_respteam;
LOOP
FETCH c_respteam into v_issue, v_respteam, v_info;
EXIT when c_respteam%NOTFOUND;
dbms_output.put_line('Responsible team is '|| v_respteam || ' --> ' || v_info);
END LOOP;
CLOSE c_respteam;
end;
/
2.script
-- cursor with while loop
set verify off
DECLARE
v_issue teams.issue%type; --variable for issue column from teams table
v_respteam teams.responsible_team%type; --variable for responsible_team column from teams table
v_info teams.more_info%type; --variable for more_info column from teams table
CURSOR c_respteam
RETURN teams%ROWTYPE IS
SELECT issue, responsible_team, more_info
FROM teams
WHERE lower(issue) like '%&Describe_Issue%';
BEGIN
OPEN c_respteam;
FETCH c_respteam INTO v_issue, v_respteam, v_info;
WHILE c_respteam%FOUND
LOOP
dbms_output.put_line('Responsible team is '|| v_respteam || ' --> ' || v_info);
FETCH c_respteam INTO v_issue, v_respteam, v_info;
END LOOP;
CLOSE c_respteam;
END;
/
You could rewrite to:
declare
l_found boolean :=false;
cursor c_respteam is
select issue
,responsible_team
,more_info
from teams
where lower(issue) like '%&Describe_Issue%';
begin
for r in c_respteam
loop
l_found := true;
dbms_output.put_line('Responsible team is ' || r.responsible_team || ' --> ' || r.more_info);
end loop;
if not l_found
then
dbms_output.put_line('No records found');
end if;
end;
/
You need to have a counter variable [ETA: ooh, I like Rene's boolean variable idea instead; either way, you need an extra variable!] to work out if any rows were returned or not. I'm not sure why you're using an explicit cursor fetch, rather than using the cursor-for-loop? Cursor-for-loops are not only easier to write, read and maintain, but Oracle have put some behind-the-scenes optimisation in, to aid performance.
Of course, depending on what you're actually doing with the data returned by your cursor (dbms_output.put_line being something that you should never have in your production code), it's debatable that you would need to loop through a cursor at all.
Anyway, with that said, here's an example demonstrating how I would handle your requirement to check for no rows returned by the cursor:
declare
cursor cur (p_val varchar2)
is
select dummy
from dual
where dummy like '%'||p_val||'%';
v_counter integer := 0;
begin
for rec in cur('Y')
loop
dbms_output.put_line('value of dummy = '||rec.dummy);
v_counter := v_counter + 1;
end loop;
if v_counter = 0 then
dbms_output.put_line('no rows returned');
end if;
end;
/
no rows returned
declare
cursor cur (p_val varchar2)
is
select dummy
from dual
where dummy like '%'||p_val||'%';
v_counter integer := 0;
begin
for rec in cur('X')
loop
dbms_output.put_line('value of dummy = '||rec.dummy);
v_counter := v_counter + 1;
end loop;
if v_counter = 0 then
dbms_output.put_line('no rows returned');
end if;
end;
/
value of dummy = X
To expand on what I said in my comment below, it sounds like you just need a single sql statement, rather than using PL/SQL and relying on dbms_output.
Eg., say you have the following statement:
select lvl
from (select 'X'||level lvl from dual connect by level <= 10)
where lvl like '%&val%';
with &val blank, you get:
LVL
-----------------------------------------
X1
X2
X3
X4
X5
X6
X7
X8
X9
X10
With &val = 2 you get:
LVL
-----------------------------------------
X2
With &val = 100 you get:
no rows selected.

Resources