I want to generate a whole lot of SQL*Plus scripts by querying the data dictionary, but I'm hitting some problems and suspect I'm missing something obvious.
For example, when I execute the following in SQL*Plus I get ORA-01756: quoted string not properly terminated:
SQL> SPOOL myscript.sql
SQL> SELECT q'[SPOOL log
2 SELECT COUNT(*) FROM DUAL;
ERROR:
ORA-01756: quoted string not properly terminated
I tried using the line continuation character to avoid this error, but it puts the continuation character into the output:
SQL> SELECT q'[SPOOL log
2 SELECT COUNT(*) FROM DUAL; -
3 PROMPT Done.
4 ]' FROM DUAL;
SPOOL log
SELECT COUNT(*) FROM DUAL; -
PROMPT Done.
Notice how the output has the - after DUAL;? I don't want that in the generated script.
One way I can get around this is to concatenate a lot of CHR() function calls to generate semicolons and linefeeds; but I hope I don't have to because these scripts being generated are very long, and having bits like ]'||CHR(59)||CHR(10)||q'[ scattered throughout the code makes it look very ugly and a pain to troubleshoot.
(I'm using SQL*Plus Release 11.2.0.1.0 Production, connecting to an 11gR2 instance.)
The problem is that SQL*Plus is interpreting your first ; as the terminator for the command. You may have noticed that if you write your commands to a text file and execute that (or edit it in a text editor from with SQL*Plus) it works.
To make it work with live typing, if you really want to do that (seems unlikely if they're going to be very long!), you can turn off the automatic detection of the terminator with SET SQLTERMINATOR off. Note that you'll have to tell SQL*Plus that you're done and that it should execute with the / instruction as the second ; is ignored as well.
SQL> SPOOL myscript.sql
SQL> SET SQLTERMINATOR off
SQL> SELECT q'[SPOOL log
2 SELECT COUNT(*) FROM DUAL;
3 PROMPT Done.
4 ]' FROM DUAL
5 /
SPOOL log
SELECT COUNT(*) FROM DUAL;
PROMPT Done.
If you're building these from the data dictionary, another option is to use PL/SQL to do the queries and manipulations and dbms_output to produce the output you're gong to spool, as long as the final file size won't exceed the buffer limits.
When I want to create a script from within the DB I tend to prefer writing a file using the UTL_FILE package instead of spooling the output of SQL*Plus. It isn't exactly what you want, but I find the control to be far less troublesome than trying to write sql scripts that format properly.
You can use getddl in dbms_metada package or mine package:
http://github.com/xtender/XT_SVN
You need to see http://download.oracle.com/docs/cd/A97630_01/server.920/a90842/ch13.htm
SET CMDS[EP] {;|c|ON|OFF}
Sets the non-alphanumeric character used to separate multiple SQL*Plus commands entered on one line to c. ON or OFF controls whether you can enter multiple commands on a line. ON automatically sets the command separator character to a semicolon (;).
For future reference for myself, instead of messing around with SET SQLTERMINATOR off when using sql plus use the following bellow so you don't need to worry about the any special sql terminator character inside the string literal body.
BEGIN
INSERT INTO SOME_TABLE (q'[
Now;
You;
Can '
Do "'"';' ;;;
any character? *
]');
END;
/
Related
My code currently that I am running in the Command Window in PL/SQL Developer:
SET MARKUP CSV ON DELIMITER | QUOTE OFF
SET FEEDBACK OFF
SPOOL C:\Users\Desktop\SpoolTest.csv
select *
from Vendor_data t
where rownum < 20
;
SPOOL OFF;
My output:
222339 |067 |001
306811 |045 |001
024253 |067 |001
I need to remove the trailing spaces:
222339|067|001
306811|045|001
024253|067|001
I am getting Cannot SET MARKUP. I have to run my spooling in a command window in PL/SQL Developer. Is there a way to do this? This is a snippet of code which is actually quite long.
SET commands belong to Oracle's command line tool, SQL*Plus.
I don't use PL/SQL Developer (and quick Google doesn't return anything useful), so - does PL/SQL Developer support SET commands at all? If not, you're out of luck, but you could install Oracle Client (if you already don't have it) and run your script directly in SQL*Plus.
On the other hand & unless I'm wrong, SET MARKUP CSV is valid for Oracle 18c and above; in lower versions, it is SET MARKUP HTML you can use (i.e. no CSV option).
The PL/SQL Developer's command-line tool is a different product than Oracle SQL*Plus command-line utility, and it has a different set of supported commands. PL/SQL Developer's command line mimics a real SQL*Plus functionality but does it only partially. For example, it doesn't support the SET MARKUP CSV command. You can find the full list of supported commands in the PL/SQL Developer's Manual (Help -> User's Guide).
In your case, trailing spaces appear because you are printing headers of columns. Apparently, PL/SQL Developer tries to align rows by the maximum length of the column:
spool file.txt
select ao.object_name, ao.object_id, ao.created, ao.status
from all_objects ao
where rownum <= 3;
OBJECT_NAME OBJECT_ID CREATED STATUS
-------------------------------------------------------------------------------- ---------- ----------- -------
TS$ 16 26.01.2017 VALID
ICOL$ 20 26.01.2017 VALID
Keeping in mind that PL/SQL Developer's supports of SET commands is very limited, we can achieve the CSV like spooling by simply not printing headers and setting colwidth to 1:
spool file.txt
set colsep |
set heading off
set colwidth 1
select ao.object_name, ao.object_id, ao.created, ao.status
from all_objects ao
where rownum <= 3;
spool off
it saves this to the spool file:
TS$|16|26.01.2017 13:52:45|VALID
ICOL$|20|26.01.2017 13:52:45|VALID
C_FILE#_BLOCK#|8|26.01.2017 13:52:45|VALID
If you must print the headers, you can add an additional select from dual before the main sql just to print headers.
I'm trying to use the inbuilt oracle function to replace '&' with &. I wrote two functions below but it's not working for me. On running these two in sql developer tool its asking me for input. My requirement is to replace html entities.
select REPLACE('&', '&', '&') from DUAL;
select regexp_replace('&', '&', '&') from DUAL;
Could any one please tell me what's wrong I am doing?.
This should probably be marked as a duplicate, but you need to add this to your script
SET SCAN OFF - that tells us to ignore the & which is used for replacing text when running code in SQLPlus
Once you have that disabled, you can run the queries one at a time with data grids (the first execution mode) or as sqlplus scripts (the second execution mode).
You should execute set define off before executing your query to suppress processing of substitution variables which start by &.
The command set define off will stay active until you enter set define on on your sqlplus session or sql developer worksheet.
thatjeffsmith answer was right but set scan off is tagged obsolete by Oracle. Anyway, it still work.
I'm spooling from a SQL*Plus script many SELECTs to separate CSVs, most of which JOIN on the same SELECT, which I've placed as a WITH before the SELECT in each SPOOL in order to at least be able to find&replace easily.
Now I'm trying to rid the script of the code repetition and to optimize for performance by maybe storing the results of the repeated SELECT and reusing them in each SPOOL.
I've tried using variables to store the results of the SELECT that is repeated as WITH, but I can't seem to be able to mix SQL*Plus SPOOLs with PL/SQL code (using SPOOL inside DECLARE BEGIN/END block fails with only the number "28" being output on the command-line). My first thought was something like temporary tables were needed, but there's only read-only access, so no DDL like CREATE/ALTER, etc.
SET MARKUP CSV ON
-- omitting delimiter and other SQL*Plus options set
SPOOL C:\test\1.csv
WITH Y AS ( SELECT * FROM Z )
SELECT * FROM X JOIN Y ON Y.W = X.W
;
SPOOL OFF
SPOOL C:\test\2.csv
WITH Y AS ( SELECT * FROM Z )
SELECT * FROM W JOIN Y ON Y.X = W.X
;
SPOOL OFF
-- many other different spools joining on the same WITH SELECT
exit;
This code works fine, but I'd prefer to not have to repeat the same WITH as it kills performance and makes the code a chore to maintain / work on. However, the main concern here is the repetition of code and not necessarily the performance hit, at least for now, although it is an ugly thing to have the server repeat the same queries over and over.
Obviously, I'm not literate in Oracle / PL/SQL.
It would help if you (or someone else) could create a view as
create view v_y
as
SELECT * FROM Z --> this is your "Y" CTE
I presume that SELECT * FROM Z is more complex than this simple statement so - if nothing else - your code would look prettier. See if a DBA or such could create a view for you.
It also means that you'd avoid CTE completely and join tables to the v_y view directly.
... fails with only the number "28" being output on the command-line
It means that you should terminate that PL/SQL block with a slash. Or, simply enter slash now and press enter:
28 / --> now press enter
Anyway, SPOOL - which is a SQL*Plus command - won't work in PL/SQL procedures. But, you could create a file using UTL_FILE package. It isn't as simple as SPOOL as you should be granted to use that package, DBA should create a directory (Oracle object) which points to a directory on database server's hard disk, grant you privileges to access it (the directory). That is far from being impossible, but you can't do it yourself.
Perhaps you'd rather ask them to create that view for you, eh?
To give an answer to the following:
but I can't seem to be able to mix SQL*Plus SPOOLs with PL/SQL code
(using SPOOL inside DECLARE BEGIN/END block fails with only the number
"28" being output on the command-line)
You can use the Spool and PL/SQL block using the following technique:
-- test.sql file
spool tejash_1.txt
declare
cursor c_emp is
select seq, req from table1;
begin
for r_emp in c_emp loop
dbms_output.put_line(r_emp.seq || ' ' || r_emp.req);
end loop;
end;
/
spool off
-- end of test.sql file
-- Command to execute in SQL*Plus
SQL> set serverout on
SQL> SET echo off
SQL> SET feedback off
SQL> SET term off
SQL> #test.sql
-- output in tejash_1.txt file
001 X1
002 X1
003 X1
004 X1
-- End of output in tejash_1.txt file
Cheers!!
I have SQL file with few commands.
What it the correct way to end lines in the script?
Is it slash [/] semicolon [;] both?
Is there any diffarent between regular sqls and Stored procedure code?
Thank you
For normal SQL statements, either a / on a line by itself, or a ; at the end of the command, will work fine.
For statements that include PL/SQL code, such as CREATE FUNCTION, CREATE PROCEDURE, CREATE PACKAGE, CREATE TYPE, or anonymous blocks (DECLARE/BEGIN/END), a ; will not execute the command. Since PL/SQL uses semicolons as line terminators, its use as a command terminator must be suppressed in these statements. So in these cases, you must use / to execute the command.
In my experience, people prefer to use the semicolon when possible and use the slash only when required.
Note that for SQLPlus client commands -- such as SET or EXECUTE -- no command terminator is necessary at all, although people often end them with a semicolon out of habit.
; is the way you should end your sql commands, same goes for PLSQL procedures:
select * from dual;
select sysdate from dual;
select table_name from user tables;
exec dbms_output.putline('Hello');
Usually we can use ";" to end sql statement,
but for create functions, triggers, procedures you have to use "/" after ";" to end SQL statement.
"/" might not be required when you use some developers tool like, oracle SQL developer, toad etc, but it should be mandate when you execute your query directly in the linux machine.
I have a view whose DDL definition is many thousands of lines long. Part of our CI process is to drop and recreate views from DDL using SQLPlus called from a command line script.
This works for hundreds of views in the database but the very large view is never created in the target schema. I always manually paste the view creation script into Toad and run it manually after the automated process has completed. This is a drag.
There is no meaningful error message from SQLPlus when the large-view portion of the DDL script is run but I suspect that it fails because of it's size.
Is there a "set" command that I can include at the top of my DDL to tell SQLPlus that it's ok to create large views or am I forever doomed to include a stoopid manual step in the otherwise automatic CI process?
Firstly, use the most recent version of SQLPlus. Its been a long time since I had a piece of code that was too large to be executed through SQLPlus. You can use the InstantClient
I'd also look at re-factoring the view. Look at the WITH clause as that is relatively new and, if the view has evolved over a long period, there's a good chance it can be amended to make use of this
Is there an empty line in the view SQL, or does any line have more than 2499 characters? Either one of these may cause SQL*Plus to behave unexpectedly but not actually fail.
If there is an empty line, Oracle will ignore everything before it and try to run everything after it. (This only applies to SQL, not PL/SQL.) For example, if you have an empty line right after the create view line, the query will run:
SQL> create or replace view newline_in_the_middle as
2
SQL> select * from dual;
D
-
X
A line with >2499 characters will be ignored but Oracle will still try to process the statement without it. This can cause problems but may still result in a valid statement:
SQL> create or replace view long_line as
2 select '...[enter 2500 characters]...' asdf from dual union all
SP2-0027: Input is too long (> 2499 characters) - line ignored
2 select '1' asdf from dual;
View created.
You may have to check the script output very carefully to find these issues.