Oracle SQL: Declaring a variable and using the variable in other queries - sql

I'm new to Oracle Database and I'm having some trouble with declaring variables and using that in other queries. For example, I want to create a variable called caseID with which store a number based on the select statement. Then I want to use that caseID in other queries I want to create. This is what I have:
DECLARE
caseID NUMBER;
BEGIN
SELECT case_id FROM cases WHERE user_id = 'test';
END;
SELECT * FROM version where case_id = :caseID
MINUS
SELECT * FROM version where version_type = 'A'
I'm not able to use the caseID in the version query, a popup comes for me to enter what caseID is.

With SQLPlus you can try to declare a SQLPlus variable (this should also work with any GUI tool that is compatible with this kind of variable declaration such as SQL Developer, TOAD, ...):
variable caseID number;
BEGIN
SELECT case_id INTO :caseID FROM cases WHERE user_id = 'test';
END;
/
select * from version where case_id = :caseID;
Another possibility that does not use special client syntax but only PL/SQL:
DECLARE
caseID number;
v version%ROWTYPE;
BEGIN
SELECT case_id INTO caseID FROM cases WHERE user_id = 'test';
SELECT * INTO v FROM version WHERE case_id = caseID;
END;
/
But in this case you have to code everything in PL/SQL and make sure to process output of SELECT statements.

I think what you are looking for is #set command in DBeaver to define and assign it at once and use it in the session:
#set userId='test'
#set caseId=(SELECT case_id FROM cases WHERE user_id = :userId)
SELECT * FROM version where case_id = :caseId

Related

Using WITH Statement in SAP HANA table functions

Is it possible to use the WITH statement in SAP HANA table functions or is there any alternative that I can use within table functions?
CREATE OR REPLACE FUNCTION "MY_SCHEMA"."TF_TEST_1" ()
RETURNS TABLE (
mytext NVARCHAR(200)
) LANGUAGE SQLSCRIPT SQL SECURITY INVOKER AS
BEGIN RETURN
WITH X AS (SELECT 'asdf' AS mytext FROM dummy)
SELECT * FROM X;
END;
In table function you need to explicitly return a table with return statement.
As experiment showed, with is not allowed inside return (similar to CTAS: create table from select statement starting with with throws an error).
But you can assign the result of your statement to table variable and return it.
create function test_tf (x int)
returns table (
id int
)
as begin
res = with a as (
select x as id
from dummy
)
select *
from a;
return(:res);
end;
select *
from test_tf(1);
| | ID|
|--:|--:|
| 1| 1|
But in HANA SQLScript we prefer to use table variables instead ow with statements, because they allow step-by-step debugging of the code with no need to rewrite it or run externally as SQL statement with prepared input and select placed after each with. You can declare them on-the-fly, that allows you not to declare something strange upfront. So the way to rewrite it is:
alter function test_tf (x int)
returns table (
id int
)
as begin
/*with a as */
a =
select x as id
from dummy
;
res =
select *
from :a;
return(:res);
end;
Note: the only thing is to add a colon before table variable when you access it so parser can distinguish it from table name.
If you want to debug your code before publishing as a function, just replace create function statement with do and return with select * from <return variable>. To view intermediary results you still need to place select * from :table_var somewhere inbetween, because as far as I know anonymous block doesn't allow debuggind with debugger.
do (in x int => ?)
begin
/*with a as */
a =
select x as id
from dummy
;
res =
select *
from :a;
select * from :res;
--return(:res);
end;

Store result of a query inside a function

I've the following function:
DO
$do$
DECLARE
maxgid integer;
tableloop integer;
obstacle geometry;
simplifyedobstacle geometry;
BEGIN
select max(gid) from public.terrain_obstacle_temp into maxgid;
FOR tableloop IN 1 .. maxgid
LOOP
insert into public.terrain_obstacle (tse_coll,tse_height,geom) select tse_coll,tse_height,geom
from public.terrain_obstacle_temp where gid = tableloop;
END LOOP;
END
$do$;
I need to modify this function in order to execute different queries according to the type of a column of public.terrain_obstacle_temp.
This is a temporary table created by reading a shapefile, and I need to know the kind of the geom column of that table. I have a query that give the data to me:
SELECT type
FROM geometry_columns
WHERE f_table_schema = 'public'
AND f_table_name = 'terrain_obstacle'
and f_geometry_column = 'geom';
It returns me a character_varying value (in this case MULTIPOLYGON).
Ho can I modify the function in order to get the result of the query, and create an if statement that allows me to execute some code according to the result of that query?
Is the intention to copy all the records from the temp table to the actual table? If so, you may be able to skip the loop:
insert into public.terrain_obstacle (tse_coll, tse_height, geom)
select tse_coll, tse_height, geom
from public.terrain_obstacle_temp
;
Do terrain_obstacle and terrain_obstacle_temp have the same structure? If so, then the "insert into ... select ..." should work fine provided the column types are the same.
If conditional typing is required, use the CASE WHEN syntax:
v_type geometry_columns.type%TYPE;
...
SELECT type
INTO v_type
FROM geometry_columns
WHERE f_table_schema = 'public'
AND f_table_name = 'terrain_obstacle'
AND f_geometry_column = 'geom'
;
insert into public.terrain_obstacle (tse_coll, tse_height, geom)
select tse_coll
,tse_height
,CASE WHEN v_type = 'MULTIPOLYGON' THEN my_func1(geom)
WHEN v_type = 'POINT' THEN my_func2(geom)
ELSE my_default(geom)
END
from public.terrain_obstacle_temp
;

PL/SQL script throws errors

I'm using PLSQL to create a procedure to update a table based on querying another table, and I'm using a loop for the purpose. FOr testing, I'm holding off on the procedure code, and just trying to make the following script work. But it keeps throwing errors including "Encountered the symbol "end-of-file"" or "invalid SQL statement", based on minor tweaks. Where am I going wrong?
DECLARE CURSOR cur IS
SELECT * FROM Summary;
BEGIN
FOR rec in cur
LOOP
UPDATE Award
SET monthly_sales = (
SELECT COUNT(*)
FROM Sales
WHERE employee_id = rec.employee_id
AND to_char(Sales.SALES_DATE,'YY/MM')=to_char(SYSDATE,'YY/MM')
)
WHERE Summary.employee_id = rec.employee_id
END LOOP
END;
/
As well as the missing semicolons that Sentinel pointed out, your where clause is incorrect:
WHERE Summary.employee_id = rec.employee_id;
Perhaps you meant:
WHERE award.employee_id = rec.employee_id;
since you're updating the AWARD table?
However, I would question why you're using a row-by-row (aka slow-by-slow) approach for this, when you could easily do it in a single statement? Perhaps something like the following would give you the correct results (untested, since you gave no table create scripts or sample input data etc):
merge into award tgt
using (select sls.employee_id,
count(*) monthly_sales
from sales sls
where trunc(sls.sales_date,'mm') = trunc(sysdate, 'mm')
and sls.employee_id in (select employee_id from summary)
group by sls.employee_id) src
on (tgt.employee_id = src.employee_id)
when matched then
update set tgt.monthly_sales = src.monthly_sales;
You are missing semicolons at the end of lines 12 and 13:
DECLARE CURSOR cur IS
SELECT * FROM Summary;
BEGIN
FOR rec in cur
LOOP
UPDATE Award
SET monthly_sales = (
SELECT COUNT(*)
FROM Sales
WHERE employee_id = rec.employee_id
AND trunc(Sales.SALES_DATE,'month') = trunc(SYSDATE,'month')
)
WHERE Summary.employee_id = rec.employee_id;
END LOOP;
END;
/

Using variables in Oracle script

There is a complex query which generates a report. The query has several sub queries that generate 3-columns table for different products. Each sub query returns one row. All returned rows then need to be united.
But there is one requirement. If there are no result rows for a sub query we need to include the corresponding product to the final report anyway, but specify that Trades_Count is equal to zero.
I can achieve this using set of variables. The following code will work perfectly in MS SQL Server:
DECLARE #PRODUCT_NAME_1 nvarchar(100);
DECLARE #OFFER_VALID_DATE_1 datetime;
DECLARE #TRADES_COUNT_1 int;
DECLARE #PRODUCT_NAME_2 nvarchar(100);
DECLARE #OFFER_VALID_DATE_2 datetime;
DECLARE #TRADES_COUNT_2 int;
--Product 1
select #PRODUCT_NAME_1 = PRODUCT_NAME, #OFFER_VALID_DATE_1 = MAX(EXPIRY_DATE), #TRADES_COUNT_1 = COUNT(DEAL_NUMBER)
from (
--Data extractions with several joins goes here....
) as TempTable1
GROUP BY PRODUCT_NAME
--Product 2
select #PRODUCT_NAME_2 = PRODUCT_NAME, #OFFER_VALID_DATE_2 = MAX(EXPIRY_DATE), #TRADES_COUNT_2 = COUNT(DEAL_NUMBER)
from (
--Data extractions with several joins goes here....
) as TempTable2
GROUP BY PRODUCT_NAME
SELECT ISNULL(#PRODUCT_NAME_1,'Product 1') AS PRODUCT_NAME, #OFFER_VALID_DATE_1 AS MAX_MATURITY, ISNULL(#TRADES_COUNT_1,0)
UNION
(
SELECT ISNULL(#PRODUCT_NAME_2,'Product 2') AS PRODUCT_NAME, #OFFER_VALID_DATE_2 AS MAX_MATURITY, ISNULL(#TRADES_COUNT_2,0)
)
I think that I haven’t used anything T-SQL specific, but pure ANSI-SQL (I’m not 100% sure though).
So this is not working in Oracle.
First of all it requires having only one DECLARE keyword. Then it forces me using Begin … End execution scope. Then it doesn’t allow me to assign variables like I do (see example above) – I need to use “Select INTO” statement instead. After all calculations are done it doesn’t allow me selecting values from local variables. Heck.
Does anyone know how to make it work in Oracle?
Thanks!
PL/SQL is different than t-sql, I did a change with some comments for you, but definitely look at the links from Andy. This was ran in oracle's free SQL Developer (which also has a "Translation Scratch Handler (tools>Migration>Translation Scratch Handler) that may be of use.
--this creates a refcursor to allow us to simply print the results
var refc refcursor
/
declare --here we declare our variables
product_name_1 varchar2(15) ;
offer_valid_date_1 date ;
trade_count_1 number ;
product_name_2 varchar2(15) ;
offer_valid_date_2 date ;
trade_count_2 number ;
begin
begin --this creates a block so we may handle any exceptions just to this
select PRODUCT_NAME, MAX(EXPIRY_DATE), COUNT(DEAL_NUMBER)
into product_name_1 , offer_valid_date_1 , trade_count_1
--in oracle you select INTO, not var=COL
from (
--Data extractions with several joins goes here....
select
123 PRODUCT_NAME,
sysdate EXPIRY_DATE,
5 DEAL_NUMBER
from dual --this is a 'fake' table to generate some data for testing
) TempTable1 --drop the "as"
GROUP BY PRODUCT_NAME ;
exception --if not data is found, then this error is thrown
--if multiple values are thrown an error will also be thrown (not caught here)
when no_data_found then
product_name_1 := null ; --note, to do a var = , we use "var := value;"
offer_valid_date_1 := null;
trade_count_1 := null;
end ;
begin
select PRODUCT_NAME, MAX(EXPIRY_DATE), COUNT(DEAL_NUMBER)
into product_name_2 , offer_valid_date_2 , trade_count_2
--in oracle you select INTO, not var=COL
from (
--Data extractions with several joins goes here....
select 555 PRODUCT_NAME, sysdate EXPIRY_DATE, 6 DEAL_NUMBER
from dual
) TempTable2 -- drop the "as"
GROUP BY PRODUCT_NAME ;
exception --if not data is found, then this error is thrown
--if multiple values are thrown an error will also be thrown (not caught here)
when no_data_found then
product_name_2 := null ;
offer_valid_date_2 := null;
trade_count_2 := null;
end ;
open :refc for --you cannot just have a select statement, you must "open" a cursor for it
--oracle IsNull is NVL (or NVL2 or you can do a case or decode...)
SELECT nvl(PRODUCT_NAME_1,'Product 1') AS PRODUCT_NAME
, OFFER_VALID_DATE_1 AS MAX_MATURITY
, nvl(TRADE_COUNT_1,0)
FROM DUAL --you also must have a table, DUAL is an oracle table for this tasks
UNION
SELECT nvl(PRODUCT_NAME_2,'Product 2') AS PRODUCT_NAME
, OFFER_VALID_DATE_2 AS MAX_MATURITY
, nvl(TRADE_COUNT_2,0)
FROM DUAL;
end ;
/
--now print the results, if you did this in a proc you would simple have this as an output
print refc;
-------------
PRODUCT_NAME MAX_MATURITY NVL(:B1,0)
-------------------------------------- ----------------------
123 18.FEB.2011 08:43 1
555 18.FEB.2011 08:43 1
Oracle concepts used here:
Dual Table , NVL, Variables, pl/sql Exception
and look at this http://www.dba-oracle.com/t_convent_sql_server_tsql_oracle_plsql.htm
PL/SQL formats procedural blocks differently than T-SQL.
You'll want to use the following structure:
DECLARE
astring varchar2(1000);
anumber number;
BEGIN
my SQL code here...
END;
You don't use the # either in PL/SQL. Just use variables names directly.

accessing a bind variable in sqlplus

In the following example,
variable recordId number;
BEGIN
SELECT MAX(recordvalue)
INTO recordId
FROM sometable;
END;
PRINT recordid;
SELECT *
FROM someothertable
WHERE recordkey = &recordId;
The select statement on the last line cannot access the value of recordId.
I know i can access recordId inside the pl/sql block using :recordId but is there a way to access recordId in a sql statement that is not in a pl/sql block? (like in the last line).
You can use bind variables in SQL*Plus too, still as :recordId. The & version will prompt for a value, and has no direct relationship to the variable version.
variable recordId number;
BEGIN
SELECT MAX(recordvalue)
INTO :recordId
FROM sometable;
END;
/
PRINT recordid;
SELECT *
FROM someothertable
WHERE recordkey = :recordId;
The slightly more common way to assign values to bind variables is with exec :recordId := value;, but exec is really just shorthand for an anonymous block anyway.
Not sure why you'd want to mix and match like this though. If the intention is to use the result of one query in a later one, you could try new_value instead:
column x_val new_value y_val; -- could also noprint if you do not need to see the intermediate value
SELECT MAX(recordvalue) x_val
FROM sometable;
SELECT *
FROM someothertable
WHERE recordkey = &y_val;