Is there a possibility to write dynamic SQL, where I can use parameter names inside the code?
The following syntax I know, makes long code completely unclear and hard to figure out the sense without digging and mapping the parameters.
format ($$ select * from %1$s $$ , tableName_1)
I would like to somehow have it like
select * from tableName_1
Sample1:
using format function. By using format you can write all parameters under same name. And values will be set to params orderly.
execute format('select * from %s.%s' , p_schema, p_table);
--Result:
select * from public.mytable
Sample2:
execute 'select * from ?.?' using p_schema, p_table;
Sample3:
execute 'select * from $1.$2' using p_schema, p_table;
Related
I am using SQL Developer. When I want to bind value. Normally I use following syntax:
SELECT * FROM table WHERE column = :bindvalue
but, I don't know how to do that in string. The following query does not work.
SELECT * FROM table WHERE column like '%:bindvalue%'
Why do I need it? Because my program runs a query in python and assigns something to bind variable:
curr.execute('''SELECT * FROM table WHERE column''' = :bindvalue, bindvalue=somevalue)
Concatenate the prefix/suffix with the bind variable:
SELECT * FROM table WHERE column like '%' || :bindvalue || '%'
I get a list of elments from R and I have to obtain the records from database that belong to a the list of elements.
INPUT:
'12345','23456', '34567', '45678'
PROCEDURE:
CREATE PROCEDURE "SCHEMA"."GET_RECORDS" (IN LIST (Type), OUT RECORDS tt_records)
LANGUAGE SQLSCRIPT
SQL SECURITY INVOKER
READS SQL DATA AS
BEGIN
RECORDS = select * from TABLE where ids in :LIST
END;
How can I provide such a list to the proceudre?
Handing over lists of parameters to SQLScript is a bit tricky as there is no straight-forward native construct for that.
One way to do it is to use the APPLY_FILTER function and to "smuggle" the list as a string parameter.
In my example I read from a table CUSERS and I create a filter condition for APPLY_FILTER that filters column USER_ID via an IN ( ) clause.
Removing the single quotes (' ') from the list is to avoid implicit type conversion when executing the query. Leaving the single quotes in place would make the IN () clause make look like this:
IN ( '<1st value>', '<2nd value>', '<3rd value>', ...)
instead of
IN (<1st value>, <2nd value>, <3rd value>, ...).
CREATE PROCEDURE "GET_RECORDS" (IN id_list VARCHAR(4000)
, OUT RECORDS tt_cusers)
LANGUAGE SQLSCRIPT
SQL SECURITY INVOKER
READS SQL DATA AS
BEGIN
declare _filter VARCHAR(4000);
_users = select * from cusers;
-- APPLY_FILTER expects a proper WHERE condition, so adding the column to filter
-- and the IN () expression is necessary.
--
-- the the id_list comes in with single quotes, let's remove those
_filter = 'USER_ID in (' || replace (:id_list, '''', '') ||')';
RECORDS = APPLY_FILTER(:_users, :_filter);
end;
call get_records (?, ?)
-- this 'list' is to be used as a single parameter value
-- '131072', '161223', '131074'
A slightly more comfortable approach for getting the data out from SAP HANA into R can be using a table typed user-defined function (UDF) instead. The main difference here is that the calling statement is a simple SELECT and the result is simply the resultset of this SELECT.
CREATE function "FGET_RECORDS" (IN id_list VARCHAR(4000))
returns tt_cusers
LANGUAGE SQLSCRIPT
SQL SECURITY INVOKER
READS SQL DATA AS
BEGIN
declare _filter VARCHAR(4000);
_users = select * from cusers;
-- APPLY_FILTER expects a proper WHERE condition, so adding the column to filter
-- and the IN () expression is necessary.
--
-- the the id_list comes in with single quotes, let's remove those
_filter = 'USER_ID in (' || replace (:id_list, '''', '') ||')';
_result = APPLY_FILTER(:_users, :_filter);
RETURN :_result;
end;
select * from fget_records (? );
In R (or in any other client) make sure to use bind variables when using this construct. Otherwise handling the different string quote-mechanisms can become cumbersome.
See the documentation on APPLY_FILTER here.
Use a User Defined Data Type.
First Create A User Defined Data Type
Database Node > Programmability > Types > User-Defined Table Types
Script :
CREATE TYPE dbo.MyTableType AS TABLE
(
ID INT
)
Create a Parameter in your procedure with the above type
CREATE PROCEDURE usp_InsertMessages
(
#MyParameter MyTableType READONLY
)
AS
BEGIN
INSERT INTO MyTable
(
id
)
SELECT
id
FROM #MyParameter
END
I want to execute a dynamic SQL statement, which searches for names whose last name is always a constant and first name is a variable.
Here is a query I have written for selecting a row with name='Test lastname'.
EXECUTE 'SELECT name FROM users
WHERE name=$1 lastname'
USING ('Test');
This generates a syntax error. Is it possible to do this?
I think you need something like this:
EXECUTE 'SELECT user_id FROM users
WHERE name=$1'
USING Test||' lastname' ;
Here Test is variable and 'lastname' is hard coded value
Also another way is as #JorgeCampos mentioned:
...WHERE name=$1 || '' lastname''' USING 'Test';
This seems like such a basic question, but I didn't find anything similar on here...
I am currently generating exec statements by concatenating various strings and columns like this:
SELECT ('exec my_package.my_procedure(input_var1 => ''' || columnA || ''', input_var2 => [... ... ...] || ''');') As sql_stmt FROM myTable;
This worked just fine until I decided to add another input parameter that should get its value from a subquery like this (I left out the irrelevant parts, it's the same query as above just with an added parameter):
input_var_my_id => (select NVL(MAX(my_id)+1,1) from someTable where [various conditions] )
After I added that parameter, I am getting a PLS-00103 error, saying that the symbol SELECT was encountered when ( - + case mod new not null [....]
were expected.
The generated exec statement looks like this:
my_package.my_procedure(input_var1 => 'whatever', input_var_my_id => (select NVL(MAX(my_id)+1,1) from someTable where [various conditions] ));
The subquery itself is valid, if I copy it from the generated statement and execute it, I am getting a single dataset as a result - just as expected.
Is it not possible to pass the result of a subquery as a parameter to a PL/SQL package? Are there any workarounds? Thanks for your help
No, it isn't possible to have a query inside the actual parameter list. You can call some functions directly but only those implemented in PL/SQL. You can't switch to an SQL context. You can see that with a simpler example:
exec dbms_output.put_line(select user from dual);
ORA-06550: line 1, column 28:
PLS-00103: Encountered the symbol "SELECT" when expecting one of the following:
...
You will have to put the query result into a local variable, which is going to be ugly in your generated code; it needs to end up as:
declare
l_input_var_my_id someTable.my_id%type;
begin
select NVL(MAX(my_id)+1,1) into l_input_var_my_id from someTable where [various conditions];
my_package.my_procedure(input_var_my_id => l_input_var_my_id, ...);
end;
You can put all of that in an exec:
exec declare l_input_var_my_id someTable.my_id%type; begin select NVL(MAX(my_id)+1,1) into l_input_var_my_id from someTable where [various conditions]; my_package.my_procedure(input_var_my_id => l_input_var_my_id, ...); end;
... and then generate that from your original query.
But as exec is just a shorthand for an anonymous block and has to be on one line by default, it might be clearer to generate the block - so have that without the exec and with a / on a line on its own. Depends how you plan to call the result, and whether it needs to be easy to read.
I need to insert data into a sql table using a csv file with apostrophe(') and ('') in few rows.
I was able to handle it using the below method.
Get open_quote and close_quote and put the username and email_id between these two variable.
SELECT CHR(113)||CHR(39)||CHR(91) INTO OPEN_QUOTE FROM dual;
SELECT CHR(93)||CHR(39) INTO CLOSE_QUOTE FROM dual;
enter image description here
It looks ugly.I could have used replace but i opted for this method.
Could you please let me know of any other method so that my code looks good?
Attached is the screenshot of the dynamic sql.
You can have a single quote in a string by doubling it. For instance:
select 'It''s Bob''s book'
from dual;
As of Oracle 10g in PL/SQL you can have:
V_SQL_STATEMENT := q'[It's Bob's book]';
See Oracle SQL Reference for details on text literals.
Use the alternative-quoting mechanism and several REPLACEs instead of concatenation. It's a little extra work but it makes it clearer what the final SQL statement will look like.
v_sql_statement := replace(replace(replace(replace(
q'[
insert into login
(USER_ID,CLIENT_ID,EMAIL,PSWD_HASH,NETWORK_ID,FAILED_LOGIN_ATTEMPTS,NAME)
VALUES
(
LOGIN_SEQ.nextval,
#P_CLIENT_ID#,
'#PARSE_EMAIL#',
#V_PSWD_HASH#,
NULL,
0,
'#USER_NAME#'
)
]'
, '#P_CLIENT_ID#', p_client_id)
, '#PARSE_EMAIL#', parse_email(lower(c1.email)))
, '#V_PSWD_HASH#', v_pswd_hash)
, '#USER_NAME#', nvl(c1.name, generate_user_name(parse_email(c1.email))))
;