Output key, value pairs with sqlplus - oracle

I am using sqlplus to output the content of a table :
select * from my_table;
The output is similar to this :
ID
-------------
FIELD1
----------------
FIELD2
----------------
someid
field 1 content
field 2 content
I tried many combinations of the page formatting options. I can format the output in a table by setting head, pages, termout, echo, feed and linesize.
But I would like to output the values in a (key, value) fashion, like this :
ID = someid
FIELD1 = field 1 content
FIELD2 = field 2 content
Delimiter and formatting is not important, as long as I have one column per line.
Is it possible using only sqlplus ? I would like to avoid scripting this. On this particular machine (an appliance I can't install anythin on) I only have bash, perl and Python 2.4.

I don't know of any SQLPlus settings that will get you this format, but can do it by brute force. Something like this should work:
SELECT
'ID = ' || id || CHR(10) ||
'FIELD1 = ' || field1 || CHR(10) ||
'FIELD2 = ' || field2
FROM my_table
Addendum OP asked if it's possible to make CHR(10) the COLSEP value in SQLPlus. I initially thought "no way", but stumbled across this StackOverflow answer on how to set the COLSEP to a tab, and modified it to do CHR(10). I don't quite understand how/why it works, but it does work:
col NEWLINE# new_value NEWLINE NOPRINT
select chr(10) NEWLINE# from dual;
set colsep "&NEWLINE"

Related

oracle db returns additional padding

Could you advise why on one of our database below query:
select ' ' from dual
returns 32 blank characters:
' '
This happens ONLY on one environment, all other databases returns correctly only one space. I checked nls_parameters, they are the same. Any idea what could be reason for that? Some DB parameter? Some patch was not applied on that instance?
Other example:
select "!" || "!" from dual
produces:
"!! "
so it's !! + 62 spaces
Check database parameter cursor_sharing. What is it set to? Set it to EXACT.
SQL> show parameters cursor_sharing
NAME TYPE VALUE
------------------------------------ ----------- ------------------------
cursor_sharing string EXACT
SQL>

Find value by any column in table

I have columns in table something like
answeremail,
answertime,
ata,
atacomment,
ataid,
atainternalnumber,
atanumber,
atatype,
author,
becomeatafromdeviation,
becomeexternalatafrominternal,
becomefastfromothertype,
briefdescription,
city,
client_answer_attachment,
clientcomment,
confirmstatus,
created_at,
deviation,
deviationnumber,
deviationtype,
duedate,
emailsent,
financeid,
forfortnox,
fromfortnox,
is_deleted,
locked,
name,
parentata,
paymenttype,
pdfurl,
projectid,
quantity,
reason,
revisiondate,
startdate,
status,
street,
suggestion,
token,
type,
unit,
userid,
zip
I want to create SELECT statment to retrive some of this column but without specify column name something like this.
SELECT * FROM ata WHERE 'field' = 'argument'
Is there any solution for this problem or either I need to specify all those column in SELECT statment ?
Not (just) in SELECT, but in WHERE. Otherwise, how will query know which columns to check?
But - beware of datatypes. Oracle will try to implicitly convert one datatype to another. Sometimes, it'll succeed (e.g. to convert number 1 to string '1'), sometimes it'll fail (e.g. convert string 'A' to number or string 'AB23F' to date value).
Therefore, although you can try to write query which will write query for you, it might take some time to actually make it work properly. PL/SQL is probably what you'll end up with.
An example which checks all tables in my schema that contain a column named PHONE_NUMBER and searches for a row whose phone number contains 654. This script returns tables that contain such a value; you'd return something else - list of columns? All columns? Can't tell.
That's just a starting point. Good luck!
DECLARE
l_str VARCHAR2(500);
l_cnt NUMBER := 0;
BEGIN
FOR cur_r IN (SELECT u.table_name, u.column_name
FROM user_tab_columns u, user_tables t
WHERE u.table_name = t.table_name
AND u.column_name = 'PHONE_NUMBER'
)
LOOP
l_str := 'SELECT COUNT(*) FROM ' || cur_r.table_name ||
' WHERE ' || cur_r.column_name || ' like (''%654%'')';
EXECUTE IMMEDIATE (l_str) INTO l_cnt;
IF l_cnt > 0 THEN
dbms_output.put_line(l_cnt ||' : ' || cur_r.table_name);
END IF;
END LOOP;
END;
There is one short solution using xml:
https://dbfiddle.uk/?rdbms=oracle_18&fiddle=e5efc164b77a55b2ecf5d4bf85d589a5
select/*+ no_xml_query_rewrite */ *
from
xmltable(
'for $row in ora:view("SAMPLE_DATA")
let $col := $row/ROW/*[text() eq $search]
where $col ne ""
return element R {
attribute X {$col},
$row
}'
passing
'BB' as "search"
columns
found_col varchar2(30) path '#X'
,row_data xmltype path '.'
);
Results:
FOUND_COL ROW_DATA
------------------------------ ----------------------------------------------------------------------------------------------------
BB <R X="BB"><ROW><COLA>10A</COLA><COLB>BB</COLB><COLC>00</COLC></ROW></R>
BB <R X="BB"><ROW><COLA>10A</COLA><COLB>BB</COLB><COLC>10</COLC></ROW></R>
BB <R X="BB"><ROW><COLA>10A</COLA><COLB>BB</COLB><COLC>20</COLC></ROW></R>
BB <R X="BB"><ROW><COLA>10A</COLA><COLB>BB</COLB><COLC>30</COLC></ROW></R>
BB <R X="BB"><ROW><COLA>10A</COLA><COLB>BB</COLB><COLC>40</COLC></ROW></R>
From your description, I think you want to retrive the columns which spicified by the where clause only, and to omit the other columns.
I don't think this is allowed by simple sql statement. In the oracle document, at the select_list section, I can't see any solution for this question.
see https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/SELECT.html#GUID-CFA006CA-6FF1-4972-821E-6996142A51C6
If you really want, you can use pl/sql.

Header formatting while spooling a csv file in sqlplus

I am required to spool a csv from a table in Oracle, using sqlplus. Following is the format required:
"HOST_SITE_TX_ID","SITE_ID","SITETX_TX_ID","SITETX_HELP_ID"
"664436565","16","2195301","0"
"664700792","52","1099970","0"
Following is the relevant piece of the shell script I wrote:
sqlplus -s $sql_user/$sql_password#$sid << eof >> /dev/null
set feedback off
set term off
set linesize 1500
set pagesize 11000
--set colsep ,
--set colsep '","'
set trimspool on
set underline off
set heading on
--set headsep $
set newpage none
spool "$folder$filename$ext"
select '"'||PCL_CARRIER_NAME||'","'||SITETX_EQUIP_ID||'","'||SITETX_SITE_STAT||'","'||SITETX_CREATE_DATE||'","'||ADVTX_VEH_WT||'"'
from cvo_admin.MISSING_HOST_SITE_TX_IDS;
spool off
(I have used some commented statements in, to signify the things that I tried but couldn't get to work)
The output I receive is:
'"'||PCL_CARRIER_NAME||'","'||SITETX_EQUIP_ID||'","'||SITETX_SITE_STAT||'","'||SITETX_CREATE_DATE||'","'||ADVTX_VEH_WT||'"'
"TRANSPORT INC","113","00000000","25-JAN-13 10.17.51 AM",""
"TRANSPORT INC","1905","00000000","25-JAN-13 05.06.44 PM","0"
Which shows that the header is messed up - it is literally printing the whole string that should have been interpreted as an sql statement, as is the case with the data displayed.
Options I am considering:
1) Using colsep
set colsep '","'
spool
select * from TABLE
spool off
This introduces other problems as the data having leading and trailing spaces, first and the last values in the files are not enclosed by quotes
HOST_SITE_TX_ID"," SITE_ID"
" 12345"," 16"
" 12345"," 21
I concluded that this method gives me more heartburn than the one I described earlier.
2) Getting the file and use a regex to modify the header.
3) Leaving the header altogether and manually adding a header string at the beginning of the file, using a script
Option 2 is more doable, but I was still interested in asking, if there might be a better way to format the header somehow, so it comes in a regular csv, (comma delimited, double quote bounded) format.
I am looking to do as less hard coding as possible - the table I am exporting has around 40 columns and I am currently running the script for around 4 million records - breaking them in a batch of around 10K each. I would really appreciate any suggestions, even totally different from my approach - I am a programmer in learning.
One easy way to have a csv with just one header is to do
set embedded on
set pagesize 0
set colsep '|'
set echo off
set feedback off
set linesize 1000
set trimspool on
set headsep off
the embedded is a hidden option but it is important to have JUST one header
This is how I created a header:
set heading off
/* header */
SELECT '"'||PCL_CARRIER_NAME||'","'||SITETX_EQUIP_ID||'","'||SITETX_SITE_STAT||'","'||SITETX_CREATE_DATE||'","'||ADVTX_VEH_WT||'"'
FROM
(
SELECT 'PCL_CARRIER_NAME' AS PCL_CARRIER_NAME
, 'SITETX_EQUIP_ID' AS SITETX_EQUIP_ID
, 'SITETX_SITE_STAT' AS SITETX_SITE_STAT
, 'SITETX_CREATE_DATE' AS SITETX_CREATE_DATE
, 'ADVTX_VEH_WT' AS ADVTX_VEH_WT
FROM DUAL
)
UNION ALL
SELECT '"'||PCL_CARRIER_NAME||'","'||SITETX_EQUIP_ID||'","'||SITETX_SITE_STAT||'","'||SITETX_CREATE_DATE||'","'||ADVTX_VEH_WT||'"'
FROM
(
/* first row */
SELECT to_char(123) AS PCL_CARRIER_NAME
, to_char(sysdate, 'yyyy-mm-dd') AS SITETX_EQUIP_ID
, 'value3' AS SITETX_SITE_STAT
, 'value4' AS SITETX_CREATE_DATE
, 'value5' AS ADVTX_VEH_WT
FROM DUAL
UNION ALL
/* second row */
SELECT to_char(456) AS PCL_CARRIER_NAME
, to_char(sysdate-1, 'yyyy-mm-dd') AS SITETX_EQUIP_ID
, 'value3' AS SITETX_SITE_STAT
, 'value4' AS SITETX_CREATE_DATE
, 'value5' AS ADVTX_VEH_WT
FROM DUAL
) MISSING_HOST_SITE_TX_IDS;
This is how you add a pipe delimited header to SQL statements. Once you spool it out that "something" wont be there
-- this creates the header
select 'header_column1|header_column2|header_column3' as something
From dual
Union all
-- this is where you run the actual sql statement with pipes in it
select
rev.value1 ||'|'||
rev.value2 ||'|'||
'related_Rel' as something
from
...
In Oracle 19 you can use set markup csv on to ensure that csv outputs are created.
You can also set the delimiter and optional quote or even spool html if you prefer
You can read more here
set markup csv on
spool "$folder$filename$ext"
select q'|wow, I can't beleive he said "hello, how are you?", can you beleive it!|' as text
from dual;
spool off
quit;

Oracle - dynamic column name in select statement

Question:
Is it possible to have a column name in a select statement changed based on a value in it's result set?
For example, if a year value in a result set is less than 1950, name the column OldYear, otherwise name the column NewYear. The year value in the result set is guaranteed to be the same for all records.
I'm thinking this is impossible, but here was my failed attempt to test the idea:
select 1 as
(case
when 2 = 1 then "name1";
when 1 = 1 then "name2")
from dual;
You can't vary a column name per row of a result set. This is basic to relational databases. The names of columns are part of the table "header" and a name applies to the column under it for all rows.
Re comment: OK, maybe the OP Americus means that the result is known to be exactly one row. But regardless, SQL has no syntax to support a dynamic column alias. Column aliases must be constant in a query.
Even dynamic SQL doesn't help, because you'd have to run the query twice. Once to get the value, and a second time to re-run the query with a different column alias.
The "correct" way to do this in SQL is to have both columns, and have the column that is inappropriate be NULL, such as:
SELECT
CASE WHEN year < 1950 THEN year ELSE NULL END AS OldYear,
CASE WHEN year >= 1950 THEN year ELSE NULL END AS NewYear
FROM some_table_with_years;
There is no good reason to change the column name dynamically - it's analogous to the name of a variable in procedural code - it's just a label that you might refer to later in your code, so you don't want it to change at runtime.
I'm guessing what you're really after is a way to format the output (e.g. for printing in a report) differently depending on the data. In that case I would generate the heading text as a separate column in the query, e.g.:
SELECT 1 AS mydata
,case
when 2 = 1 then 'name1'
when 1 = 1 then 'name2'
end AS myheader
FROM dual;
Then the calling procedure would take the values returned for mydata and myheader and format them for output as required.
You will need something similar to this:
select 'select ' || CASE WHEN YEAR<1950 THEN 'OLDYEAR' ELSE 'NEWYEAR' END || ' FROM TABLE 1' from TABLE_WITH_DATA
This solution requires that you launch SQLPLUS and a .sql file from a .bat file or using some other method with the appropriate Oracle credentials. The .bat file can be kicked off manually, from a server scheduled task, Control-M job, etc...
Output is a .csv file. This also requires that you replace all commas in the output with some other character or risk column/data mismatch in the output.
The trick is that your column headers and data are selected in two different SELECT statements.
It isn't perfect, but it does work, and it's the closest to standard Oracle SQL that I've found for a dynamic column header outside of a development environment. We use this extensively to generate recurring daily/weekly/monthly reports to users without resorting to a GUI. Output is saved to a shared network drive directory/Sharepoint.
REM BEGIN runExtract1.bat file -----------------------------------------
sqlplus username/password#database #C:\DailyExtracts\Extract1.sql > C:\DailyExtracts\Extract1.log
exit
REM END runExtract1.bat file -------------------------------------------
REM BEGIN Extract1.sql file --------------------------------------------
set colsep ,
set pagesize 0
set trimspool on
set linesize 4000
column dt new_val X
select to_char(sysdate,'MON-YYYY') dt from dual;
spool c:\DailyExtracts\&X._Extract1.csv
select '&X-Project_id', 'datacolumn2-Project_Name', 'datacolumn3-Plant_id' from dual;
select
PROJ_ID
||','||
replace(PROJ_NAME,',',';')-- "Project Name"
||','||
PLANT_ID-- "Plant ID"
from PROJECTS
where ADDED_DATE >= TO_DATE('01-'||(select to_char(sysdate,'MON-YYYY') from dual));
spool off
exit
/
REM ------------------------------------------------------------------
CSV OUTPUT (opened in Excel and copy/pasted):
old 1: select '&X-Project_id' 'datacolumn2-Project_Name' 'datacolumn3-Plant_id' from dual
new 1: select 'MAR-2018-Project_id' 'datacolumn2-Project_Name' 'datacolumn3-Plant_id' from dual
MAR-2018-Project_id datacolumn2-Project_Name datacolumn3-Plant_id
31415 name1 1007
31415 name1 2032
32123 name2 3302
32123 name2 3384
32963 name3 2530
33629 name4 1161
34180 name5 1173
34180 name5 1205
...
...
etc...
135 rows selected.

ORACLE - Exporting Procedures / Packages to a file

I would like to programmatically export my Procedures / Functions and Packages into individual files (as a backup) and using Oracle 9.2.
The closest solution i found was using DBMS_METADATA.GET_DDL , but how do i output the CLOB to a text file, without losing any parts (due to length or indentation) ?
Or maybe do you have other solutions to backup packages or other functions individually (only the one i want, not all of them) ?
Thanks
Trying to get CLOBS (and LONGS) from command line utilities like SQL*Plus always seems to give me formatting/truncation problems. My solution was to write a simple utility in a non- type checking language (Perl) that uses DBMS_METADATA to bring the CLOB back into a string.
Snippet:
...
$sthRef = $dbhRef->prepare("select dbms_metadata.get_ddl(?,?) from dual");
$sthRef->execute('PACKAGE', $thisName);
while (($thisDDL) = $sthRef->fetchrow()) {
print $thisDDL;
}
$sthRef->finish;
...
If you want to get the DDL, there really is no way except DBMS_METADATA like you already said.
Usually, this kind of a backup is done with exp (or expdp), although this doesn't create a SQL file like you would get with most other DBMS systems.
SET pages 0
spool proclist.sql
SELECT
CASE line
WHEN 1 THEN
'CREATE OR REPLACE ' || TYPE || ' ' || NAME || CHR(10) || text
ELSE
text
END
FROM user_source
WHERE TYPE IN ( 'PROCEDURE','FUNCTION')
ORDER BY name, line;
spool OFF
exit
Thanks goes for RAS , guest for his answer ,
I needed to get codes for some procedures only, so I tried the code , to find that this code truncate the code after procedure name in first line of the code and replace it with three dots '...'
so I changed the code to the following:
SELECT CASE line
WHEN 1 THEN 'CREATE OR REPLACE ' -- || TYPE || ' ' || NAME || --CHR(10) || ' ('
|| text
ELSE
text
END
FROM user_source
WHERE TYPE IN ( 'PROCEDURE') and name like 'SomeThing%'
ORDER BY name, line;
and this page
export procedures & triggers
have a very usefaul code:
connect fred/flintstone;
spool procedures_punch.lst
select
dbms_metadata.GET_DDL('PROCEDURE',u.object_name)
from
user_objects u
where
object_type = 'PROCEDURE';
spool off;
Final way to do it by using Toad Schema Browser , then select all the needed procedures and mouse right click then select export from the menu.

Resources