Can we use a host-variable as table name in SELECT query? - sql

I'm experimenting with Pro*C code.
I have 3 tables emp, mgr, all; All 3 tables contain emp_id and emp_name. I tried the below code it is giving me error. Please let me know if it is possible?
const char table_name[3]={'emp','mgr','all'}
int counter = 0;
while(counter < 3)
{
. . .
EXEC SQL SELECT emp_name INTO :ename
From :table_name[counter++]
where emp_id=:emp_id;
}
Can we use variables for SELECT and FROM ?

This is called Dynamic SQL:
Use this info:
Dynamic SQL
While embedded SQL is fine for fixed applications, sometimes it is important for a program to dynamically create entire SQL statements.
With dynamic SQL, a statement stored in a string variable can be issued.
PREPARE turns a character string into a SQL statement, and EXECUTE executes that statement. Consider the following example.
char *s = "INSERT INTO emp VALUES(1234, 'jon', 3)";
EXEC SQL PREPARE q FROM :s;
EXEC SQL EXECUTE q;
Alternatively, PREPARE and EXECUTE may be combined into one statement:
char *s = "INSERT INTO emp VALUES(1234, 'jon', 3)";
EXEC SQL EXECUTE IMMEDIATE :s;
Source: http://infolab.stanford.edu/~ullman/fcdb/oracle/or-proc.html

No, we can't use a host-variable to supply a table-name to a static SQL statement in Pro*C.
To quote Oracle Pro*C Programmer's Guide for Oracle 11.2g:
You cannot use input host variables to supply SQL keywords or the names of database objects. [..] If you need to change database object names at runtime, use dynamic SQL. See also Chapter 13, "Oracle Dynamic SQL".
In Pro*C Oracle provides different methods to execute SQL statements. The main distinction is between static and dynamic methods. And for dynamic there are several sub-methods that allow different degrees of freedom.
A static SQL statement is not 100% static - you can use input/output host-variables in where clause expressions (as operands), to supply values in insert statements, as select targets, etc. But not to specify table names. This is 'too dynamic' for static embedded SQL.
Note that you can still use host-variables in your dynamically prepared SQL statements. This is recommended to avoid SQL injection issues and increase performance (when a statement is executed several times).
Example (uses Oracle Dynamic SQL method 3):
const char *table_name[3] = {"emp", "mgr", "all"};
unsigned table_name_size = 0;
unsigned i = 0;
for (i = 0; i<table_name_size; ++i) {
char stmt[128] = {0};
snprintf(stmt, 128,
"SELECT emp_name "
" FROM %s "
" WHERE "
" emp_id = :emp_id",
table_name[i]);
EXEC SQL PREPARE emp_stmt FROM :stmt;
// check sqlca.sqlcode ...
EXEC SQL DECLARE emp_cursor CURSOR FOR emp_stmt;
EXEC SQL OPEN emp_cursor USING :emp_id;
// check sqlca.sqlcode ...
EXEC SQL FETCH emp_cursor INTO :ename;
// check sqlca.sqlcode ...
EXEC SQL CLOSE emp_cursor;
// check sqlca.sqlcode ...
// ...
}

Related

postgres sql / dbweaver - Using a variable for tablename?

One of the SQL Queries use the same table name again and again -
Example -
select
rr.jdoc as child_node, json_agg(parent_rr.jdoc)::jsonb as parent_node , array_length(array_agg(parent_rr.jdoc)::jsonb[], 1) as count
from MYTABLE rr, MYTABLE parent_rr
where
parent_rr.jdoc #> (rr.jdoc->'somefield')::jsonb
group by rr.jdoc
UNION
select rr.jdoc, NULL as parent_id, null as pcount
from MYTABLE rr where
not (rr.jdoc ?? 'somefiled')
and ((rr.jdoc->'crazyfiled'->>'doublecrazyfiled')<>'gotyou')
You can see the same MYTABLE is used 3 times. The thing is, I have to run the same query for different tables i.e the MYTABLE substituted.
So I am trying to find - if anything like below is possible -
SET TABLENAME=CUS_DELTA --//then use the above like -
select * from $TABLENAME;
I am using postgres 13.5 with DBeaver for running the queries.
If not SQL, does DBeaver provide any such means.
If SQL can not support it, I am ok to use Dynamic SQL / PL SQL (though i have not idea about these) - if its not going to be too complicate for the above case. But I am not aware about the code, so request to share the same through an answer.
Read this article about Dynamic SQL carefully. Example from the docs:
EXEC SQL BEGIN DECLARE SECTION;
const char *stmt = "INSERT INTO test1 VALUES(?, ?);";
EXEC SQL END DECLARE SECTION;
EXEC SQL PREPARE mystmt FROM :stmt;
...
EXEC SQL EXECUTE mystmt USING 42, 'foobar';
Basically this way you can simply pass values. Now, your values are dynamic as well, but no worries. Declare your tablename as varchar and SELECT tbname INTO yourvariable FROM sometable. Then you can pass this variable to your dynamic query.
EDIT:
Minimalistic example:
Setting a variable to hard-coded value
SET TABLENAME = 'CUS_DELTA';
Setting a variable to a dynamic table name
SELECT tbname
INTO TABLENAME
FROM yourtable
And then let's use our TABLENAME variable like this:
EXEC SQL BEGIN DECLARE SECTION;
const char *stmt = "SELECT * FROM ?";
EXEC SQL END DECLARE SECTION;
EXEC SQL PREPARE mystmt FROM :stmt;
EXEC SQL EXECUTE mystmt USING TABLENAME;
The above is untested.

Pro C dynamic SQL query

I have to execute the following query using Pro C to get the output and den display the output to the user.
i tried the following code snippet:
int count=0;
char query1[100]="select count(code) from customer where customer_type='a';";
EXEC SQL ALLOCATE DESCRIPTOR 'out' ;
EXEC SQL PREPARE statement FROM :query1 ;
EXEC SQL DESCRIBE OUTPUT statement USING DESCRIPTOR 'out' ;
EXEC SQL SET DESCRIPTOR 'out' VALUE 1 TYPE = :data_type,
LENGTH = :data_len, DATA = :count ;
EXEC SQL DECLARE c CURSOR FOR statement ;
EXEC SQL OPEN c ;
EXEC SQL FETCH c INTO DESCRIPTOR 'out' ;
EXEC SQL GET DESCRIPTOR 'out' VALUE 1 :count = DATA;
EXEC SQL CLOSE c ;
printf("%-8d ",count);
but the output i get is always 0.
How shall i proceed to get the proper output??
can anyone help pls...
It is quite possible you have some errors in there that arn't getting noticed.
Use the EXEC SQL WHENEVER to get some error checking going on.
The one thing that jumps out at me is the semicolon at the end of the query1 value. If I recall correctly, Pro*c will barf on it.
I would strongly recommend against using this method of Pro*C dynamic SQL (Oracle dynamic SQL method 4) unless you can possibly avoid it.
The only case you should need to use this method is when you are using dynamically generated SQL and you don't know how many host variables will be used. E.g. You don't know how many columns will be in the SELECT clause.
A fully fledged example of using Oracle dynamic SQL method 4 can be found at http://docs.oracle.com/cd/B28359_01/appdev.111/b28427/pc_15ody.htm#i7419.

PLSQL Execute Immediate with Dynamic Using

I am dynamically building a search query with bind variables with at least 1 and at most 7 different potential criteria. I know I can do this -
EXECUTE IMMEDIATE sql USING bind_var1, bind_var2 or
EXECUTE IMMEDIATE sql USING bind_var3, bind_var5, bind_var7.
Is it possible to include the bind variables within the sql?
sql = 'SELECT * FROM table WHERE id = :bind_var1 AND name = :bind_var2 USING bind_var1, bind_var2'
and do
EXECUTE IMMEDIATE sql?
I want and need to dynamically build the USING piece instead of writing a lot of IF THEN statements.
According to your tags, I assume this will be used inside some kind of PL/SQL block. So, maybe are you looking for the open for statement.
This allows you to get a cursor on an dynamic query:
sql := 'SELECT * FROM table WHERE id = :bind_var1 AND name = :bind_var2';
open my_cursor for sql using bind_var1, bind_var2';
-- do whatever you need with your cursor
your USING bind_var1, bind_var2 pice of code should be out side os your sql string and come at the end of execute immediate statement and also for select senarios try to use dynamic sql for select with a cursor unless you want to select into a variable

Call stored procedure with table-valued parameter from java

In my application I want to execute query like SELECT * FROM tbl WHERE col IN (#list) where,#list can have variable no of values. I am using MS SQL server database. When I google this problem then I found this link
http://www.sommarskog.se/arrays-in-sql-2008.html
This link says to use table-valued parameter. So I created user-defined data type using Microsoft SQL Server Management Studio.
CREATE TYPE integer_list_tbltype AS TABLE (n int NOT NULL PRIMARY KEY)
Then I wrote stored procedure
CREATE PROCEDURE get_product_names #prodids integer_list_tbltype READONLY AS
SELECT p.ProductID, p.ProductName
FROM Northwind.dbo.Products p
WHERE p.ProductID IN (SELECT n FROM #prodids)
and then using management studio only I executed this procedure
DECLARE #mylist integer_list_tbltype
INSERT #mylist(n) VALUES(9),(12),(27),(37)
EXEC get_product_names #mylist
and it is giving me correct output. But I am wondering how to call this stored procedure from java source code. I know how to call simple stored procedure with constant number of argument
CallableStatement proc_stmt = null;
proc_stmt = con.prepareCall("{call test(?)}");
proc_stmt.setString(1,someValue);
but how to call stored procedure in table-value parameter case?
This is documented here in the JDBC driver manual. In your case, you'd have to do this:
try (SQLServerCallableStatement stmt =
(SQLServerCallableStatement) con.prepareCall("{call test(?)}")) {
SQLServerDataTable table = new SQLServerDataTable();
sourceDataTable.addColumnMetadata("n", java.sql.Types.INTEGER);
sourceDataTable.addRow(9);
sourceDataTable.addRow(12);
sourceDataTable.addRow(27);
sourceDataTable.addRow(37);
stmt.setStructured(1, "dbo.integer_list_tbltype", table);
}
I've also recently documented this in an article.
Looks like this is a planned addition to JDBC but has not been implemented yet:
http://blogs.msdn.com/b/jdbcteam/archive/2012/04/03/how-would-you-use-table-valued-parameters-tvp.aspx
Pass the parameter as a delimited string ("9,12,27,37") and then create a table-valued function in SQL Server called "fnSplit" or whatever that will return the integer values in a table (just search for "sql server split function," there are millions of them).
The typical answers (comma delimited or XML) all have problems with SQL Injection. I needed an answer that allows me to use a PreparedStatement. So I came up with this:
StringBuilder query = new StringBuilder();
query.append(
"DECLARE #mylist integer_list_tbltype;" +
"INSERT #mylist(n) VALUES(?)");
for (int i = 0; i < values.size() - 1; ++i) {
query.append(",(?) ");
}
query.append("; EXEC get_product_names #mylist ");
PreparedStatement preparedStmt = conn.prepareStatement(query.toString());
for (int i = 0; i < values.size(); ++i) {
preparedStmt.setObject(i + 1, itemLookupValues.get(i));
}
Now it's been added to JDBC Driver 6.0.
It CTP2 yet.
"This new driver now supports Table-Valued Parameters and Azure Active Directory. In addition to these new features, we have added additionally functionality for Always Encrypted. The driver also supports Internationalized Domain Names and Parameterized."
https://blogs.msdn.microsoft.com/jdbcteam/2016/04/04/get-the-new-microsoft-jdbc-driver-6-0-preview/
Download link:https://www.microsoft.com/en-us/download/details.aspx?displaylang=en&id=11774
Here is the documentation how to use it
https://msdn.microsoft.com/en-us/library/mt651781(v=sql.110).aspx
After searching for a while I found answer of this problem.Specially when you use IN clause and no of operands are variable then you can use comma-delimited values as an input in IN clause.
Here's the example how the stored procedure that will retrieve all lawyers of the given lawyer type in the provided ZIP code will look like using dynamic SQL.
CREATE PROCEDURE [dbo].[GetLawyers] ( #ZIP CHAR(5), #LawyerTypeIDs VARCHAR(100) )
AS
DECLARE #SQL VARCHAR(2000)
SET #SQL = 'SELECT * FROM [dbo].[Lawyers]
WHERE [ZIP] = ' + #ZIP + ' AND
[LawyerTypeID] IN (' + #LawyerTypeIDs + ')'
EXECUTE (#SQL)
GO
To execute the stored procedure passing the ZIP code entered by the user and the selected lawyer types in a comma separated value:
EXECUTE [dbo].[GetLawyers] '12345', '1,4'
So conclusion is you do not need to use TVP. Whichever language[Java, PHP] you use just pass parameters as comma-delimited string to stored procedure and it will work perfect.
So in JAVA you can call above stored procedure:-
proc_stmt = con.prepareCall("{call GetLawyers(?,?)}");
proc_stmt.setString(1,"12345");
proc_stmt.setString(2,"'1,4'");

check for zero rows in select query

My C program has an embedded sql query. This program runs on windows and queries the oracle database.
The query is similar to EXEC SQL SELECT ...
I need to add here a check to know if the query returns zero rows.
Basically I want to set a local valiable to know my query has returned no rows and
handle this condition accordingly.
How can I add it. I know that EXISTS statement can be used. But I am not getting
how do I use it in embedded sql.
Thanks for any help.
Use the sqlca struct
EXEC SQL include "sqlca.h"
#define NO_ROWS_FOUND (sqlca.sqlcode==1403)
EXEC SQL BEGIN DECLARE SECTION;
int val=0;
short ind=0;
EXEC SQL END DECLARE SECTION;
EXEC SQL
select value
int :val :ind
from mytable where rownum=1;
if(NO_ROWS_FOUND)
printf("No rows found\n");
Use SELECT COUNT(*) FROM ... and compare result to 0.