Oracle 10g Create a View passing in a parameter - oracle

I am have been searching but I have not been able to find a satisfactory solution where I can pass a parameter to a view.
What I am trying to do is to call a view saved in Oracle 10g passing in a date via NHibernate, which sounds simple enough, but I am reading that passing parameters to view is not so. So, I am unless I am being misled, can someone please advise me whether this is possible and how; or should I do this as a Stored Procedure?
CREATE OR REPLACE aView AS VIEW
SELECT col1,
col2,
col3,
FROM someTable
WHERE col4 <= TO_DATE('somePassInDate', 'dd/mm/yyyy');
The above is the sort of query I want to run. I don't hibernate to create this query.
Thanks

There is a problem with pipelined-table-functions as they are
inefficient and could take a while to return any rows. This query is
part of a housekeeping query where every night it will return
thousands of rows. This is why I have taken it away from nhibernate as
it is inefficient
This is wrong. Pipelined functions are more efficient than table functions. The reason being:
Pipelining allows rows to be passed out of table functions as they are
produced, rather than waiting for whole collections to be produced
before the results are returned. The outcome is a reduction in the
time taken for the first rows to be produced and a reduction in the
total amount of memory consumed by the table function.
You can read an elaborative example here Pipelined Functions
However not sure what is your need, but you can create a dynamic block or Procedure to get your requirement. On execution of the below block it would prompt for a input date.
DECLARE
v_sql VARCHAR2 (1000);
v_date DATE := :v_date;
BEGIN
v_sql := q'[CREATE OR REPLACE aView AS VIEW
SELECT col1,
col2,
col3,
FROM someTable
WHERE col4 <= TO_DATE(':somePassInDate', 'dd/mm/yyyy')]';
EXECUTE IMMEDIATE v_sql USING v_date;
END;

The only way how to "customize" output from a via is to use system context. It is something like a global variable in a SQL engine.
https://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_5002.htm
CREATE OR REPLACE aView AS VIEW
SELECT col1,
col2,
col3,
FROM someTable
WHERE col4 <= TO_DATE(SYS_CONTEXT ('MY_CONTEXT', 'MY_CTX_VARIABLE') , 'dd/mm/yyyy');
Where you will find HOWTO set value for a context variable:
https://dba.stackexchange.com/questions/114252/oracle-how-do-i-set-a-context-variable
IMHO it is not worth of trying, you will need privilege granted "CREATE ANY SYSTEM CONTEXT". If you use hibernate, you should not combine it with advanced Oracle stuff.

Related

How to find the column used in the dynamic query without executing whole query

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

Delete duplicated records using procedure in Oracle/PLSQL

As the title, I wanna create a procedure in Oracle/PLSQL to delete rows which share same values in some columns. I know how to implement it using Query, but how to do it using procedure? Do I have to use any loop? I am very new to PLSQL
Please help, thank you a lot!
If you want a simple procedure to delete from a particular table you can use the below piece of code:
CREATE OR REPLACE PROCEDURE DELETE_DUPLICATE AS
BEGIN
FOR I IN (SELECT TAB.A, TAB.B, MIN(ROWID) RID
FROM DUPLICATE_TABLE TAB
GROUP BY TAB.A, TAB.B
HAVING COUNT(*) > 1) LOOP
DELETE FROM DUPLICATE_TABLE TAB
WHERE I.RID <> TAB.ROWID
AND TAB.A = I.A
AND TAB.B = I.B;
COMMIT;
END LOOP;
END;
Here DUPLICATE_TABLE is the table having duplicate values. We are deleting rows having same values in columns A and B.
Hey. As per your question, although it is not advicable to create
procedure for this simpler task which can be easily done via Pure SQL.
But if its really imp to make it as a stored procedure then i would
suggest to use PURE SQL logic than using any kind of loop as there
will be Context Switching which will have a toll on the database.
Below is a snippet which i think will be useful also incorporated
Analytical function to suffice your issue. Let me know if it helps.
CREATE OR REPLACE PROCEDURE Dup_DELETE
AS
BEGIN
DELETE
FROM EMP
WHERE EMP.ROWID IN
-- Assuming that i am trying to segregate the duplicate values on Empno and ename
(SELECT A.ROWID
FROM
(SELECT ROW_NUMBER() OVER(PARTITION BY EMPNO,ENAME ORDER BY JOB DESC) RNK,
empno,
ename,
rowid
FROM EMP
)A
WHERE A.RNK <> 1
);
END;
Just put your SQL statement in a procedure. There's no rule that says you have to change the approach because it's PL/SQL. For example,
create or replace procedure dedupe_sometable
as
begin
delete sometable
where rowid in
( select lag(rowid) over (partition by id order by null)
from sometable );
end dedupe_sometable;
Add logging etc as needed.
(Ideally this would be within a package and not a standalone procedure.)
If you know how to do it in SQL, better to do it in sql. PL/SQL should be used only when you cannot write specific task in SQL statement or if there is performance issues in the query and can improve by writing the logic in PL/SQL (second scenario is very rare).
If you want to write PL/SQL procedure to parameterize so that any table can be passed to delete the duplicates from it, then it makes sense. You need to dynamically generate delete statement in the procedure and execute using execute immediate.
If your intention is to learn PL/SQL, then it is programming language and you need to spend some time as if you are learning new programming language.
It is not recommended to use plsql for something that can be done using plain sql.
Whenever you have a combination of sql and plsql, you are switching between sql and plsql engine. So it does not make sense to incur this overhead without proper requirement.
If for some reason there is still a need for doing this, you can atleast implement bulk delete to reduce some overhead. Please refer to the code below to find out how to do that -
DECLARE
TYPE t_del IS TABLE OF VARCHAR2(100);
l_del t_del;
CURSOR c IS
SELECT MIN(ROWID) RID
FROM test_tbl TAB
GROUP BY TAB.age, TAB.gender
HAVING COUNT(*) > 1;
BEGIN
OPEN c;
LOOP
FETCH c BULK COLLECT INTO l_del;
EXIT WHEN l_del.COUNT = 0;
FORALL i IN l_del.FIRST..l_del.last
DELETE FROM test_tbl WHERE ROWID = l_del(i);
END LOOP;
END;

Combining Multiple Statements in a Stored Procedure

I'm new to Oracle, what I'm trying to achieve I could in SQL but I'm having a tough time in doing it in Oracle.
So within a Stored Procedure, I'm trying to truncate a table, then insert values, lastly run a SELECT statement against the table.
Here is what I have but it doesn't work, when I run this script, it runs with no error but it seems it only goes through the first (TRUNCATE) statement and that's it.
I would like it create the Store Procedure (which it does) and then show me the contents of the table from the SELECT statement.
CREATE OR REPLACE procedure MYSTOREDPROCEDURE is
BEGIN
EXECUTE IMMEDIATE 'TRUNCATE TABLE MYTABLE';
INSERT INTO MYTABLE
(COL1,
COL2,
COL3)
SELECT COL1, COL2, COL3 FROM MYOTHERTABLE;
end ;
/
SELECT * FROM MYTABLE
END MYSTOREDPROCEDURE;
For clarification SQL is a language implmemented by many RDBMS including SQL Server, PostgreSQL etc. If you're doing this in Oracle you are using SQL. However, most RDBMS have also added a procedural extension to SQL such as T-SQL (SQL Server), pgPL/SQL (PostgreSQL) and PL/SQL (Oracle). In this case you're attempting to use PL/SQL.
From what you're attempting to do I assume you're used to SQL Server and temporary tables. It is less common and less necessary to use temporary tables in Oracle.
Firstly, EXECUTE IMMEDIATE 'TRUNCATE TABLE MYTABLE';. It should rarely be necessary to perform DDL in a stored procedure in Oracle; it would normally be indicative of a flaw in the data-model. In this case it appears as though you're using an actual table as a temporary table. I wouldn't do this. If you need a temporary table use a global temporary table.
Secondly, SELECT * FROM MYTABLE. You can't do this in PL/SQL. If you need to select some data you have to use SELECT <columns> INTO <variables> FROM .... If you do this it won't show you the contents of the table.
From your description of what you're attempting you only need to do the following:
SELECT COL1, COL2, COL3 FROM MYOTHERTABLE;
There is no need for PL/SQL (stored procedures) at all.
I suspect you intended to execute the SELECT * FROM MYTABLE after the procedure was compiled and executed. The above could be restrucured as:
CREATE OR REPLACE procedure MYSTOREDPROCEDURE is
BEGIN
EXECUTE IMMEDIATE 'TRUNCATE TABLE MYTABLE';
INSERT INTO MYTABLE (COL1, COL2, COL3)
SELECT COL1, COL2, COL3
FROM MYOTHERTABLE;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Error in MYSTOREDPROCEDURE : ' || SQLCODE || ' - ' || SQLERRM);
RAISE;
END MYSTOREDPROCEDURE;
/
EXECUTE MYSTOREDPROCEDURE
/
SELECT * FROM MYTABLE
/
Share and enjoy.
My DBA won't let me have TRUNCATE privileges even though truncate removes high water marks compared to delete MYTABLE; Your code will fail without these privileges. Both #Ben and #Bob Jarvis provide sound advice with the missing INTO and nicer form. One addition to Bob Jarvis' answer is that you need to start the command with SET SERVEROUTPUT ON SIZE 100000;. Otherwise, his nicely crafted error message will not be displayed. The serveroutput command is used for DBMS_OUTPUT and only lasts as long as your database session lasts.

Populating a cursor with a rowtype variable

No amount of googling seems to find the answer to this one...
I'm trying to modify Oracle sproc that that currently pulling values out of some tables and moving them to other tables.
It has a ROWTYPE variable that is defined like this:
myRow my_tbl%ROWTYPE;
Right now, the sproc does some logic that populates the rowtype variable and then uses it to populate a table:
INSERT INTO MY_TBL
( col1,
col2,
col3,
-snip-
)
VALUES (
myRow.aValue,
myRow.aValue2,
myRow.aValu3,
-snip-
)
Instead of populating a table, I want to use the ROWTYPE to populate a cursor that is returned to a Web app. However, I can't find a way to do this ROWTYPE -> REF CURSOR conversion. Is this possible? If not, is there a way to manually populate a cursor with data drawn from various tables and using some complex logic? I'm using Oracle 10g.
Thanks!
The tricky part about this is that you are using a REF CURSOR, which is intended for a set of rows to return data for just a single row. I imagine you are just doing this because your web app understands ref cursors and how to get them from Oracle, but not object types. I am also guessing that for some reason you can't just write a single select statment to retrieve and manipulate the data as needed (this is the easiest way, so with more info we can possibly help you out to achieve it).
There are a few ways I can think of to do this, none of them very pretty, so hopefully someone else will chime in with a better idea.
1) Create the cursor by selecting your calculated variables from dual
DECLARE
refcur SYS_REFCURSOR;
myRow TBL%ROWTYPE;
BEGIN
myRow.aValue := 1;
myRow.aValue2 := 3;
myRow.aValue3 := 5;
OPEN refcur
FOR
select
myRow.aValue,
myRow.aValue2,
myRow.aValue3
from
dual;
CLOSE refcur;
END;
2) Create a pipelined function that returns a table of your rowtype, and create your cursor from a select from that function.
The select from dual would be something like
select myRow.aValue,
myRow.aValue2,
myRow.aValu3
from dual;
You should be able to declare a cursor for that.
There is a good writeup of REF CURSOR at http://psoug.org/reference/ref_cursors.html

DBMS_SQL.Execute and BULK update - Need Help

I have this query
select col1, col2 from table where
critera = :criteria_var
The particular query was being used in DBMS_SQL to open cursor and BIND the variables. The values are then being fetched by DBMS_SQL functions which are then updating another table. but this is happening one row at a time.
I want to use BULK FETCH INTO. I have read the tutorials but i couldn't find anythign where i can use BULK FETCH INTO with DBMS_SQL.
is it possible? if yes, then how?
You can use the BIND_ARRAY procedure in the DBMS_SQL package to do a bulk fetch. There is an example of this in the DBMS_SQL documentation.
Unless there is a particular need to use DBMS_SQL, however, (and assuming dynamic SQL is actually necessary in the first place) it seems likely that it would be easier to use native dynamic SQL, i.e.
EXECUTE IMMEDIATE 'SELECT col1, col2 FROM tableName WHERE criteria = :1'
BULK COLLECT INTO l_col1_collection, l_col2_collection
USING l_criteria_variable;
If you are just fetching the data from this query in order to update a different table, however, it would be more efficient to just let Oracle do that work by constructing a single UPDATE statement that used this query to fetch multiple rows. Something like
UPDATE destination_table dest
SET (col1, col2) = (SELECT col1, col2
FROM source_table_name src
WHERE criteria = l_criteria_variable
AND src.key_column = dest.key_column)
WHERE EXISTS( SELECT 1
FROM source_table_name src
WHERE criteria = l_criteria_variable
AND src.key_column = dest.key_column)

Resources