Oracle APEX - Download hidden SQL query into CSV - sql

I am trying to create a button on a page in my application that will download the full table I am referencing as a CSV file. I cannot use interactive reports > actions > download CSV because the interactive reports have hidden columns. I need all columns to populate in the CSV file.
Is there a way to create a SQL Script and reference it in the button?
I have already tried the steps referenced in this link: Oracle APEX - Export a query into CSV using a button but it does not help as my queries will contain columns that are hidden in the Interactive Report.

Welcome to StackOverflow!
One flexible option would be to use an application process, to be defined in the shared components (process point = ajax callback).
Something like this:
declare
lClob clob;
lBlob blob;
lFilename varchar2(250) := 'filename.csv';
begin
lClob := UNISTR('\FEFF'); -- was necessary for us to be able to use the files in MS Excel
lClob := lClob || 'Tablespace Name;Table Name;Number of Rows' || utl_tcp.CRLF;
for c in (select tablespace_name, table_name, num_rows from user_tables where rownum <= 5)
loop
lClob := lClob || c.tablespace_name || ';' || c.table_name || ';' || c.num_rows || utl_tcp.CRLF;
end loop;
lBlob := fClobToBlob(lClob);
sys.htp.init;
sys.owa_util.mime_header('text/csv', false);
sys.htp.p('Conent-length: ' || dbms_lob.getlength(lBlob));
sys.htp.p('Content-Disposition: attachment; filename = "' || lFilename || '"');
sys.htp.p('Cache-Control: no-cache, no-store, must-revalidate');
sys.htp.p('Pragma: no-cache');
sys.htp.p('Expires: 0');
sys.owa_util.http_header_close;
sys.wpg_docload.download_file(lBlob);
end;
This is the function fClobToBlob:
create function fClobToBlob(aClob CLOB) RETURN BLOB IS
tgt_blob BLOB;
amount INTEGER := DBMS_LOB.lobmaxsize;
dest_offset INTEGER := 1;
src_offset INTEGER := 1;
blob_csid INTEGER := nls_charset_id('UTF8');
lang_context INTEGER := DBMS_LOB.default_lang_ctx;
warning INTEGER := 0;
begin
if aClob is null then
return null;
end if;
DBMS_LOB.CreateTemporary(tgt_blob, true);
DBMS_LOB.ConvertToBlob(tgt_blob, aClob, amount, dest_offset, src_offset, blob_csid, lang_context, warning);
return tgt_blob;
end fClobToBlob;
On the page, you need to set your button action to "Redirect to Page in this Application", the target Page to "0". Under "Advanced", set Request to "APPLICATION_PROCESS=downloadCSV", where downloadCSV is the name of your application process.
If you need to parameterize your process, you can do this by accessing page items or application items in your application process.
Generating the CSV data can be cumbersome, but there are several packages out there that make it easier. The alexandria packages are one of them:
https://github.com/mortenbra/alexandria-plsql-utils
An example on how to use the CSV Package is here:
https://github.com/mortenbra/alexandria-plsql-utils/blob/master/demos/csv_util_pkg_demo.sql

Related

Using multiple procedures and multiple cursors in a package

I have created a package with two procedures and two cursors in it, but while executing the procedure, it is executed successful, but same record executed multiple times and a buffer overflow occurred.
I also tried removing the loop from the cursor but for 1 record that will be fine and for multiple record it won't work as expected.
EXPECTED
I just need to remove multiple execution of same record from the procedure where i am getting multiple execution of same record
for single procedure and single cursor it is working properly but for multiple cursor and multiple procedure i am getting problem here which caused buffer overflow too where i need different record
Is there any alternative way that I can fix the problem ?
CREATE OR REPLACE PACKAGE test.report AS
PROCEDURE distribution (
code_in IN user.test.code%TYPE,
fromdate date,
todate date
);
PROCEDURE tdvalue (
id IN user.test.custid%TYPE
);
END report;
/
Package Body
CREATE OR REPLACE PACKAGE BODY test.report as
----------VARIABLE DECLARATION----------------
code_in user.test.code%TYPE;
custidin user.test.custid%TYPE;
fromdate DATE;
todate DATE;
diff number(17,2);
---------------CURSOR DECLARATION--------------
CURSOR td_data(code_in user.test.code%TYPE,
fromdate date,
todate date
) IS
( SELECT
test.code,
COUNT(test.code) AS count,
SUM(test2.Deposit_amount) AS total,
test.currency
FROM
user.test2
JOIN user.test ON test2.acid = test.acid
WHERE
user.test2.open_effective_date BETWEEN TO_DATE(fromdate, 'dd-mm-yyyy') AND TO_DATE(todate, 'dd-mm-yyyy')
and
user.test.code = code_in
GROUP BY
test.code,test.currency
);
td__data td_data%rowtype;
CURSOR C_DATA(custidin user.test.custid%TYPE) IS SELECT
test.custid,
test2.id,
TO_DATE(test2.initial_date, 'dd-mm-yyyy') - TO_DATE(test2.end_date, 'dd-mm-yyyy') AS noofdays,
round(((test2.deposit_amount *((TO_DATE(test2.initial_date, 'dd-mm-yyyy') - TO_DATE(test2.end_date, 'dd-mm-yyyy'
)) / 365) * test4.interest_rate) / 100), 2) + test2.deposit_amount AS calculated_amount,
SUM(test.flow_amt) + test2.deposit_amount AS system_amount
FROM
user.test
JOIN user.test2 ON test3.entity_id = test2.id
WHERE
test.custid = custidin
GROUP BY
test.custid,
test2.id;
c__data c_data%ROWTYPE;
PROCEDURE distribution
(
code_in IN user.test.code%TYPE,
fromdate in date,
todate in date
)
AS
BEGIN
OPEN td_data(code_in,fromdate,todate);
loop
FETCH td_data INTO td__data;
dbms_output.put_line(td__data.code
|| ' '
|| td__data.count
|| ' '
||td__data.currency
||' '
||td__data.total
);
end loop;
CLOSE td_data;
END distribution;
PROCEDURE tdvalue (
custidin IN user.test.custid%TYPE
)
AS
BEGIN
open c_data(custidin);
fetch c_data into c__data;
loop
diff:= c__data.calculated_amount- c__data.system_amount;
dbms_output.put_line(c__data.custid
|| ' '
|| c__data.noofdays
|| ' '
|| c__data.end_date
|| ' '
|| c__data.initial_date
|| ' '
|| c__data.calculated_amount
||' '
||diff
);
end loop;
close c_data;
END tdvalue;
END report;
/
To run
ALTER SESSION set nls_date_format='dd-mm-yyyy';
SET SERVEROUTPUT ON;
EXEC REPORT.DISTRIBUTION('872328','01-02-2016','08-02-2019');
/
EXEC REPORT.tdvalue('S9292879383SS53');
Buffer overflow - ORU-10027 - happens when the total number of bytes displayed through DBMS_OUTPUT exceeds the size of the serveroutput buffer. The default is only 20000 bytes (who knows why?). Your session is using that default because of how you enable serveroutput. Clearly one record is less than 2000 and you only hit that limit when you run for multiple records.
To fix this try this
SET SERVEROUTPUT ON size unlimited
It's not actually unlimited, but the upper bound is the PGA limit (session memory) and you really shouldn't hit that limit with DBMS_OUTPUT. Apart from anything else who would read all that?
So the other problem with your code - as #piezol points out - is that your loops have no exit points. You should test whether the FETCH actually fetched anything and exit if it didn't:
loop
FETCH td_data INTO td__data;
exit when td_data%notfound;
dbms_output.put_line(td__data.code
|| ' '
|| td__data.count
|| ' '
||td__data.currency
||' '
||td__data.total
);
end loop;
Remembering to do this is just one reason why implicit cursors and cursor for loops are preferred over explicit cursors.
The second cursor loop is even worse because not only does it not have an exist point, the fetch is outside the loop. That's why you have repeated output for the same record.
So let's rewrite this ...
open c_data(custidin);
fetch c_data into c__data; -- should be inside
loop
diff:= c__data.calculated_amount- c__data.system_amount;
… as a cursor for loop:
PROCEDURE tdvalue (
custidin IN user.test.custid%TYPE
)
AS
BEGIN
for c__data in c_data(custidin)
loop
diff:= c__data.calculated_amount- c__data.system_amount;
dbms_output.put_line(c__data.custid
|| ' '
|| c__data.noofdays
|| ' '
|| c__data.end_date
|| ' '
|| c__data.initial_date
|| ' '
|| c__data.calculated_amount
||' '
||diff
);
end loop;
END tdvalue;
No need for OPEN, CLOSE or FETCH, and no need to check when the cursor is exhausted.
In PL/SQL, the preferred mechanism for setting the DBMS_OUTPUT buffer size would be within your procedure. This has the benefit of working in any client tool, such as Java or Toad (though it is still up to the client tool to retrieve the output from DBMS_OUTPUT).
DBMS_Output.ENABLE
Pass in a parameter of NULL for unlimited buffer size.
It would go like this:
BEGIN
DBMS_OUTPUT.ENABLE(NULL);
FOR I IN 1..1000 LOOP
DBMS_OUTPUT.PUT_LINE('The quick red fox jumps over the lazy brown dog.');
END LOOP;
END;
/
Bonus fact:
You can use the other functions and procedures in DBMS_OUTPUT to roll your own if you aren't using SQL*Plus or a DBMS_OUTPUT-savvy tool like Toad.
You can use the GET_LINE or GET_LINES procedures from your client code to get whatever may have been written to DBMS_OUTPUT.
GET_LINE

SQL ORACLE converting table to the html code

I'm working on oracle and taking report from database Oracle.
SELECT OPERATION, OBJECT_TYPE, CPU_COST, IO_COST FROM SYS.V_$SQL_PLAN;
I want to send this report with html formate in mail body to the Database Admin.
Is there any way to do this in pl-sql in oracle?
Yes this is possible you need:
a) PL/SQL procedure to create the body html out of the SQL
b) PL/SQL procedure to send the body html in an html email to the admin
For a) here you can write a custom procedure which creates the html out of the sql cursor
for cur in (select * from query) loop
html := html || '<tr>';
html := html || '<td>';
html := html || cur.col1;
html := html || '</td>';
html := html || '<td>';
html := html || cur.col2;
html := html || '</td>';
...
html := html || '<td>';
html := html || cur.coln;
html := html || '</td>';
html := html || '</tr>';
or search/write some generic code like https://aykutakin.wordpress.com/2013/06/18/create-html-table-from-oracle-table-in-plsql/
For b) Use UTL_SMTP
Use all steps mentioned in attached screenshot, in case of any problem let me know to help you.
Here instead of col 1 you can define col 2, col 3 .... And so on, with one open td and one close td in similar way.
Make sure data count is not more then 10000. Else during browser load it will hang.

Updating integer values with a integer variable in MS Access using Delphi 10

iexc := round(iexc/icount);
iint := round(iint/icount);
inau := round(inau/icount);
iovr := round(iovr/icount);
adoquery1.SQL.Text := 'update Tickets_Tbl set Excitment = iexc where
RollerCoaster = "'+sride+'"';
adoquery1.ExecSQL;
adoquery1.SQL.Text := 'update Tickets_Tbl set Intensity = iint where
RollerCoaster = "'+sride+'"';
adoquery1.ExecSQL;
adoquery1.SQL.Text := 'update Tickets_Tbl set Nausea = inau where
RollerCoaster = "'+sride+'"';
adoquery1.ExecSQL;
adoquery1.SQL.Text := 'update Tickets_Tbl set Overall = iovr where
RollerCoaster = "'+sride+'"';
adoquery1.ExecSQL;
The main problem is that when I run the code it keeps saying that there is no default value for field begin updated. Please help.
The problem is that you're hard-coding the variable names within your SQL instead of setting the values properly (which should be done using a parameterized query, BTW). Also, your SQL is horrific; you can do this in a single UPDATE instead of repeating it multiple times.
This should get you started (see note below about the #13):
iexc := round(iexc/icount);
iint := round(iint/icount);
inau := round(inau/icount);
iovr := round(iovr/icount);
AdoQuery1.SQL.Text := 'update Tickets_Tbl'#13 +
'set Excitement = :iexc,'#13 +
' Intensity = :iint,'#13 +
' Nausea = :inau,'#13 +
' Overall = :iovr'#13 +
'where RollerCoaster = :sride';
AdoQuery1.Parameters.ParamByName('iexc').Value := iexc;
AdoQuery1.Parameters.ParamByName('iint').Value := iint;
AdoQuery1.Parameters.ParamByName('inau').Value := inau;
AdoQuery1.Parameters.ParamByName('iovr').Value := iovr;
AdoQuery1.Parameters.ParamByName('sride').Value := sride;
AdoQuery1.ExecSQL;
The #13 in the code is a carriage return, which simply makes it easier because you don't have to worry about properly inserting spaces before or after the end or start of each successive line. It's basically the same as typing the query into SQL Server Management Studio line-by-line, pressing Enter at the end of each line. You can do the same thing using AdoQuery1.SQL.Add for each line, but to me that makes it harder when you need to copy the SQL out and clean it up to run externally for testing or debugging or modification. The above is similar to
AdoQuery1.SQL.Clear;
AdoQuery1.SQL.Add('update Tickets_Tbl');
AdoQuery1.SQL.Add('set Excitement = :iexc,');
AdoQuery1.SQL.Add('Intensity = :iint,');
AdoQuery1.SQL.Add('Nausea = :inau,');
AdoQuery1.SQL.Add('Overall = :iovr');
AdoQuery1.SQL.Add('where RollerCoaster = :sride');
Use whichever one is easier and more readable for you personally. I use the first, because I have a utility that will take plain SQL from the clipboard and format it with the ' and #13 + automatically for pasting into Delphi's Code Editor, or take that quoted text containing embedded '' characters and #13 + and remove them automatically to allow pasting into SSMS or Access or another SQL utility without manual cleanup.

Oracle - measure time of reading blob files using sql

ive got a problem for which i couldnt find answwer so far.
Is there a way to read blob file from oracle table using sql or pl/sql and measure time of reading it? I mean like reading whole of it, i dont need it displayed anywhere. All i found was to read 4000 bytes of file but thats not enough.
For importing there is simply
SET TIMING ON and OFF option in sqlplus but using select on tablle gives only small portion of file and doesnt matter how big it is, it always takes the same time pretty much.
Any help anybody?
Not quite sure what you're trying to achieve, but you can get some timings in a PL/SQL block using dbms_utility.get_time as LalitKumarB suggested. The initial select is (almost) instant though, it's reading through or processing the data that's really measurable. This is reading a blob with three different 'chunk' sizes to show the difference it makes:
set serveroutput on
declare
l_start number;
l_blob blob;
l_byte raw(1);
l_16byte raw(16);
l_kbyte raw(1024);
begin
l_start := dbms_utility.get_time;
select b into l_blob from t42 where rownum = 1; -- your own query here obviously
dbms_output.put_line('select: '
|| (dbms_utility.get_time - l_start) || ' hsecs');
l_start := dbms_utility.get_time;
for i in 1..dbms_lob.getlength(l_blob) loop
l_byte := dbms_lob.substr(l_blob, 1, i);
end loop;
dbms_output.put_line('single byte: '
|| (dbms_utility.get_time - l_start) || ' hsecs');
l_start := dbms_utility.get_time;
for i in 1..(dbms_lob.getlength(l_blob)/16) loop
l_16byte := dbms_lob.substr(l_blob, 16, i);
end loop;
dbms_output.put_line('16 bytes: '
|| (dbms_utility.get_time - l_start) || ' hsecs');
l_start := dbms_utility.get_time;
for i in 1..(dbms_lob.getlength(l_blob)/1024) loop
l_kbyte := dbms_lob.substr(l_blob, 1024, i);
end loop;
dbms_output.put_line('1024 bytes: '
|| (dbms_utility.get_time - l_start) || ' hsecs');
end;
/
For a sample blob that gives something like:
anonymous block completed
select: 0 hsecs
single byte: 950 hsecs
16 bytes: 61 hsecs
1024 bytes: 1 hsecs
So clearly reading the blob in larger chunks is more efficient. So your "measure time of reading it" is a bit flexible...
I guess you already have the solution to access the BLOB data. For getting the time, use DBMS_UTILITY.GET_TIME before and after the step in your PL/SQL code. You could declare two variables, start_time and end_time to capture the respective times, and just subtract them to get the time elapsed/taken for the step.
See this as an example, http://www.oracle-base.com/articles/11g/plsql-new-features-and-enhancements-11gr1.php

pdf to text conversion using oracle package

i m seeing some strange behavior for my pdf to text conversion using oracle
bellow is the code of a sql file.
create or replace directory pdf_dir as '&1';
create or replace directory l_curr_dir as '&3';
declare
ll_clob CLOB;
l_bfile BFILE;
l_filename VARCHAR2(100) := '&2';
begin
begin
ctx_ddl.drop_policy('test_policy');
exception
when others then
null;
end;
ctx_ddl.create_policy('test_policy','ctxsys.auto_filter');
l_bfile := bfilename('PDF_DIR',l_filename);
dbms_lob.createtemporary(ll_clob, true);
ctx_doc.policy_filter(
policy_name => 'test_policy'
, document => l_bfile
, restab => ll_clob
, plaintext => true
);
ll_clob := REPLACE(TRIM(ll_clob), chr(13), chr(10));
ll_clob := REPLACE(ll_clob, chr(10), chr(32) || '<<EOL>>' || chr(10)||'<<BOL>>');
INSERT into tempclob_op(filename, data) VALUES(l_filename, ll_clob);
DBMS_XSLPROCESSOR.clob2file (ll_clob,'L_CURR_DIR' , 'plaintext.text');
dbms_lob.freeTemporary( ll_clob );
end;
/
problem is i have run this code for 10000 files and it gives correct results for almost all but for almost 10 files it corrupts the output in plaintext.text file. And i dont know why is it happening? Also when i run this sql code for individual files it gives me correct results.
I have added some delay of 2 seconds in every execution while in loop for each file. and seems it resolved the problem strangely ..no concrete answers though.