In an Oracle PL/SQL block, why is dynamic sql allowed
begin
execute immediate 'drop table table_name';
end;
but static is not?
begin
drop table table_name;
end;
I hope the answer is more insightful than "because that's how the language works".
The answer is PL/SQL does not support dynamic polymorphism. it only supports static polymorphism because
All PL/SQL generats a "DIANA" -> Descriptive Intermediate Attributed Notation for Ada , a tree-structured intermediate language. DIANA is used internally by compilers.
At compile time, PL/SQL source code is translated into system code and generates corresponding DIANA. Now think if there were a DDL statement like create table statement which at the compile time does not exists it will be created after running the program. how would your PL/SQL engine generate a DIANA then ????
The DIANA is plays an important role in PL/SQL to check/validate that the sub program. this is required because as we know that a sub-program can use database objects such as Tables,Views,Synonyms or other stored procs. it could be possible that the the objects may have changed/removed/droped when next time you run the program. For ex : some one might have droped the table, the stored proc or function singnature may have changed.
Thats why generally PL/SQL is used to manipulate the data within database structure, but not to manipulate those structures.
but there are ways to manipulate using dynamic SQL and DBMS_SQL package but theses methodlogy are again should be used cautiously. For example if you are creating a Table you should check first if this table is already exists or not using data dictionary views.
Probably because otherwise some code would be like:
begin
create table tmp (n number);
insert into tmp values (1);
end;
And we would expect the compiler to know that at time of the insert, the table exists. The compilation of the block would me much more difficult. Here it is a very simple case, but we can easily imagine some conditional branching, and complex blabla.
But, since we need to put the DDL in an execute immediate block, the limitation maybe somehow easier to understand.
Just an idea...
Before execution/compilation, oracle checks all access permissions,validity and dependencies of all schema objects like tables,views,stored procs etc referenced inside a pl/sql block. But problem with DDL statement is that it
can create, alter or drop schema object and as a result, it can change the object dependencies.So it is possible that we are referring one object which has been dropped using DDL. To prevent such situation I guess plsql block does not allow direct DDL statement.
But we can still include DDL in PL/SQL block using Dynamic query. As in this case the actual query is not known until run-time, basically we can hide the DDL statement in the form of Dynamic SQL and include DDL inside PL/SQL block.
You can refer my blog to understand the concept with example:
Why oracle does not allow direct DDL statements inside the procedure (PLSQL BLOCK)
Related
This question is inspired by this.
As stated, I don't want a solution from PL/SQL. I want a 1 or 2 SQL statements that will check for table existence and if its not exist - create it.
Such statement(s) will be plugged into C++ application (not a script) and so I want a plain SQL solution. If such solution is not exist (please say so), I'd like to have a simple string I can plug into C++ code and use either SQLExecute() or a native Oracle client API to execute such a string.
Trying to google for a solution I am getting a results that can be used either in the shell script or a stored procedure. As I explain here and in the previous question - my situation is completely different - I work in C++ and want an appropriate solution.
There is no single SQL statement that will create a table only if it does not exist in Oracle 11g.
It is not obvious to me why you're objecting to a PL/SQL based solution. If you're using raw ODBC calls in C++, you can pass a PL/SQL block to SQLPrepare just as you would pass a plain SQL statement. Given that PL/SQL blocks work almost exactly like a pure SQL statement, it would be unusual to categorically reject a PL/SQL based solution.
If you are going to categorically reject PL/SQL, you can certainly take the logic from any of the PL/SQL based solutions and implement that in a couple of SQL statement executed from your application. For example, you can query dba_| all_| user_tables (depending on your privileges, whether you are creating tables in other schemas, etc.) to determine whether the table exists and then conditionally execute your DDL
select owner, table_name
from dba_tables
where owner = <<schema that will own the table>
and table_name = <<name of the table>>
If that returns no rows you can then execute your DDL.
Of course, you can also just execute your DDL statement and catch the ORA-00955 name is already used by an existing object error in C++.
I try to use variable in the alias.
Is it possible not to change to dynamic SQL???
For example
get_this_year --> this is the function call this year, so 2018.
CREATE OR REPLACE PROCEDURE do_activity
v_cur_year VARCHAR2(11);
BEGIN
v_cur_year := get_this_year;
select t.1 AS v_cur_year -- I wanna use in here
FROM table1
END do_activity
how can I use Variable as alias.
Thanks
What you are asking for is not possible.
The column list, names, structure, etc. has to be known, when the query is parsed. Queries within PL/SQL are parsed, when PL/SQL code is parsed/compiled, so in your case, on procedure creation. (This obviously excludes dynamic queries, which are constructed in run-time, hence can't be parsed on PL/SQL compilation.)
You would have to use the dynamic SQL, to get the column name defined by the function result, but you already stated, that you do not want dynamic SQL.
There's a second issue with your code, although it may be a result of you simplifying the code. You are not capturing the query result in your procedure, which is obligatory in Oracle PL/SQL. You can't just run a query, and expect its result to be returned by running the procedure - it's not Transact-SQL. To return a data set from PL/SQL, you would have to write a tabular function (still, this would require stable data set structure, so no dynamic column naming) or you would have to use an OUT parameter of ref-cursor type.
First of all usually I am working with MSSQL. But I have a stored procedure in MSSQL, which I need to use in Oracle now and since I am absolutely new to Oracle I have no idea at all how to do it correct.
I needed to use user defined table types in my MS SQL stored procedure because I am using "logical" tables in my stored procedure, which I also need to pass them to a dynamic sql statement within this procedure (using column names of "physical" tables as variables/parameters).
I've started to add the oracle function in a package I made before for another function. It looks like
TYPE resultRec IS RECORD
(
[result columns]
);
TYPE resultTable IS TABLE OF resultRec;
Function MyFunctionName([A LOT PARAMETERS]) RETURN resultTable PIPELINED;
I also described the layout of the tables (the user defined table types in MSSQL), which I want to use within this function in this package header.
So far so good, but now I don't really know where I have to declare my table variables or user defined table types. I also tried to put them in the package header, but if I am trying to use these tables in the package body, where I am describing my function, Oracle tells met, that the table or view does not exist.
I also tried it to describe the tables within the package body or in the block of my function, which looks like that:
FUNCTION MyFunctionName
(
[MyParameters]
)
RETURN resultTable PIPELINED is rec resultrec;
TYPE tableVariableA IS TABLE OF tableRecA;
TYPE tableVariableB IS TABLE OF tableRecB;
BEGIN
INSERT INTO tableVariableA
SELECT ColumnA, ColumnB FROM physicalTable WHERE[...];
[A LOT MORE TO DO...]
END;
But in this case Oracle also tells me, that it doesn't know the table or view.
I also tried a few more things, but at the end I wasn't able to tell Oracle what table it should use...
I would appreciate every hint, which helps me to understand how oracle works in this case. Thanks a lot!
You can't insert into a collection (e.g. PL/SQL table). You can use the bulk collect syntax to populate the collection:
SELECT ColumnA, ColumnB
BULK COLLECT INTO tableVariableA
FROM physicalTable
WHERE [...];
However, you might want to check this is an appropriate approach, since SQL Server and Oracle differ quite a bit. You can't use PL/SQL tables in plain SQL (at least prior to 12c), even inside your procedure, so you might need a schema-level type rather than a PL/SQL type, but it depends what you will do next. You might not really want a collection at all. Trying to convert T-SQL straight to PL/SQL without understanding the differences could lead you down a wrong path - make sure you understand the actual requirement and then find the best Oracle mechanism for that.
I am constructing a rather large Oracle 10g package full of functions etc.
The problem is some of these functions are pulling information from materilized view's or tables that other functions are creating.
Is there a way to successfully compile a package even though some of the functions cannot find the information they are looking for (But these functions will work once the views have been created?).
Attempts:
I have looked into PRAGMA RESTRICT_REFERENCES but have had no success so far. Am I even on the right track or is this not even possible?
You cannot refer using static SQL to objects that do not exist when the code is compiled. There is nothing you can do about that.
You would need to modify your code to use dynamic SQL to refer to any object that is created at runtime. You can probably use EXECUTE IMMEDIATE, i.e.
EXECUTE IMMEDIATE
'SELECT COUNT(*) FROM new_mv_name'
INTO l_cnt;
rather than
SELECT COUNT(*)
INTO l_cnt
FROM new_mv_name;
That being said, however, I would be extremely dubious about a PL/SQL implementation that involved creating any new tables and materialized views at runtime. That is almost always a mistake in Oracle. Why do you need to create new objects at runtime?
I just discovered Oracle's ANYDATA and ANYTYPE and thought it would be useful to generic-ify some very repetitive code I have. I tried a very simple code sample that looks like this:
declare
--simple test query
cursor myCurs is
select sysdate from dual;
type t_arr is table of myCurs%rowtype;
v_arr t_arr;
v_anydata ANYDATA;
begin
open myCurs;
fetch myCurs bulk collect into v_arr;
close myCurs;
dbms_output.put_line(v_arr.count || ' records');
v_anydata := ANYDATA.convertCollection(v_arr);
dbms_output.put_line(anydata.gettypename(v_anydata));
end;
/
Running this code gives me the error "ORA-22370: incorrect usage of method ORA-22370 incorrect ussage of method ConvertCollection" (Yes, it actually repeats ORA-22370).
My question is: Is it possible to use ANYDATA on collections of rowtypes? I ask this because all examples I've seen of ANYDATA (so far) with collections use object types, none use rowtypes, but I haven't seen anything (yet) that explicitly says that rowtypes can't be used.
My goal was to clean up some very repetitive bulk-collect/insert code that does the same thing over and over, but with different cursors for different tables.
(Oracle 10g)
ANYDATA works with SQL data_types, i.e. anything you could use to define a table column.
%ROWTYPE is a PL/SQL construct, and so is not recognised by the SQL engine. If you think about the dynamic nature of %ROWTYPE you'll be able to figure out why.
Wanting to clean up repetitive code is a noble idea. I have previously implemented an ETL processor in SQL Types which used a supertype Object to own the generic processing, and subtypes to execute table specific code (such as inserts). These subtypes are completely generatable: not as slick as a dynamic %ROWTYPE implementation would have been, but still easy enough.