I am transitioning from SQL Server to Vertica. Is there any comparable way to create a variable?
Usually I do something like:
Define #myVariable int
Set #myVariable = select MAX(Field1) from myTable
I do not think Vertica allows variables, except if you are using vsql directly, but then vsql variables are very limited and will not do what you expect:
-- this will work
\set a foo
\echo :a
foo
-- this is not what you expect:
\set a NOW()
\echo :a
NOW()
\set a select max(id) from s.items()
\echo :a
selectmax(id)froms.items()
See for more information the vertica doc at https://my.vertica.com/docs/6.1.x/HTML/index.htm#2732.htm
You do not "create variables" in Vertica the same way you do not "create variables" in SQL Server. What you're trying to convert is a T-SQL script.
You can do the same in Vertica by creating Perl or Python or Java ... scripts running outside the database or writing a user defined function in C++ or R or Java running inside Vertica.
You can use :variable_name in Vertica for a user input variable. For example:
select date_time from table_1 where date_time between :start and :end
In above start and end are the variables. When you run the query, a dialog box opens prompting you to enter the values for start and end.
If you are using vsql, you can create variables that contain results of queries, though it is a bit convoluted:
Lets assume you start vsql with vsql -v INARG=33;
SELECT :INARG+1; -- Set up the query
\pset format u
\pset t -- Update output format to bare
\g `echo /tmp/dyneval` -- Eval the query and write into file
\set DYNARG `cat /tmp/dyneval` -- Set var from shell command output
\echo :DYNARG
So basically we write the query result into a file and read the file's contents into a variable.
You can use /tmp/dyneval-${PPID}_id instead of /tmp/dyneval, so you can ensure, that parallel executions wont alter each other. (PPID being the parent process's process ID, that is the vsql process's PID.)
The solution has some limitations:
assumes a linux env (echo, cat)
it changes the output formatting settings (\pset)
Related
In SQL Developer,
I want to call a script like this:
#"path\to\script.sql" develop
and use the value develop in my script as a table prefix like this:
SELECT * FROM <parameter>_table; which should then evaluate to SELECT * FROM develop_table
Is something like this possible in SQL Developer?
See the article below. It will answer your question.
https://blogs.oracle.com/opal/sqlplus-101-substitution-variables#2_7
I'll make an example for you, based off that blog article.
Creating a dummy table:
CREATE TABLE t(x NUMBER, t VARCHAR2(255));
INSERT INTO t(x, t) VALUES (1, 'Example');
COMMIT;
The script below has been saved in C:\Users\William. The filename is example.sql.
SELECT *
FROM &1
;
Now, from SQL Developer, I execute:
#C:\Users\William\example.sql t
Note that when you pass a parameter to a script like this, you are passing the text value that is stored in implicitly-named substitution variables. The substitution variable is named according to its order (e.g., &1 then &2 then &3 etc.).
This is the script output:
old:SELECT *
FROM &1
new:SELECT *
FROM t
X T
-- ----------
1 Example
You should probably take some time to consider other solutions to the problem you are solving. You may not need to execute a script via SQL developer. Perhaps you'd be better off creating a procedure that generates dynamic SQL, based off parameters you feed to the procedure. You would then use EXECUTE IMMEDIATE on the dynamic SQL statement inside the procedure.
I personally might find that technique more useful when performing actions like ETL (as opposed to queries). Then again, I'm no expert, and there're probably even better solutions out there.
In SQL*Plus, I want to execute multiple SQL queries in single line like
create table emp(name varchar2(20)); desc emp;
I tried executing this one but didn't work for me.
BEGIN OPEN :1 FOR SELECT * FROM table1; OPEN :2 FOR SELECT * FROM table2; END;
is there any way to accomplish this?
Thanks in advance!
SQL*Plus expects either:
A single SQL command, terminated by either a ";" character or a "/" on a line by itself.
A PL/SQL block
A SQL*Plus command
What you have entered is 2 queries on a single line, which SQL*Plus will send to the RDBMS - Oracle will then try and parse the string sent as a single query and fail because it is not valid SQL.
A quick workaround would be to have all your commands in a sql file and run them using #file.sql
I have an SQL statement in Oracle SQL developer that has some variables:
DEFINE custom_date = "'22-JUL-2016'" --run1
DEFINE custom_date = "'25-JUL-2016'" --run2
SELECT * FROM TABLE WHERE date=&custom_date
The real query is much more complicated and has many more variables and new tables are created from the results of the query. How can I create a script so that the query is executed twice, the first time with the custom date set as in the first line and the second time as in the second line.
In Oracle, the &variable is a "substitution variable" and is not part of SQL; it is part of the SQL*Plus scripting language (understood by SQL Developer, Toad etc.)
The better option, and what you are asking about, is BIND VARIABLES. The notation is :variable (with a colon : instead of &), for example :custom_date.
The difference is that a substitution variable is replaced by its value in the front-end application (SQL Developer in your case) before the query is ever sent to the Oracle engine proper. A bind variable is substituted at runtime. This has several benefits; discussing them is outside the scope of your question.
When you execute a query with bind variables in SQL Developer, the program will open a window where you enter the desired values for the bind variables. You will have to experiment with that a little bit till you can make it work (for example I never remember if a date must be entered with the single quotes or without). Good luck!
Define is used in TRANSACT SQL. To do this Oracle way, You can create anonymus PL/SQL block, similar to this:
DECLARE
p_param1 DATE;
p_param2 NUMBER;
CURSOR c_cur1(cp_param1 DATE,cp_param2 NUMBER)
IS
SELECT * FROM table WHERE date = cp_param1
;
BEGIN
-- Execute it first time
p_param1 := TO_DATE('2016-09-01','YYYY-MM-DD');
FOR r IN c_cur1(p_param1)
LOOP
NULL;
END LOOP;
-- Execute it second time
p_param1 := TO_DATE('2015-10-11','YYYY-MM-DD');
FOR r IN c_cur1(p_param1)
LOOP
NULL;
END LOOP;
END;
And in it, You create cursor with parameters and execute it twice with different parameter.
I do not know why You want to execute this query twice, so the script abowe does nothing with results, but it certainly should execute Your query twice, with different params.
I'm Facing below error:
An unexpected token "(" was found following " CURSOR ". Expected tokens may include: "CURSOR". SQLSTATE=42601
And I'm just trying to create a simple cursor, actually the example one found here in IBM documentation.
Cursor declaration looks something like:
DECLARE
CURSOR c1 (max_wage NUMBER) IS
SELECT * FROM emp WHERE sal < max_wage;
Not sure if this is do to the version of DB2 being used or not. Can anyone suggest maybe an alternative to creating a parameterized cursor?
You are trying to use PL/SQL syntax in DB2. This requires changes to the server environment. If you want to support the Oracle datatypes as well, the database must be created with the right settings, too. See this article for more details. The summary of that article is:
Open a DB2 Command Window (in Administrator mode)
Run db2start
Run db2set DB2_COMPATIBILITY_VECTOR=ORA
Run db2set DB2_DEFERRED_PREPARE_SEMANTICS=YES
Run db2stop
Run db2start
Execute your PL/SQL statements, e.g. in a DB2 CLP (run db2 -tv) command window.
Note that you should run
SET SQLCOMPAT PLSQL; in your DB2 CLP before trying PL/SQL. This enables using a forward slash (/) as a PL/SQL statement terminator. You should then obviously also then actually terminate your command with a forward slash :)
Here's an example taken from your link, modified to work with the default SAMPLE database in DB2:
SET SQLCOMPAT PLSQL;
DECLARE
my_record emp%ROWTYPE;
CURSOR c1 (max_wage integer) IS
SELECT * FROM employee WHERE salary < max_wage;
BEGIN
OPEN c1(40000);
LOOP
FETCH c1 INTO my_record;
EXIT WHEN c1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('Name = ' || my_record.firstnme || ', salary = '
|| my_record.salary);
END LOOP;
CLOSE c1;
END;
/
If you don't want to do all the above, then use the standard DB2 cursor syntax:
DECLARE [cursor name] CURSOR FOR [...]
...but that doesn't support parameterized cursors. To do so, I'd recommend creating a stored procedure taking the parameter. This stored procedure can then create a cursor using that parameter directly in the SQL.
So what I'm trying to do is to clear the audit logs of the PDB in an Oracle database. The name of the PDB can be different each time, so I cannot use tnsnames to sqlplus directly into the PDB to do this. I'm passing commands into bash and then passing those into a SQLPLUS command. Each of these work except for one and I can't seem to figure out how to get it to work.
My code is
AUDIT="DELETE FROM SYS.AUD$ WHERE NTIMESTAMP# < sysdate-30;"
FINDPDB="select pdb_name from dba_pdbs where pdb_name != 'PDB\$SEED';"
ALTER="alter session set container=$FINDPDB;"
sqlplus -S /nolog <<EOF1
connect / as sysdba
set echo off feedback off head off pages 0
set serveroutput on
$FINDPDB
$ALTER
$AUDIT
exit;
EOF1
The error I keep getting is
alter session set container=select pdb_name from dba_pdbs where pdb_name != 'PDB$SEED';
*
ERROR at line 1:
ORA-65015: missing or invalid container name
This tells me that it's not passing the output of the select statement to $FINDPDB, but rather the actual select statement itself.
Is there a way I can pass this value to the ALTER variable and have it alter the session and clear the sys.aud$ table?
The error I keep getting is
alter session set container=select pdb_name from dba_pdbs where pdb_name != 'PDB$SEED';
*
ERROR at line 1:
ORA-65015: missing or invalid container name
This tells me that it's not passing the output of the select statement to $FINDPDB, but rather the actual select statement itself.
I don't see why you would expect this to pass the output of the SELECT query into $FINDPDB. You're putting together a big long string which bash passes to the standard input of sqlplus and then writes to stdout the output from sqlplus. At no point is bash picking out certain lines of the sqlplus output and putting them into shell variables.
In fact, try adding echo $ALTER to your bash script before you call sqlplus. You will quite probably find that the output is
alter session set container=select pdb_name from dba_pdbs where pdb_name != 'PDB$SEED';
If so, then bash has already done the substitution you didn't want before you've even started sqlplus.
You seem to want bash and sqlplus to have some kind of back-and-forth dialog. I would give up on this approach. Instead of trying to put the PDB name into a shell variable, I would put it into a sqlplus substitution variable. I would try something like the following (not tested):
sqlplus -S /nolog <<"EOF1"
connect / as sysdba
set echo off feedback off head off pages 0
set serveroutput on
column pdb_name new_value pdb
select pdb_name from dba_pdbs where pdb_name != 'PDB\$SEED';
alter session set container = &pdb.;
delete from sys.aud$ where ntimestamp# < sysdate - 30;
exit;
EOF1
We use column pdb_name new_value pdb to set the substitution variable pdb to the next value to be selected from a column named pdb_name. We then run a select query to fetch the PDB name and hence store it in pdb. Once we've got this value in a substitution variable, we can then issue the alter session statement to change the PDB and finally the delete statement to delete data from the PDB.
I'm tempted to avoid the use of a PL/SQL block for this, as has been suggested in another answer. I would prefer that the delete statement is parsed after the PDB is changed as I would want to be sure that the data from the 'correct' PDB is being deleted. My concern with using PL/SQL for this is that the PL/SQL compiler would determine which table to delete from when the block is parsed, which would be before it runs the block, and hence before it executes the alter session statement to change the PDB. However, I don't know PDBs and CDBs in Oracle 12c well enough to say whether this is a genuine problem or unfounded nonsense.
I don't have access to a pluggable Oracle 12c database to run something like this against, so I can't tell you whether this script works. If not, hopefully it should give you an idea of where to go.
I have no Oracle instance at hand but I see two ways to do this :
Make many connections through SQL*Plus
First, to retrieve pdb_name.
Second, to set container and delete audits.
Uses a single SQL*Plus but uses two named pipes
One to send generated SQL commands
Second to read SQL*Plus output
As alternative way I should have used a "real" programming language (Ruby, Python, JavaScript) which are better dedicated to deal with data read from database.
EDIT: After some search, it mays be done in PL/SQL
DECLARE
v_pdb_name VARCHAR2(255);
BEGIN
SELECT pdb_name INTO v_pdb_name FROM dba_pdbs WHERE pdb_name != 'PDB\$SEED';
EXECUTE IMMEDIATE 'ALTER SESSION SET container='||v_pdb_name;
DELETE FROM sys.aud$ WHERE ntimestamp# < sysdate-30;
END;
/