Can I execute a select query stored as a Varchar in another table's column ??.
Suppose there is a table TB_SQL_QUERIES, with column name as SQL_QUERY_TEXT and ID.
Is there a way to execute a query from this table where ID=(to be input by the user)
Yes, you would do something like:
DECLARE
l_sql VARCHAR2(4000);
BEGIN
SELECT sql_query_text INTO l_sql
FROM tb_sql_queries
WHERE id = 1;
EXECUTE IMMEDIATE l_sql;
END;
See EXECUTE IMMEDIATE.
Inprovising on the other answer about EXECUTE IMMEDIATE :
Firstly, in 12c, you can have 32767 for SQL rather than the limitation of 4000 in prior versions. Thus, you can eliminate the effort to have multiple variables to keep concatenating the dynamic string whenever it exceeds 4000 characters.
Secondly, since you mentioned that the input would be by user, you need to have Bind variable to get the value at run time.
Related
Problem Statement
I have a dynamic SQL which i need to store in a table ,but before
storing the sql i need to validate the sql with the list of columns
stored in another table.
Without executing the query , is it possible to find name of columns in the select ?
Approach1
Only option i can think of is ,try to use explain plan of the query and read the meta data in the data dictionaries table .But unfortunately i am not able to find any table with such data.Please let me know if you know such views?
Approach2
Use DBMS_SQL.DESCRIBE_COLUMNS package to find the column name ,but i believe this will execute the whole query.
You don't need to execute the query to get the column names, you just need to parse it; e.g. as a simple example:
set serveroutput on
declare
l_statement varchar2(4000) := 'select * from employees';
l_c pls_integer;
l_col_cnt pls_integer;
l_desc_t dbms_sql.desc_tab;
begin
l_c := dbms_sql.open_cursor;
dbms_sql.parse(c=>l_c, statement=>l_statement, language_flag=>dbms_sql.native);
dbms_sql.describe_columns(c=>l_c, col_cnt=>l_col_cnt, desc_t=>l_desc_t);
for i in 1..l_col_cnt loop
dbms_output.put_line(l_desc_t(i).col_name);
end loop;
dbms_sql.close_cursor(l_c);
exception
when others then
if (dbms_sql.is_open(l_c)) then
dbms_sql.close_cursor(l_c);
end if;
raise;
end;
/
which outputs:
EMPLOYEE_ID
FIRST_NAME
LAST_NAME
EMAIL
PHONE_NUMBER
HIRE_DATE
JOB_ID
SALARY
COMMISSION_PCT
MANAGER_ID
DEPARTMENT_ID
PL/SQL procedure successfully completed.
You can do whatever validation you need on the column names inside the loop.
Bear in mind that you'll only see (and validate) the column names or aliases for column expressions, which won't necessarily reflect the data that is actually being retrieved. Someone could craft a query that pulls any data from anywhere it has permission to access, but then gives the columns/expression aliases that are considered valid.
If you're trying to restrict access to specific data then look into other mechanisms like views, virtual private database, etc.
DBMS_SQL.PARSE will not execute a SELECT statement but it will execute a DDL statement. If the string 'select * from employees' is replaced by 'drop table employees' the code will fail but the table will still get dropped.
If you're only worried about the performance of retrieving the metadata then Alex Poole's answer will work fine.
If you're worried about running the wrong statement types then you'll want to make some adjustments to Alex Poole's answer.
It is surprisingly difficult to tell if a statement is a SELECT instead of something else. A simple condition checking that the string begins with select will work 99% of the time but getting from 99% to 100% is a huge amount of work. Simple regular expressions cannot keep up with all the different keywords, comments, alternative quoting format, spaces, etc.
/*comment in front -- */ select * from dual
select * from dual
with asdf as (select * from dual) select * from asdf;
((((((select * from dual))))));
If you need 100% accuracy I recommend you use my open source PLSQL_LEXER. Once installed you can reliably test the command types like this:
select
statement_classifier.get_command_name(' /*comment*/ ((select * from dual))') test1,
statement_classifier.get_command_name('alter table asdf move compress') test2
from dual;
TEST1 TEST2
----- -----
SELECT ALTER TABLE
I am trying to extract data from tables. The names of multiple tables can be given as comma separated values (like a,b,c) and based on that I need to extract the data into a file. Here is the code, which is run through SQL*Plus:
declare
p_schema_name varchar2(1000) := '&1';
p_table_names varchar2(4000) := '&2';
p_statement varchar2(4000);
begin
p_statement := 'select * from :x'||'.'||':y';
for foo in (
select
regexp_substr (replace(p_table_names, ' ', ''), '[^,]+', 1, level) as txt
from dual
connect by
regexp_substr (replace(p_table_names, ' ', ''), '[^,]+', 1, level) is not null)
loop
execute immediate p_statement using p_schema_name, foo.txt;
end loop;
end;
When I execute this script then I get the following error:
ORA-00903: invalid table name
ORA-06512: at line 14
Where I am doing wrong?
You can only bind variables, not object names. The bind substitution is done when your query is executed, not when it is parsed. The object names have to be known at parse time.
You have to concatenate the schema and table name into the query:
...
begin
p_statement := 'select * from ';
...
execute immediate p_statement || p_schema_name ||'.'|| foo.txt;
end loop;
end;
Of course that now has the potential for SQL injection, so you need to sanitise the inputs. You can check if they are valid names, or check the extracted tables exist in the specified schema before trying to execute the query. You might find the DBMS_ASSERT package useful.
Although your query won't actually be executed at the moment; from the documentation:
If dynamic_sql_statement is a SELECT statement, and you omit both into_clause and bulk_collect_into_clause, then execute_immediate_statement never executes.
You need to select your data into something, which is going to be tricky if the tables that can be passed in have different structures. If you plan to write the queried data into a file and don't know the structure then you need to use the DBMS_SQL package rather then execute immediate.
Table names and Column names cannot be passed as Bind variables to an Execute immediate statement!
Instead use them as variables and concatenate them.
But then the other error would be that you are missing an INTO clause for the Execute immediate statement processing a SELECT statement as said by the other contributor.
Thanks,
B
As per your question how you are going to handle the output of SELECT * query from PLSQL. I guess you either need to have a ref cursor to anyway use this. Also as per your code if you loop through multiple tables the output will be always overwritten by the next one so you will get the last query output only.
So for this spool the output in a file and iterate to next table.
Hope this info helps.
It is possible to build a statement using variables for the table name without creating a VARCHAR and using EXECUTE IMMEDIATE?
ex:
#table_name := test_table
UPDATE #table_name
SET col1 = val1
WHERE condition...;
this is my actual workaround:
DECLARE
sql_stmt VARCHAR2(1000);
table_name VARCHAR2(30) := 'test_table';
BEGIN
sql_stmt := 'UPDATE ' || table_name || ' SET col1 = val1...';
EXECUTE IMMEDIATE sql_stmt;
END;
/
"[Is it] possible to build a statement using variables for the table
name without creating a VARCHAR and using EXECUTE IMMEDIATE?"
No.
SQL is a strongly typed language. The compiler requires the actual table name in order to validate the various components of the SQL statement ( projection, filters, etc).
Dynamic SQL (EXECUTE IMMEDIATE and DBMS_SQL) is necessary to short-circuit the compile time validation. Essentially it's a mechanism relieving the compiler of the burden of verifying the SQL and taking it on ourselves (because humans are so much better at such jobs than machines. Not).
Alternatively, it's a mechanism for generating runtime errors instead of compilation errors.
Either way, it's pretty odd to know the name of the COLUMN in the filter but not the name of the TABLE being updated.
If i type my source tables name, data are copy into our target table:
'Create or Replace procedure p
(source_tab varchar2,target_tab varchar2)
As
query varchar2(200);
Type I_tab is table of varchar2(100000) index by binary_integer;
Rec_i I_tab;
Begin
query=’select * from’||source_tab;
Execute immediate query bulk collect into rec_i;
Forall i in rec_i.first..rec_i.last
Execute immediate’insert into’||target_tab||’values’||rec_i(i);
Commit;
End;
/
In the absence of anything useful like the actual compilation or runtime errors are we can do is guess.
As it happens your code has several obvious bloomers.
You appear to be using MS Word type smart quotes ’ whereas Oracle expects regular ASCII dumb quotes '. So that would be a compilation error.
The maximum size of a VARCHAR2 is 32767 so your nested table statement will hurl a compilation error.
You need to put spaces around the key words in the dynamic SQL. Otherwise you'll get a runtime error. So ’insert into’||target_tab||’values’ needs to 'insert into '||target_tab||' values ' . Make sure you check all the strings.
In PL/SQL the assignment operator is :=; = is the test for equality.
Less obvious, but we can't use EXECUTE IMMEDIATE in a FORALL. It's not a programmatic loop, and it needs to be a DML (SQL) statement: EXECUTE IMEMDIATE is PL/SQL. However you can do the same thing with
execute immediate
'insert into '|| target_tab ||
' select * from '|| source_tab
This question is similar to a couple others I have found on StackOverflow, but the differences are signficant enough to me to warrant a new question, so here it is:
I want to obtain a result set from dynamic SQL in Oracle and then display it as a result set in a SqlDeveloper-like tool, just as if I had executed the dynamic SQL statement directly. This is straightforward in SQL Server, so to be concrete, here is an example from SQL Server that returns a result set in SQL Server Management Studio or Query Explorer:
EXEC sp_executesql N'select * from countries'
Or more properly:
DECLARE #stmt nvarchar(100)
SET #stmt = N'select * from countries'
EXEC sp_executesql #stmt
The question "How to return a resultset / cursor from a Oracle PL/SQL anonymous block that executes Dynamic SQL?" addresses the first half of the problem--executing dynamic SQL into a cursor. The question "How to make Oracle procedure return result sets" provides a similar answer. Web search has revealed many variations of the same theme, all addressing just the first half of my question. I found this post explaining how to do it in SqlDeveloper, but that uses a bit of functionality of SqlDeveloper. I am actually using a custom query tool so I need the solution to be self-contained in the SQL code. This custom query tool similarly does not have the capability to show output of print (dbms_output.put_line) statements; it only displays result sets. Here is yet one more possible avenue using 'execute immediate...bulk collect', but this example again renders the results with a loop of dbms_output.put_line statements. This link attempts to address the topic but the question never quite got answered there either.
Assuming this is possible, I will add one more condition: I would like to do this without having to define a function or procedure (due to limited DB permissions). That is, I would like to execute a self-contained PL/SQL block containing dynamic SQL and return a result set in SqlDeveloper or a similar tool.
So to summarize:
I want to execute an arbitrary SQL statement (hence dynamic SQL).
The platform is Oracle.
The solution must be a PL/SQL block with no procedures or functions.
The output must be generated as a canonical result set; no print statements.
The output must render as a result set in SqlDeveloper without using any SqlDeveloper special functionality.
Any suggestions?
The closest thing I could think of is to create a dynamic view for which permission is required. This will certainly involve using a PL/SQL block and a SQL query and no procedure/function. But, any dynamic query can be converted and viewed from the Result Grid as it's going to be run as a select query.
DEFINE view_name = 'my_results_view';
SET FEEDBACK OFF
SET ECHO OFF
DECLARE
l_view_name VARCHAR2(40) := '&view_name';
l_query VARCHAR2(4000) := 'SELECT 1+level as id,
''TEXT''||level as text FROM DUAL ';
l_where_clause VARCHAR2(4000):=
' WHERE TRUNC(1.0) = 1 CONNECT BY LEVEL < 10';
BEGIN
EXECUTE IMMEDIATE 'CREATE OR REPLACE VIEW '
|| l_view_name
|| ' AS '
|| l_query
|| l_where_clause;
END;
/
select * from &view_name;
You seem to be asking for a chunk of PL/SQL code that will take an arbitrary query returning result set of undetermined structure and 'forward/restructure' that result set in some way such that is can easily be rendered by some "custom GUI tool".
If so, look into the DBMS_SQL for dynamic SQL. It has a DESCRIBE_COLUMNS procedure which returns the columns from a dynamic SELECT statement. The steps you would need are,
Parse the statement
Describe the result set (column names and data types)
Fetch each row, and for each column, call the datatype dependent function to return that value into a local variable
Place those local variables into a defined structure to return to the calling environment (eg consistent column names [such as col_1, col_2] probably all of VARCHAR2)
As an alternative, you could try building the query into an XMLFOREST statement, and parse the results out of the XML.
Added :
Unlike SQL Server, an Oracle PL/SQL call will not 'naturally' return a single result set. It can open up one or more ref cursors and pass them back to the client. It then becomes the client's responsibility to fetch records and columns from those ref cursors. If your client doesn't/can't deal with that, then you cannot use a PL/SQL call.
A stored function can return a pre-defined collection type, which can allow you to do something like "select * from table(func_name('select * from countries'))". However the function cannot do DML (update/delete/insert/merge) because it blows away any concept of consistency for that query. Plus the structure being returned is fixed so that
select * from table(func_name('select * from countries'))
must return the same set of columns (column names and data types) as
select * from table(func_name('select * from persons'))
It is possible, using DBMS_SQL or XMLFOREST, for such a function to take a dynamic query and restructure it into a pre-defined set of columns (col_1, col_2, etc) so that it can be returned in a consistent manner. But I can't see what the point of it would be.
Try try these.
DECLARE
TYPE EmpCurTyp IS REF CURSOR;
v_emp_cursor EmpCurTyp;
emp_record employees%ROWTYPE;
v_stmt_str VARCHAR2(200);
v_e_job employees.job%TYPE;
BEGIN
-- Dynamic SQL statement with placeholder:
v_stmt_str := 'SELECT * FROM employees WHERE job_id = :j';
-- Open cursor & specify bind argument in USING clause:
OPEN v_emp_cursor FOR v_stmt_str USING 'MANAGER';
-- Fetch rows from result set one at a time:
LOOP
FETCH v_emp_cursor INTO emp_record;
EXIT WHEN v_emp_cursor%NOTFOUND;
END LOOP;
-- Close cursor:
CLOSE v_emp_cursor;
END;
declare
v_rc sys_refcursor;
begin
v_rc := get_dept_emps(10); -- This returns an open cursor
dbms_output.put_line('Rows: '||v_rc%ROWCOUNT);
close v_rc;
end;
Find more examples here. http://forums.oracle.com/forums/thread.jspa?threadID=886365&tstart=0
In TOAD when executing the script below you will be prompted for the type of v_result. From the pick list of types select cursor, the results are then displayed in Toad's data grid (the excel spreadsheet like result). That said, when working with cursors as results you should always write two programs (the client and the server). In this case 'TOAD' will be the client.
DECLARE
v_result sys_refcursor;
v_dynamic_sql VARCHAR2 (4000);
BEGIN
v_dynamic_sql := 'SELECT * FROM user_objects where ' || ' 1 = 1';
OPEN :v_result FOR (v_dynamic_sql);
END;
There may be a similar mechanism in Oracle's SQL Developer to prompt for the binding as well.