How to echo text during SQL script execution in SQLPLUS - sql

I have a batch file which runs a SQL script in sqlplus and sends the output to a log file:
sqlplus user/pw < RowCount.sql > RowCount.log
My log file contains this:
Connected to:
Oracle Database 11g Express Edition Release 11.2.0.2.0 - Production
SQL> SQL>
COUNT(*)
----------
0
SQL>
COUNT(*)
----------
0
etc. but it's several thousand lines of output and therefore hard to determine which results belong to which statement.
I would like to add some formatting to the output, so that I may discern what happened. Either an echo of the executed statement or manually inserting some "echo" statements into the script would be fine. Ideally it would look something like this:
SQL> select(*) from TableA;
COUNT(*)
----------
0
SQL> select(*) from TableB;
COUNT(*)
----------
0

The prompt command will echo text to the output:
prompt A useful comment.
select(*) from TableA;
Will be displayed as:
SQL> A useful comment.
SQL>
COUNT(*)
----------
0

You can use SET ECHO ON in the beginning of your script to achieve that, however, you have to specify your script using # instead of < (also had to add EXIT at the end):
test.sql
SET ECHO ON
SELECT COUNT(1) FROM dual;
SELECT COUNT(1) FROM (SELECT 1 FROM dual UNION SELECT 2 FROM dual);
EXIT
terminal
sqlplus hr/oracle#orcl #/tmp/test.sql > /tmp/test.log
test.log
SQL>
SQL> SELECT COUNT(1) FROM dual;
COUNT(1)
----------
1
SQL>
SQL> SELECT COUNT(1) FROM (SELECT 1 FROM dual UNION SELECT 2 FROM dual);
COUNT(1)
----------
2
SQL>
SQL> EXIT

You can change the name of the column, therefore instead of "COUNT(*)" you would have something meaningful. You will have to update your "RowCount.sql" script for that.
For example:
SQL> select count(*) as RecordCountFromTableOne from TableOne;
Will be displayed as:
RecordCountFromTableOne
-----------------------
0
If you want to have space in the title, you need to enclose it in double quotes
SQL> select count(*) as "Record Count From Table One" from TableOne;
Will be displayed as:
Record Count From Table One
---------------------------
0

Related

not able to export table into csv format

I am trying to export table into csv format as below:
SQL> desc test;
Name Null? Type
----------------------------------------- -------- ----------------------------
DN NUMBER(10)
DISCONNECT_DATE DATE
SQL> select DN ,DISCONNECT_DATE from test into OUTFILE '/tmp/data.csv';
select DN ,DISCONNECT_DATE from test into OUTFILE '/tmp/data.csv'
*
ERROR at line 1:
ORA-00933: SQL command not properly ended
could you please anyone help me to resolved above problem.
I got the answer if we are using sql plus then we need use spool to get data into csv format. Below is the steps ...(don't forget to spool off after execution of query. )
SQL> set colsep ,
SQL> set headsep off
SQL> set pagesize 0
SQL> set trimspool on
SQL> spool /tmp/data.csv
SQL> select * from test;
-----------------(we cant placed the data)
10 rows selected.
SQL> spool off

Fetch changed data for table in Oracle Database to csv

I Need to fetch the changed data for table in oracle. Need help with the sql query which will fetch only the changed data for every 20 mins.
Or is there another workaround for this where we can fetch changed data?
If you know how to detect "only changed data" - I presume you must have some kind of a "timestamp" column - then that would be
select columns
from your_table
where timestamp_column >= sysdate - 20/(24*60);
because of this: 20 minutes / (24 hours in a day * 60 minutes in an hour)
SQL> alter session set nls_date_format = 'dd.mm.yyyy hh24:mi:ss'
Session altered.
SQL> select sysdate right_now,
2 sysdate - 20/(24*60) twenty_minutes_ago
3 from dual;
RIGHT_NOW TWENTY_MINUTES_AGO
------------------- -------------------
24.10.2019 08:40:18 24.10.2019 08:20:18
SQL>
How to create a CSV?
One option is to store your query into a .SQL file and use operating system's Task Scheduler (for MS Windows) or cron (for Unix) or whatever your operating system is. Let the script spool the result. There are numerous SQL*Plus SET options you can use to make the output pretty; for example:
set termout off
set trimspool on
set echo off
set verify off
set autoprint off
set serveroutput off
set arraysize 1000
set pagesize 0
set linesize 100
set long 10000
set numwidth 10
set feedback off
set colsep ';'
col empno format 99999
col ename format a10
col sal format 999G990
spool emps.txt
select empno, ename, sal from emp
where timestamp_column >= sysdate - 20/(24*60);
spool off
Another option is to create a stored procedure which will use UTL_FILE package and create that file in a directory (you'll have to talk to your DBA about it; it is - usually - on the database server; DBA should create a directory (as an Oracle object) and grant read/write privileges to you).
Then you'd create a database job (using DBMS_JOB or DBMS_SCHEDULER packages), scheduling it to run every 20 minutes.
Maybe the first one is simpler to implement; if nothing else, CSV is created on your local computer, not on the server.
You need a last_updated column of type datetime/date in your table. Then to get updated in last 20 minutes, the query can become like below.
select t.*
from table t
where t.last_updated >= sysdate - 20/(24*60)
where t is your tablename and last_updated is last updated datetime.
If you don't have a way to detect when rows change within the table then you can use a flashback query on your database (if it is set up to support it).
To get the new/modified rows:
SELECT *
FROM table_name
MINUS
SELECT *
FROM table_name AS OF TIMESTAMP( SYSTIMESTAMP - INTERVAL '20' MINUTE )
and to get the deleted rows:
SELECT *
FROM table_name AS OF TIMESTAMP( SYSTIMESTAMP - INTERVAL '20' MINUTE )
WHERE primary_key_column NOT IN ( SELECT primary_key_column FROM table_name )

Setting NLS_SORT variable for a single select only

Good day,
my customer uses an application that was initially designed for MSSQL, which is probably doing case-insensitive searches by default. But the customer uses Oracle and hence, needs some extra tweaking.
So the question is: How can I tell Oracle to make a given SELECT LIKE-Statement search case-insensitive with the following limitations?
ALTER SESSION cannot be used individually (by trigger: maybe)
Other queries from the same session must not be affected
The SELECT-statement cannot be altered
I know about the possibility to set NLS_SORT on system level, but this will basically kill the performance, as all indexes are disabled.
You can use DBMS_ADVANCED_REWRITE to rewrite the SQL into a case-insensitive version.
Subtly changing queries like this can be confusing and can make troubleshooting and tuning difficult. The package also has some limitations that may make it impractical, such as not supporting bind variables.
1. Sample Schema
SQL> drop table test1;
Table dropped.
SQL> create table test1(a varchar2(100));
Table created.
SQL> insert into test1 values ('case INSENSITIVE');
1 row created.
SQL> commit;
Commit complete.
2. The query is initially case-sensitive and matches 0 rows
SQL> select count(*) total from test1 where a like '%case insensitive%';
TOTAL
----------
0
3. Create rewrite equivalence - add a LOWER function
SQL> begin
2 sys.dbms_advanced_rewrite.declare_rewrite_equivalence(
3 name => 'case_insensitive_1',
4 source_stmt => q'[select count(*) total from test1 where a like '%case insensitive%']',
5 destination_stmt => q'[select count(*) total from test1 where lower(a) like '%case insensitive%']',
6 validate => false
7 );
8 end;
9 /
PL/SQL procedure successfully completed.
4. Now the same query is case-insensitive and matches 1 row
SQL> alter session set query_rewrite_integrity = trusted;
Session altered.
SQL> select count(*) total from test1 where a like '%case insensitive%';
TOTAL
----------
1

force subquery resolution first

I'm creating a query which uses 2 embedded server functions multiple times.
Problem: the functions search through a decently large table, and they take a long time to execute.
Goal: Use a subquery as if it were a table so that I can reference columns without running the function to generate the column more than once.
Example Pseudocode:
Select general.column1, general.column2, general.column1-general.column2
from (select package.function1('I take a long time') column1,
package.function2('I take even longer') column2,
normal_column
from bigtable) general;
When I run my code general.column1 will reference the function in the statement of column1, not the data returned by it (which is ultimately what I'm after).
I'm fairly new to SQL, so any help is appreciated and if you need more info, I'll do my best to provide it.
Thanks!
I suggest you tu use the subquery factoring. The first subquery will be executed only once and then used through the rest of he query.
WITH function_result AS
(SELECT package.function1('I take a long time') column1
, package.function2('I take even longer') column2
FROM dual)
SELECT function_result.column1
, function_result.column2
, function_result.column1 - function_result.column2
, bigtable.normal_column
FROM bigtable
In general what you want to do is in this case is take advatage of scalar subquery caching.
i.e. put:
Select general.column1, general.column2, general.column1-general.column2
from (select (select package.function1('I take a long time') from dual) column1,
(select package.function2('I take even longer') from dual) column2,
normal_column
from bigtable) general;
delcaring the function as deterministic too helps if it is deterministic.
a small example:
SQL> create or replace function testfunc(i varchar2)
2 return varchar2
3 is
4 begin
5 dbms_application_info.set_client_info(userenv('client_info')+1 );
6 return 'hi';
7 end;
8 /
Function created.
now lets test a call to the function like you have:
SQL> exec dbms_application_info.set_client_info(0);
PL/SQL procedure successfully completed.
SQL> set autotrace traceonly
SQL> select *
2 from (select testfunc(owner) a
3 from all_objects);
57954 rows selected.
SQL> select userenv('client_info') from dual;
USERENV('CLIENT_INFO')
----------------------------------------------------------------
57954
the function was called 57954 times (once per row). now lets use scalar caching:
SQL> exec dbms_application_info.set_client_info(0);
PL/SQL procedure successfully completed.
SQL> select *
2 from (select (select testfunc(owner) from dual) a
3 from all_objects);
57954 rows selected.
SQL> select userenv('client_info') from dual;
USERENV('CLIENT_INFO')
----------------------------------------------------------------
178
178 calls instead of 57k!
in your case you've only shown that you have a literal and no input that is varying per row (if this is the case, the number of calls after using scalar caching should be 1).
if we add deterministic:
SQL> create or replace function testfunc(i varchar2)
2 return varchar2 deterministic
3 is
4 begin
5 dbms_application_info.set_client_info(userenv('client_info')+1 );
6 return 'hi';
7 end;
8 /
Function created.
SQL> exec dbms_application_info.set_client_info(0);
PL/SQL procedure successfully completed.
SQL> select *
2 from (select (select testfunc(owner) from dual) a
3 from all_objects);
57954 rows selected.
SQL> select userenv('client_info') from dual;
USERENV('CLIENT_INFO')
----------------------------------------------------------------
55
now down to 55. in 11g we have result_cache which we can put in place of deterministic, which would reduce the calls on subsequant runs to 0 calls.

How to declare variable and use it in the same Oracle SQL script?

I want to write reusable code and need to declare some variables at the beginning and reuse them in the script, such as:
DEFINE stupidvar = 'stupidvarcontent';
SELECT stupiddata
FROM stupidtable
WHERE stupidcolumn = &stupidvar;
How can I declare a variable and reuse it in statements that follow such as in using it SQLDeveloper.
Attempts
Use a DECLARE section and insert the following SELECT statement in BEGIN and END;. Acces the variable using &stupidvar.
Use the keyword DEFINE and access the variable.
Using the keyword VARIABLE and access the the variable.
But I am getting all kinds of errors during my tries (Unbound variable, Syntax error, Expected SELECT INTO...).
There are a several ways of declaring variables in SQL*Plus scripts.
The first is to use VAR, to declare a bind variable. The mechanism for assigning values to a VAR is with an EXEC call:
SQL> var name varchar2(20)
SQL> exec :name := 'SALES'
PL/SQL procedure successfully completed.
SQL> select * from dept
2 where dname = :name
3 /
DEPTNO DNAME LOC
---------- -------------- -------------
30 SALES CHICAGO
SQL>
A VAR is particularly useful when we want to call a stored procedure which has OUT parameters or a function.
Alternatively we can use substitution variables. These are good for interactive mode:
SQL> accept p_dno prompt "Please enter Department number: " default 10
Please enter Department number: 20
SQL> select ename, sal
2 from emp
3 where deptno = &p_dno
4 /
old 3: where deptno = &p_dno
new 3: where deptno = 20
ENAME SAL
---------- ----------
CLARKE 800
ROBERTSON 2975
RIGBY 3000
KULASH 1100
GASPAROTTO 3000
SQL>
When we're writing a script which calls other scripts it can be useful to DEFine the variables upfront. This snippet runs without prompting me to enter a value:
SQL> def p_dno = 40
SQL> select ename, sal
2 from emp
3 where deptno = &p_dno
4 /
old 3: where deptno = &p_dno
new 3: where deptno = 40
no rows selected
SQL>
Finally there's the anonymous PL/SQL block. As you see, we can still assign values to declared variables interactively:
SQL> set serveroutput on size unlimited
SQL> declare
2 n pls_integer;
3 l_sal number := 3500;
4 l_dno number := &dno;
5 begin
6 select count(*)
7 into n
8 from emp
9 where sal > l_sal
10 and deptno = l_dno;
11 dbms_output.put_line('top earners = '||to_char(n));
12 end;
13 /
Enter value for dno: 10
old 4: l_dno number := &dno;
new 4: l_dno number := 10;
top earners = 1
PL/SQL procedure successfully completed.
SQL>
Try using double quotes if it's a char variable:
DEFINE stupidvar = "'stupidvarcontent'";
or
DEFINE stupidvar = 'stupidvarcontent';
SELECT stupiddata
FROM stupidtable
WHERE stupidcolumn = '&stupidvar'
upd:
SQL*Plus: Release 10.2.0.1.0 - Production on Wed Aug 25 17:13:26 2010
Copyright (c) 1982, 2005, Oracle. All rights reserved.
SQL> conn od/od#etalon
Connected.
SQL> define var = "'FL-208'";
SQL> select code from product where code = &var;
old 1: select code from product where code = &var
new 1: select code from product where code = 'FL-208'
CODE
---------------
FL-208
SQL> define var = 'FL-208';
SQL> select code from product where code = &var;
old 1: select code from product where code = &var
new 1: select code from product where code = FL-208
select code from product where code = FL-208
*
ERROR at line 1:
ORA-06553: PLS-221: 'FL' is not a procedure or is undefined
In PL/SQL v.10
keyword declare is used to declare variable
DECLARE stupidvar varchar(20);
to assign a value you can set it when you declare
DECLARE stupidvar varchar(20) := '12345678';
or to select something into that variable you use INTO statement, however you need to wrap statement in BEGIN and END, also you need to make sure that only single value is returned, and don't forget semicolons.
so the full statement would come out following:
DECLARE stupidvar varchar(20);
BEGIN
SELECT stupid into stupidvar FROM stupiddata CC
WHERE stupidid = 2;
END;
Your variable is only usable within BEGIN and END so if you want to use more than one you will have to do multiple BEGIN END wrappings
DECLARE stupidvar varchar(20);
BEGIN
SELECT stupid into stupidvar FROM stupiddata CC
WHERE stupidid = 2;
DECLARE evenmorestupidvar varchar(20);
BEGIN
SELECT evenmorestupid into evenmorestupidvar FROM evenmorestupiddata CCC
WHERE evenmorestupidid = 42;
INSERT INTO newstupiddata (newstupidcolumn, newevenmorestupidstupidcolumn)
SELECT stupidvar, evenmorestupidvar
FROM dual
END;
END;
Hope this saves you some time
If you want to declare date and then use it in SQL Developer.
DEFINE PROPp_START_DT = TO_DATE('01-SEP-1999')
SELECT *
FROM proposal
WHERE prop_start_dt = &PROPp_START_DT
The question is about to use a variable in a script means to me it will be used in SQL*Plus.
The problem is you missed the quotes and Oracle can not parse the value to number.
SQL> DEFINE num = 2018
SQL> SELECT &num AS your_num FROM dual;
old 1: SELECT &num AS your_num FROM dual
new 1: SELECT 2018 AS your_num FROM dual
YOUR_NUM
----------
2018
Elapsed: 00:00:00.01
This sample is works fine because of automatic type conversion (or whatever it is called).
If you check by typing DEFINE in SQL*Plus, it will shows that num variable is CHAR.
SQL>define
DEFINE NUM = "2018" (CHAR)
It is not a problem in this case, because Oracle can deal with parsing string to number if it would be a valid number.
When the string can not parse to number, than Oracle can not deal with it.
SQL> DEFINE num = 'Doh'
SQL> SELECT &num AS your_num FROM dual;
old 1: SELECT &num AS your_num FROM dual
new 1: SELECT Doh AS your_num FROM dual
SELECT Doh AS your_num FROM dual
*
ERROR at line 1:
ORA-00904: "DOH": invalid identifier
With a quote, so do not force Oracle to parse to number, will be fine:
17:31:00 SQL> SELECT '&num' AS your_num FROM dual;
old 1: SELECT '&num' AS your_num FROM dual
new 1: SELECT 'Doh' AS your_num FROM dual
YOU
---
Doh
So, to answer the original question, it should be do like this sample:
SQL> DEFINE stupidvar = 'X'
SQL>
SQL> SELECT 'print stupidvar:' || '&stupidvar'
2 FROM dual
3 WHERE dummy = '&stupidvar';
old 1: SELECT 'print stupidvar:' || '&stupidvar'
new 1: SELECT 'print stupidvar:' || 'X'
old 3: WHERE dummy = '&stupidvar'
new 3: WHERE dummy = 'X'
'PRINTSTUPIDVAR:'
-----------------
print stupidvar:X
Elapsed: 00:00:00.00
There is an other way to store variable in SQL*Plus by using Query Column Value.
The COL[UMN] has new_value option to store value from query by field name.
SQL> COLUMN stupid_column_name new_value stupid_var noprint
SQL> SELECT dummy || '.log' AS stupid_column_name
2 FROM dual;
Elapsed: 00:00:00.00
SQL> SPOOL &stupid_var.
SQL> SELECT '&stupid_var' FROM DUAL;
old 1: SELECT '&stupid_var' FROM DUAL
new 1: SELECT 'X.log' FROM DUAL
X.LOG
-----
X.log
Elapsed: 00:00:00.00
SQL>SPOOL OFF;
As you can see, X.log value was set into the stupid_var variable, so we can find a X.log file in the current directory has some log in it.
Just want to add Matas' answer.
Maybe it's obvious, but I've searched for a long time to figure out that the variable is accessible only inside the BEGIN-END construction, so if you need to use it in some code later, you need to put this code inside the BEGIN-END block.
Note that these blocks can be nested:
DECLARE x NUMBER;
BEGIN
SELECT PK INTO x FROM table1 WHERE col1 = 'test';
DECLARE y NUMBER;
BEGIN
SELECT PK INTO y FROM table2 WHERE col2 = x;
INSERT INTO table2 (col1, col2)
SELECT y,'text'
FROM dual
WHERE exists(SELECT * FROM table2);
COMMIT;
END;
END;
In Toad I use this works:
declare
num number;
begin
---- use 'select into' works
--select 123 into num from dual;
---- also can use :=
num := 123;
dbms_output.Put_line(num);
end;
Then the value will be print to DBMS Output Window.
Reference to here and here2.
Here's your answer:
DEFINE num := 1; -- The semi-colon is needed for default values.
SELECT &num FROM dual;
You can use a with clause and move filter criteria from a where to a join.
It helps here: Oracle SQL alternative to using DEFINE.
with
mytab as (select 'stupidvarcontent' as myvar from dual)
SELECT
stupiddata
FROM
stupidtable a
inner join
mytab b
on
a.stupidcolumn = b.myvar
WHERE ...;
It works in Oracle 12R2.
It works for one SQL command only.
It is standard ANSI notation.
I'm using it in SQL Developer.
One possible approach, if you just need to specify a parameter once and replicate it in several places, is to do something like this:
SELECT
str_size /* my variable usage */
, LPAD(TRUNC(DBMS_RANDOM.VALUE * POWER(10, str_size)), str_size, '0') rand
FROM
dual /* or any other table, or mixed of joined tables */
CROSS JOIN (SELECT 8 str_size FROM dual); /* my variable declaration */
This code generates a string of 8 random digits.
Notice that I create a kind of alias named str_size that holds the constant 8. It is cross-joined to be used more than once in the query.
Sometimes you need to use a macro variable without asking the user to enter a value. Most often this has to be done with optional script parameters. The following code is fully functional
column 1 noprint new_value 1
select '' "1" from dual where 2!=2;
select nvl('&&1', 'VAH') "1" from dual;
column 1 clear
define 1
Similar code was somehow found in the rdbms/sql directory.