Create txt from a select [duplicate] - sql

I am trying to generate CSV file from my oracle DB and send it as an attachment via mail . I have an issue with the CSV file generated. I could see that certain values from the table is not read properly. Like the value in description column is written in CSV file in two different cells instead of a single cell .
you can see that a value is written in two cells instead of a single cell as rest of the values. Here 'Testing pls ignore mails' should be treated as a single value and should be in the same cell .
I noticed one more thing that the value is in the table as follows:
could this be because my code is unable to read the value in multiple lines ?
Here is my code for creating the CSV file and the field FC_ED_DESC is causing the problems and I am including " l_clob " in the attachment section of the code that I have written for mailing .
DECLARE
l_clob CLOB;
l_attach_text VARCHAR2 (32767);
l_attach_text_h VARCHAR2 (32767);
FC_SV_STATUS_DESC VARCHAR2(200) := 'open';
-- select query from table to get the values needed
CURSOR c1 IS
select FC_ED_RECORD_ID,to_char(FC_ED_UPLOADTIME,'DD.MM.YY')FC_ED_UPLOADTIME,FC_ED_USER_ID ,FC_ED_BROKER,FC_ED_ACT_NUM,FC_ED_POLICY_NUM,FC_ED_AMOUNT,FC_ED_TRANS_TYPE,FC_ED_CURRENCY,to_char(FC_ED_DUE_DATE,'DD.MM.YY')FC_ED_DUE_DATE,FC_ED_SENDER_NAME,FC_ED_DESC,to_char(FC_ED_CREDIT_DATE,'DD.MM.YY')FC_ED_CREDIT_DATE from MYTABLE where FC_ED_EXPCASH_STATUS = 1 ;
BEGIN
-- csv file columns are these
l_attach_text_h :=
'FC_ED_RECORD_ID ,FC_ED_UPLOADTIME ,FC_ED_USER_ID ,FC_ED_BROKER ,FC_ED_ACT_NUM ,FC_ED_POLICY_NUM ,FC_ED_AMOUNT ,FC_SV_STATUS_DESC ,FC_ED_TRANS_TYPE ,FC_ED_CURRENCY ,FC_ED_DUE_DATE ,FC_ED_SENDER_NAME ,FC_ED_DESC ,FC_ED_CREDIT_DATE';
FOR employee_rec in c1
LOOP
DBMS_OUTPUT.put_line('Before loop COUNT Boss ...'||c1%ROWCOUNT);
-- each value is read using loop
l_attach_text :=
employee_rec.FC_ED_RECORD_ID ||','||
employee_rec.FC_ED_UPLOADTIME ||','||
employee_rec.FC_ED_USER_ID ||','||
employee_rec.FC_ED_BROKER ||','||
employee_rec.FC_ED_ACT_NUM ||','||
employee_rec.FC_ED_POLICY_NUM ||','||
employee_rec.FC_ED_AMOUNT ||','||
FC_SV_STATUS_DESC ||','||
employee_rec.FC_ED_TRANS_TYPE ||','||
employee_rec.FC_ED_CURRENCY ||','||
employee_rec.FC_ED_DUE_DATE ||','||
employee_rec.FC_ED_SENDER_NAME ||','||
employee_rec.FC_ED_DESC ||','||
employee_rec.FC_ED_CREDIT_DATE ||chr(13);
l_clob := l_clob || l_attach_text;
END LOOP;
-- adding values
l_clob := l_attach_text_h ||chr(13)|| l_clob;
Can anyone please help me with this ?

The problem is that you are not surrounding your fields with double quotes. Anything that contains a carriage return will be read as a new record in your CSV file.
Since you are using SQL Developer, the easiest way to generate a CSV file is to use the /*csv*/ hint in your query. This will properly quote all the fields.
Put this code into SQL Developer and execute it using F5 to run it in Script mode and you should get a properly quoted CSV output in DBMS_OUTPUT.
SELECT /*csv*/
FC_ED_RECORD_ID,
TO_CHAR (FC_ED_UPLOADTIME, 'DD.MM.YY') FC_ED_UPLOADTIME,
FC_ED_USER_ID,
FC_ED_BROKER,
FC_ED_ACT_NUM,
FC_ED_POLICY_NUM,
FC_ED_AMOUNT,
FC_ED_TRANS_TYPE,
FC_ED_CURRENCY,
TO_CHAR (FC_ED_DUE_DATE, 'DD.MM.YY') FC_ED_DUE_DATE,
FC_ED_SENDER_NAME,
FC_ED_DESC,
TO_CHAR (FC_ED_CREDIT_DATE, 'DD.MM.YY') FC_ED_CREDIT_DATE
FROM MYTABLE
WHERE FC_ED_EXPCASH_STATUS = 1;
Another option is to just run the query you want in SQL Developer, then highlight the results you want to export (or press Control + A), then right click and select Export and choose your Format to be CSV
Update
To fix your existing code, put double quotes around each of the fields you are combining into your l_attach_text variable. See example below.
l_attach_text := '"' ||
employee_rec.FC_ED_RECORD_ID ||'","'||
employee_rec.FC_ED_UPLOADTIME ||'","'||
employee_rec.FC_ED_USER_ID ||'","'||
employee_rec.FC_ED_BROKER ||'","'||
-- ...(the rest of your fields here)
employee_rec.FC_ED_DESC ||'","'||
employee_rec.FC_ED_CREDIT_DATE || '"' || chr(13);

Related

Oracle Block Statement DBMS output syntax

I made this account just for this question because I'm really not sure how to word it properly for google to pick up on it. So here goes...
I was working on a query for a group project that I was assigned in my database II course. I made 2 and was working on a third when I wanted to see if I could do something like the following.
declare
Emp_ID := 03;
Salary_Increment := 2.0 --constants do not change
Salary_ID := 03;
Current_Salary := 47500
Updated_Salary := Current_Salary * 2.0
BEGIN
dbms_output.put_line('The Employee with the ID of' +Emp_ID 'will receive a 2% bonus.');
dbms_output.put_line('This Employees new salary will be: ' +Updated_Salary ');
end;
I attempted to do this previously but with a more simple code snippet. Figured I would see if I could do this just to simplify how much I have to type out.
TL;DR - Can I use a reference like +Emp_ID in a Oracle SQL dbms output?
In oracle, There are two ways of concating the string.
using || operator.
Like this.
dbms_output.put_line('The Employee with the ID of' || Emp_ID || ' will receive a 2% bonus.');
using CONCAT method.
Like this:
dbms_output.put_line(CONCAT(CONCAT('The Employee with the ID of', Emp_ID), ' will receive a 2% bonus.'));
Note that CONCAT uses only two parameter as input. So you need to use it multiple times to concat more than 2 strings.
Cheers!!
The string concatenation operator is || in PL/SQL, so you'd want to write
dbms_output.put_line('The Employee with the ID of' || Emp_ID || ' will receive a 2% bonus.');

replace single quote with dbouble quote inside clob text

i am inserting a CLOB data into a table.
as part of this, I am inserting a complete plsql procedure into a clob column. Procedure has many dynamic sql statements. when inserting its throwing ora error.
sample code:
insert into t_prc_cmpre( prc_nm,vrsn_nbr,v_CLOB ,envr)
(select 'PRC_1','3.7.5',
'CREATE OR REPLACE PROCEDURE PRC1
IS
v_sql clob;
BEGIN
v_stmt:='INSERT INTO '||v_targetschema||'.'|| PI_TABLE ||' (COL1,COL2,COL3...)
execute immediate v_stmt;
end;
/'
since insert statement has single quote ,its not allowing to insert into clob column.
Please help me to resolve the issue.
Many a thanks!
An easy way to insert 'awkward' data is with the the 'q' syntax, eg
insert into t ( c) values ( q'{ This is some text with 'quotes' etc}' );
I'm not sure what you're trying to do and your block is incomplete and won't compile. But if you're trying to put a ' character inside a varchar or clob, you enter it as two 's, like this:
x := 'The cat''s whiskers';

Need to build a query with column names stored in another table

I have a table as shown below. This table will be generated dynamically and I have no prior idea about what value it is going to hold.
------------------------------------------
TABLE_NAME COLUMN_NAME CHAR_LENGTH
------------------------------------------
EMPLOYEE COL1 100
EMPLOYEE COL2 200
EMPLOYEE COL3 300
EMPLOYEE COL4 400
Based on this table, I want to build a query in such a way that it would give me those columns, that contains data having char length greater than CHAR_LENGTH column value.
For example if COL2 contains data having char length 500 (>200), then query would give me COL2.
I don't have any draft code to show my attempt, as I have no idea how would I do this.
I don't think this is possible in pure SQL due to the dynamic nature of your requirement. You'll need some form of PL/SQL.
Assuming you're ok with simply outputting the desired results, here is a PL/SQL block that will get the job done:
declare
wExists number(1);
begin
for rec in (select * from your_dynamic_table)
loop
execute immediate 'select count(*)
from dual
where exists (select null
from ' || rec.table_name || ' t
where length(t.' || rec.column_name || ') > ' || rec.char_length || ')'
into wExists;
if wExists = 1 then
dbms_output.put_line(rec.column_name);
end if;
end loop;
end;
You'll also notice the use of the exists clause to optimize the query, so as not to iterate over the whole table unnecessarily, when possible.
Alternatively, if you want the results to be queryable, you can consider converting the code to a pipelined function.
select column_name
from (
select statement that builds the table output
) A
where char_length<length(column_name)
will that help?
You would need a procedure to achieve the same :
Here I am treating the all_tab_columns table in Oracle, which is a default table with much similar structure as your reference example.(Try select * from all_tab_columns). The structure of all_tab_columns is much like yours, except that you will never find a varchar record whose value has exceeded its data length(obvious database level constraint). Date fields may exceed data length, and do reflect in this procedure's output. I am searching all columns in EMPLOYEES whose size exceeds what is specified.
DECLARE
cursor c is select column_name,data_length,table_name from all_tab_columns where table_name=:Table_name;
V_INDEX_NAME all_tab_columns.column_name%type;
v_data_length all_tab_columns.data_length%type;
V_NUMBER PLS_INTEGER;
v_table_name all_tab_columns.table_name%type;
BEGIN
open c;
LOOP
FETCH c into v_index_name,v_data_length,v_table_name;
EXIT when c%NOTFOUND;
v_number :=0;
execute immediate 'select count(*) from '|| :Table_name ||' where length('||v_index_name||')>'||v_data_length into v_number;
if v_number>1 then
dbms_output.put_line(v_index_name||' has values greater than specified'||' '||V_INDEX_NAME||' '||v_data_length);
end if;
END LOOP;
close c;
END;
/
Replace all_tab_columns and its respective columns with the column name of your table.
DEFECTS : The table name is hardcoded. Trying to make the code generic execute immediate or any other trick. Will achieve soon.
EDIT : Defect fixed.

Dynamically Trim Column Values

Here's a question for you all, is there any way to make the PL/SQL code below work by dynamically replacing the column_name in the cursor.column_name syntax? I know I'm confusing the pl/sql engine, but I'm not entirely sure on how to fix this...
Currently I get the error below, but I think the actual issue is that the pl/sql engine doesn't know how to interpret TRIM (e_rec.v_temp_column_name) :
"[Error] PLS-00302 (133: 26): PLS-00302: component
'V_TEMP_COLUMN_NAME' must be declared"
Parameter: x_person_rec IN OUT xxsome_table%ROWTYPE
v_temp_column_name dba_tab_columns.column_name%TYPE;
...
BEGIN
FOR e_rec IN (SELECT * FROM xxsome_table WHERE ..)
LOOP
--LOG (3, 'Loading/Sanitizing Header Record');
FOR col IN (SELECT column_name
FROM dba_tab_columns
WHERE table_name = UPPER ('xxsome_table'))
LOOP
--LOG (3, 'Sanitizing Column Name: ' || col.column_name);
v_temp_column_name := col.column_name;
x_person_rec.v_temp_column_name := TRIM (e_rec.v_temp_column_name);
END LOOP;
END LOOP;
...
I've tried doing this (which results in different error): x_person_rec.col.column_name := TRIM (e_rec.col.column_name);
No, you can't and you are indeed confusing the PL/SQL engine. The problem is that v_temp_column_name is a character, so TRIM (e_rec.v_temp_column_name) is evaluated as TRIM (e_rec.'v_temp_column_name'), which doesn't make any sense.
The best things to do, if trailing whitespace is a problem, is to ensure that all data being put into your database is trimmed by your application/ETL processes at the time. If you can't do this then use a trigger to ensure that it happens inside the database. If it's a really bad problem you can even enforce check constraints to stop it from ever happening.
Now, to sort of answer your question, there's no need to do anything dynamically here. Your cursor may be implicit but it's not been dynamically generated. You know every column in the table so be a little less lazy and type them all out.
FOR e_rec IN (SELECT trim(col1) as col1
, trim(col2) as col2
FROM xxsome_table WHERE ...
If you can't fix your data (or if it's not broken!) then this is easily the simplest way to do it.
To actually answer your question, you can dynamically build your SELECT statement using the same techniques you're using here...
declare
l_cols varchar2(4000);
l_curs sys_refcursor;
begin
select wm_concat('trim(' || column_name || ')')
into l_cols
from user_tab_columns
where table_name = 'XXSOME_TABLE'
;
open l_curs for
' select ' || l_cols || ' from xxsome_table where ...';
loop
...
end loop;
end;
/
As you're on 10g you can't use the excellent LISTAGG(), but there are plenty of other string aggregation techniques. Please note that if the resulting string is greater than 4,000 bytes, you'll have to loop rather than generating the column list in a single SQL statement.
P.S., if these columns are CHAR(n) then you'll have to trim them every time you select. It might be worth changing them to VARCHAR2(n)

Inserting a select statement into a table- ORA-06502

I have 6 TEST environments and 1 production environment. I create quite a few different reports as Oracle views, and need a way to sync these between environments.
I am trying to make a script that I can run, which will basically output a list of commands that I can copy and paste into my different environment to create the necessary views/public synonyms and privileges.
I have to put the resultant text into a database table as dbms_output.put_line has a certain limitation on how many characters it can show.
I have the following, but if I try to insert the data, I get ORA-06502: PL/SQL: numeric or value error. I am guessing this is probably got to do with character literals not being escaped and what not.
CREATE OR REPLACE PROCEDURE EXPORT_REPORTS AS
statements CLOB;
tmp_statement CLOB;
CURSOR all_views IS
SELECT
OWNER,
VIEW_NAME,
TEXT
FROM
ALL_VIEWS
WHERE
OWNER = 'PAS'
;
BEGIN
FOR v IN all_views LOOP
tmp_statement := 'CREATE OR REPLACE FORCE VIEW "' || v.OWNER || '"."' || v.VIEW_NAME || '" AS ' || CHR(13) || CHR(10) || v.TEXT;
statements := statements || tmp_statement;
END LOOP;
EXECUTE IMMEDIATE 'INSERT INTO VIEW_EXPORTS VALUES ('''|| statements || ''')';
END EXPORT_REPORTS;
Any idea what I can do to try and fix this?
If it is because some of the text in the statements variable contains single quotes, how can I escape this before inserting the data into a table?
This sounds like a job for Data Pump.
Oracle Data Pump technology enables very high-speed movement of data and metadata from one database to another.
http://docs.oracle.com/cd/B28359_01/server.111/b28319/dp_overview.htm
For getting database object DDL I would recommend using the DBMS_METADATA package.
http://docs.oracle.com/cd/B28359_01/appdev.111/b28419/d_metada.htm#BGBDJAHH
you cannot use CLOB as a datatype for the local variables. Use VARCHAR instead