DBMS_Output: Some extra "enable" might be missing - sql

Sorry the silly question. I am stuck with Dbms_Output:
First I tried it within a batch/sqlplus call. But the spool file would only contain the message "pl/sql sucessfully executed". (See first code part)
I am running the code on Oracle Database 12c Enterprise Edition Release 12.1.0.2.0
Spool D:\log\spool.txt
Declare
Some_Num Number := 5;
Begin
Dbms_Output.Enable(1000000);
Dbms_Output.Put_Line('Id, Timestamp');
For Rec In
(
Select 1 As Id, Sysdate As Timestamp From Dual
Union
Select 2 As Id, Sysdate As Timestamp From Dual
Union
Select 3 As Id, Sysdate As Timestamp From Dual
Union
Select 4 As Id, Sysdate As Timestamp From Dual
)
Loop
Dbms_Output.Put_Line( Rec.Id || ', ' || Rec.Timestamp );
-- some code here was actually executed;
End Loop;
--Dbms_Output.Disable;
Exception
When Others Then
Null;
Dbms_Output.Put_Line('Error');
--Dbms_Output.Disable;
End;
/
Spool Off
exit;

Found it myself. I needed to add
SET SERVEROUTPUT ON SIZE 1000000
at the beginning of the script.

Related

Return Multiple Values from Oracle Function

I want to create a function that returns multiple rows into a table that is of object type.
I have created an object and a nested table object and now when I run the function there is an error which says
PL/SQL: SQL Statement ignored
PL/SQL: ORA-00947: not enough values
-- Object type creation
create or replace type test_object_sn as object
(
column_1 varchar2(30),
column_2 varchar2(30),
column_3 number
);
-- Table of object
create or replace type test_otable_sn as table of test_object_sn;
-- function (where I get an error)
create or replace function load_test_object_sn
return test_otable_sn
as
details test_otable_sn;
begin
with ad as (select 'a', 'b', 4 from dual
union all
select 'r', '5', 3 from dual
union all
select 'g', 's', 3 from dual)
select * into details from ad;
return details;
end;
I want to have the test_otable_sn table object loaded with the data and then query it using the table() function via my load_test_object_sn function
e.g. select * from table(load_test_object_sn);
Update:
do you know how to modify this for scenario whereby I have an sql
statement contained in a string variable to execute?
Yes, we can use a cursor reference (SYS_REFCURSOR) and OPEN/FETCH/CLOSE instead of a CURSOR and CURSOR FOR LOOP.
The syntax is OPEN <cursor-reference> FOR <string-containing-sql-statement> . See below.
CREATE OR REPLACE FUNCTION load_test_object_sn
RETURN test_otable_sn
AS
details test_otable_sn := test_otable_sn();
-- Variable stores SQL statement for cursor
l_sql CLOB :=
q'[with ad as (
select 'a' column_1, 'b' column_2, 4 column_3 from dual union all
select 'r', '5', 3 from dual union all
select 'g', 's', 3 from dual
)
select *
from ad]';
-- Cursor reference allows us to open cursor for SQL statement above
rc SYS_REFCURSOR;
-- Define object instance to store each row fetched from the cursor
l_obj test_object_sn := test_object_sn(NULL, NULL, NULL);
i PLS_INTEGER := 1;
BEGIN
-- Explicitly open, fetch from, and close the cursor
OPEN rc FOR l_sql;
LOOP
FETCH rc INTO l_obj.column_1, l_obj.column_2, l_obj.column_3;
EXIT WHEN rc%NOTFOUND;
details.extend();
details(i) := test_object_sn(l_obj.column_1, l_obj.column_2, l_obj.column_3);
i := i + 1;
END LOOP;
CLOSE rc;
RETURN details;
END;
Original answer:
Unfortunately, one can't use SELECT * INTO with a collection in this manner, so here's an alternative way to populate the table:
create or replace function load_test_object_sn
return test_otable_sn
as
details test_otable_sn := test_otable_sn();
cursor c_ad is
with ad as (select 'a' column_1, 'b' column_2, 4 column_3 from dual
union all
select 'r', '5', 3 from dual
union all
select 'g', 's', 3 from dual)
select * from ad;
i pls_integer := 1;
begin
for ad_rec in c_ad loop
details.extend();
details(i) := test_object_sn(ad_rec.column_1, ad_rec.column_2, ad_rec.column_3);
i := i + 1;
end loop;
return details;
end;
/
Output:
SQL> SELECT * FROM TABLE(load_test_object_sn);
COLUMN_1 COLUMN_2 COLUMN_3
---------- ---------- ----------
a b 4
r 5 3
g s 3

How to use 'sysdate' if its a string constant

I extract data from a table, the field is mostly null, but sometimes it's sysdate. However since its from a table, after getting the field its 'sysdate', between single quotes. How can I use it?
I have tried to_date, to_date(to_char()).
I need something that works within
select to_date('sysdate') from dual;
You can use a case expression:
select case
when the_column = 'sysdate' then sysdate
else to_date(the_column)
end as date_value
from the_table;
The only way I know is dynamic SQL. Here's an example:
SQL> create table test (id number, col varchar2(20));
Table created.
SQL> insert into test
2 select 1, '''sysdate''' from dual union all
3 select 2, null from dual;
2 rows created.
SQL> declare
2 l_res test%rowtype;
3 l_str varchar2(200);
4 begin
5 for cur_r in (select id, col from test) loop
6 l_str := 'select ' || cur_r.id ||', '||
7 nvl(replace(cur_r.col, chr(39), null), 'null') || ' from dual';
8 execute immediate l_str into l_res;
9 dbms_output.put_line(l_res.id ||': '|| l_res.col);
10 end loop;
11 end;
12 /
1: 24.06.2019 12:18:39
2:
PL/SQL procedure successfully completed.
SQL>

Checking a specific date format in Oracle SQL using RegexLike

I am using oracle data integrator as an ETL tool to load data, there is one column which as a source comes in format YYYYMMDD for ex: 20190418. I want to query that table to identify all those records which do not fit 20190418 or YYYYMMDD format.
P.S. the column is in varchar2 datatype.
I already tried using something like this:
SELECT CASE WHEN NOT REGEXP_LIKE('20190418', '^\d{4}(0[1-9]|1[12])(0[1-9]|[12]\d|3[01])$') then '00000000' else '20190418' END FROM DUAL;
This seems to identify those illegal dates, but for example it didn't work for this one : '20181023'.
Can someone figure it out, I think I am missing some kind of pattern
I would propose a function rather than a regex, will be easier.
CREATE OR REPLACE FUNCTION Verify_date_number(inNumber IN NUMBER) RETURN VARCHAR2 AS
res DATE;
BEGIN
res := TO_DATE(inNumber, 'fxYYYYMMDD');
RETURN TO_CHAR(res, 'YYYYMMDD');
EXCEPTION
WHEN OTHERS THEN
RETURN '00000000';
END;
If you are running Oracle 12.2 then you can also use VALIDATE_CONVERSION
SELECT
CASE VALIDATE_CONVERSION('20190418' AS DATE, 'fxYYYYMMDD')
WHEN 1 THEN '20190418'
ELSE '00000000'
END
FROM dual;
Using PL/SQL maybe a better alternative
declare
v_date date;
begin
for c in
(
select '20190418' as date_str from dual union all
select '20191804' from dual union all
select '201904187' from dual
)
loop
begin
v_date := to_date(c.date_str,'yyyymmdd');
dbms_output.put_line(c.date_str);
exception when others then null;
end;
end loop;
end;
gives only the decently formatted data as output. In the above case, it's
20190418 only.
For 20191804, we would get ORA-01843: not a valid month
For 201904187, we would get ORA-01830: date format picture ends before converting entire input string
errors
you can try this one:
with tab as(
select '20190418' as dat from dual union all
select '20181023' as dat from dual union all
select '20181123' as dat from dual union all
select '20181223' as dat from dual union all
select '20181201' as dat from dual union all
select '20181209' as dat from dual union all
select '20181210' as dat from dual union all
select '20181229' as dat from dual union all
select '20181231' as dat from dual union all
select '20181232' as dat from dual union all
select '20181200' as dat from dual union all
select '20191418' as dat from dual
)
SELECT CASE WHEN NOT REGEXP_LIKE(dat, '^\d{4}(0[1-9]|(1[0-2]))(0[1-9]|[1-2][0-9]|3[0-1])$') then '00000000' else dat END as dat
FROM tab;
Result:
20190418
20181023
20181123
20181223
20181201
20181209
20181210
20181229
20181231
00000000
00000000
00000000

ORA-01403 - no data found -- even though cursor SELECT statement is running fine

declare
CURSOR C1 IS
Select to_date(DateRange,'dd-mm-rrrr') DateRange
from (select to_date('01-JAN-2016','DD-MON-RRRR') - 1 + level as DateRange
from DUAL
where (TO_DATE('01-JAN-2016', 'DD-MON-RRRR') - 1 + level) <= LAST_DAY(TO_DATE('31-DEC-2016', 'DD-MON-RRRR'))
connect by level<=366)
where substr(to_char(DateRange,'DAY'),1,3) in ('SUN','SAT');
begin
if not c1%ISopen
then
open C1;
end if;
dbms_output.put_line("Cursor is created and number of affected rows are: " || c1%rowcount);
close c1;
end;
/
Even though the select statement used in Creating Cursor is running fine, but when i am trying to execute the code it is throwing error -- ORA-01403 - no data found
You open the cursor, but do not fetch it.
From documentation:
%ROWCOUNT Attribute A cursor attribute that can be appended to the
name of a cursor or cursor variable. When a cursor is opened,
%ROWCOUNT is zeroed. Before the first fetch, cursor_name%ROWCOUNT
returns 0. Thereafter, it returns the number of rows fetched so far.
The number is incremented if the latest fetch returned a row.
The code should be something like this:
DECLARE
CURSOR C1
IS
SELECT TO_DATE (DateRange, 'dd-mm-rrrr') DateRange
FROM ( SELECT TO_DATE ('01-JAN-2016', 'DD-MON-RRRR') - 1 + LEVEL
AS DateRange
FROM DUAL
WHERE (TO_DATE ('01-JAN-2016', 'DD-MON-RRRR') - 1 + LEVEL) <=
LAST_DAY (TO_DATE ('31-DEC-2016', 'DD-MON-RRRR'))
CONNECT BY LEVEL <= 366)
WHERE SUBSTR (TO_CHAR (DateRange, 'DAY'), 1, 3) IN ('SUN', 'SAT');
a DATE;
BEGIN
IF NOT c1%ISOPEN
THEN
OPEN C1;
END IF;
LOOP
FETCH c1 INTO a;
EXIT WHEN C1%NOTFOUND;
END LOOP;
DBMS_OUTPUT.put_line (
'Cursor is created and number of affected rows are: ' || c1%ROWCOUNT);
CLOSE c1;
END;
/
Your select statement is not right. Replace to_date with to_char.
declare
CURSOR C1 IS
select to_char(DateRange,'dd-mm-rrrr') DateRange --replaced to_date with to_char
from
(
select to_date('01-JAN-2016','DD-MON-RRRR') - 1 + level as DateRange
from DUAL
where (TO_DATE('01-JAN-2016', 'DD-MON-RRRR') - 1 + level) <= LAST_DAY(TO_DATE('31-DEC-2016', 'DD-MON-RRRR'))
connect by level<=366
)
where substr(to_char(DateRange,'DAY'),1,3) in ('SUN','SAT');
begin
if not c1%ISopen
then
open C1;
end if;
dbms_output.put_line("Cursor is created and number of affected rows are: " || c1%rowcount);
close c1;
end;
/
From inner query you are returning a date but in the main select clause you are using to_date on a date which might be why you are not getting desired result.

Oracle Package return table

I am trying to return a table via a function in an Oracle package:
CREATE OR REPLACE PACKAGE test AS
TYPE rec IS RECORD(
col1 VARCHAR(10));
TYPE rec_table IS TABLE OF rec;
FUNCTION get_table(input VARCHAR2)
RETURN rec_table
PIPELINED;
END;
CREATE OR REPLACE PACKAGE BODY test AS
FUNCTION get_table(input VARCHAR2)
RETURN rec_table
PIPELINED IS
rec1 rec;
BEGIN
SELECT * INTO rec1
FROM
(
SELECT '1' from dual
UNION ALL
SELECT '2' from dual
);
PIPE ROW (rec1)
RETURN;
END get_table;
END;
but when I try to run
select * from table(test.get_table('blah'))
I get an error: exact fetch returns more then requested number of rows
I've read a bit about BULK COLLECT INTO, but I am not understanding the syntax...
The following piece of code:
SELECT '1' from dual
UNION ALL
SELECT '2' from dual
Returns two, not one record, and you are trying to put those two records in one rec variable. You should instead loop through the results of the UNION:
FOR v_rec IN (
SELECT *
FROM (
SELECT '1' from dual
UNION ALL
SELECT '2' from dual
)
)
LOOP
PIPE ROW (v_rec);
END LOOP;