Using variables in Oracle SQL Developer 3.2 - oracle

I am extremely new to SQL, and manage to extract from some other queries we use the following syntax:
--VARIABLES
undefine PF_PROD --product;
undefine PF_PSG --shop;
--QUERY
SELECT *
FROM ET1250
WHERE PRODUCT=&&PF_PROD
AND PRICE_SHOP_GROUP=&&PF_PSG
ORDER BY PERIOD_YEAR desc,PERIOD_WEEK desc;
This works fine as long as I run the undefine statements first, is there a way to make the query always ask for these variables without me having to undefine them first?

Use a single &. This is covered briefly in the SQL Developer documentation:
For substitution variables, the syntax &&variable assigns a permanent
variable value, and the syntax &variable assigns a temporary (not
stored) variable value.
... and in more detail in the SQL*Plus documentation, which is largely relevant for both clients.
Note that if you define or accept a variable then it won't be prompted for even with a single ampersand, but that doesn't seem to be relevant to you at the moment.

There are two types of variable in SQL-plus: substitution and bind.
Substitution variables can replace SQL*Plus command options or other hard-coded text:
define a = 1;
select &a from dual;
undefine a;
Bind variables store data values for SQL and PL/SQL statements executed in the RDBMS; they can hold single values or complete result setsb:
var x number;
exec :x := 10;
select :x from dual;
exec select count(*) into :x from from dual;
exec print x;
SQL Developer support substitution variables, but when you execute query with bind :var syntax you are prompted for binding (in dialog box).
Reference:
http://www.oracle.com/technetwork/testcontent/sub-var-087723.html SQL*Plus Substitution Variables, Christopher Jones, 2004
https://stackoverflow.com/a/22171706/173149

Related

Toad for Oracle 12.12: What is the difference between Literal and Environment Variable?

I am using the variable &WEIGHT several times in my SQL code. When I am executing, the following window appears requesting me to set the value of my variable.
I have two main questions:
1) What is the difference between the Literal and the Environment Variable?
2) The variable &WEIGHT is stated several times in the SQL code. This means that all these statements will take the same value is that correct?
Littlefoot's explanation of the literal vs. environment variable is spot on. Regarding your second question you will be prompted for a value for each WEIGHT you have declared using a single ampersand. If you want to enter WEIGHT once and retain the same value for each use then use a double ampersand.
Example 1, prompts for WEIGHT twice.
select &WEIGHT, &WEIGHT from dual;
Example 2, prompts for WEIGHT once.
select &&WEIGHT, &&WEIGHT from dual;
The single/double ampersand also applies when you have many statements executed together as a script, F5 shortcut in Toad.
Example 1, prompts for WEIGHT twice, once for each statement.
select &WEIGHT from dual;
select &WEIGHT from dual;
Example 2, prompts for WEIGHT once, once first time it is encountered and that value is reused for every subsequent statement using &&WEIGHT.
select &&WEIGHT from dual;
select &&WEIGHT from dual;
See the "Avoiding Unnecessary Prompts for Values" section in the SQL*Plus user's guide.
A substitution variable(&WEIGHT here) is a placeholder, just as a variable in a program unit, and useful for performance reasons. Since parsing for execution of the select statement is performed once in this case, while for every literal parsing operation is repeated. So, None of each statement will have the same value for each invoke.
Moreover using a literal has vulnerability to sql injection.
P.S. For string type of values, single-quoted '&STR' substitution variables is used.
Literal is something you enter, manually - for example, 10 as DEPTNO from Scott's tables.
Environment variable is what its name says - lets you pick among TOAD or system variables (such as SYSDATE, PATH, TNSADMIN, etc.).
As of your second question, yes - no matter how many statements you have (SQL or PL/SQL), windows, different connections ... if you use the same WEIGHT variable in all of them, they'll get the same value.

Parameterizing adhoc scripts

In SQL server I always had a set of diagnostic scripts and always made sure to declare variables with identifiers so that my other selects and updates would leverage them. I'm having trouble adopting this pattern in Oracle.
I might have 4 or 5 select queries, and then also some updates that I may uncomment once I've verified the results. I want to see the results of the select queries in output.
I am using SQL Developer.
First I tried using a DEFINE block, but it seems this must be paired with BEGIN/END block, and once a query is inside a block, it seems like it becomes cumbersome to view the results. The examples I've seen either involve setting a cursor then iterating over the cursor to print results, or you must print individual values which is even more cumbersome.
So instead I tried using variable's since I can reference them without declare/begin/end, but I am having trouble setting the value of the variable:
variable customerid number;
customerid := 1234;
But I get this error:
Error starting at line : 5 in command - customerid := 1234 Error
report - Unknown Command
I also tried
select t.customerid into :customerid
from customer t
where t.customerid = 1234
and get:
SQL Error: ORA-01006: bind variable does not exist
01006. 00000 - "bind variable does not exist"
My goal is to have my id declarations at the top where I set the values, and be able to run the script and all my adhoc selects appear in output.
You need to set the bind variable in a PL/SQL context, either with an execute syntactic wrapper:
variable customerid number;
exec :customerid := 1234;
or slightly more explicitly:
variable customerid number;
begin
:customerid := 1234;
end;
/
which is (almost) equivalent, but will probably be more convenient if you want to set multiple variables. You can populate the bind variabke from a query too, as you attempted, but that also needs to be in a PL/SQL context:
begin
select t.customerid into :customerid
from customer t
where t.customerid = 1234;
end;
/
Notice the colon before customerid, indicating it is a bind variable, in all of those. You need that when you reference it later, e.g. in a SQL query (which doesn't need to be in a PL/SQL block):
select * from customer where customerid = :customerid;
You can use the same mechanism in your updates later. The exception to using a colon is if you want to just see the value of the variable; you could select :customerid from dual, but there is also the ability to
print customerid
That's even more useful if your variable is a refcursor.
define is a completely different mechanism, for substitution variables rather than bind variables. You don't need to use PL/SQL blocks for this either:
define customerid=1234
select * from customer where customerid = &customerid;
Notice there is no colon this time. And also note that if your variable is a string, you need to enclose it in quotes when you use it:
define name=aaron
select * from users where first_name = '&name';
You can also use the result of a query to populate a substitution variable, using the new_value syntax.

Using PL/SQL variables in OCI results in "ORA-01008: not all variables bound" error

The following (highly contrived and simplified) example runs fine in SQLDeveloper, but results in an ORA-01008 error when run through OCI.
declare
CURRENT_LINE_ID NUMBER := 120;
TARGETVAR NUMBER;
begin
SELECT 1 INTO TARGETVAR FROM DUAL WHERE 120 = :CURRENT_LINE_ID;
end;
Is there any way to restructure this so that the bind variable is satisfied in OCI?
I experimented with substitution variables a little (again works in SQL Developer), but DEFINE appear to be completely invalid in OCI.
DEFINE MYSUBST = 120;
DECLARE
TARGETVAR NUMBER;
BEGIN
SELECT 1 INTO TARGETVAR FROM DUAL WHERE 120 = &MYSUBST;
END;
When you use :CURRENT_LINE_ID NUMBER , OCI looks for that bind variable in your host program only. Here C++. So you should have had this variable declare in your c++ program in a exec declare section or wherever it should be. When you run anything in SQL developer, when encountered a :variable, it blindly prompts the user to enter the value for it, so dont mix it up with the way it do and the oci libraries work.
In your case finally, when a PL/SQL is used and variable is declared there, you can always refer it without colon. If you want to bind it from the hostprogram, you have declare it as host variable. PRO*C supports that. not sure about c++. pro*c is nothing but a embedded sql in C provided by oracle.
Maheswaran's answer led me to search for host variable, which led me to the answer: I was a single colon away from success.
DECLARE
CURRENT_LINE_ID NUMBER := 120;
targetVar NUMBER;
begin
SELECT 1 INTO targetVar FROM DUAL WHERE 120 = CURRENT_LINE_ID;
end;
Although now it turns out that I'm going to have to wrap everything in a stored procedure to actually get the rows back.

Use a database value in SQLPLUS as a parameter to a batch file

I need to run a batch file and pass in a database value. Depending on the day, a different value needs to be passed into the DOS command. This particular business rule is defined in a view in the database.
Our primary scheduling tool is datastage which runs scripts in SQLPLUS, so this is my preferred method for schedules activities. I could also run a batch file directly taht calls SQLPLUS and gets a value, but there is much more hassle setting it up so I'd prefer not to.
I'm a SQLPLUS amateur.
The SQL script I'm passing into SQLPLUS is below. The problem I'm having is it's passing in :sTriggerName, not the value of it.
set echo on
set feedback on
set termout on
VAR sTriggerName VARCHAR2(100)
SELECT TRIGGERNAME INTO :sTriggerName
FROM SCHEMA.VIEW
WHERE CALENDAR_DATE = TRUNC(SYSDATE) AND ROWNUM < 2;
/
HOST "E:\CallScheduleTrigger.CMD :sTriggerName."
quit
In the example above I am using a bind variable.
This link showed me hwo to load a substitution variable from the database and use that instead:
http://www.oracle.com/technetwork/testcontent/sub-var9-086145.html
To load a a database value into a substitution variable called sTriggerName
COLUMN TRIGGERNAME new_value sTriggerName
SELECT TRIGGERNAME FROM SCHEMA.VIEW WHERE CALENDAR_DATE = TRUNC(SYSDATE) AND ROWNUM < 2;
To use this substitution variable in a host command (i.e. as a parameter to a batch file):
HOST "E:\CallScheduleTrigger.CMD &sTriggerName"

Generating SQL*Plus script using SQL*Plus

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;
/

Resources