Tried the following code:
--------------- Setup ------------
drop table suk_rc_t1;
create table suk_rc_t1 (x number, y number);
insert into suk_rc_t1(x) values(1);
commit;
create or replace function suk_instn_id_wrap(
call_id pls_integer )
return pls_integer as
begin
dbms_output.put_line('suk_instn_id_wrap ' || call_id);
--return suk_instn_id;
return 123;
end;
/
--------------- How many RUNs of suk_instn_id_wrap in 2 queries below ? ------------
select 3 from suk_rc_t1
where (coalesce (y, suk_instn_id_wrap(1)) = suk_instn_id_wrap(2)
or suk_instn_id_wrap(3) is null);
begin
dbms_output.put_line('Done');
end;
/
with suk_rc_t1 as (select 1 x, null y from dual)
select 3 from suk_rc_t1
where (coalesce (y, suk_instn_id_wrap(1)) = suk_instn_id_wrap(2)
or suk_instn_id_wrap(3) is null);
begin
dbms_output.put_line('Done');
end;
/
I was expecting to get the same output. Instead I got:
suk_instn_id_wrap 3
suk_instn_id_wrap 1
suk_instn_id_wrap 2
Done
suk_instn_id_wrap 1
suk_instn_id_wrap 2
Done
Does anybody have an explanation for this behaviour?
It will be an optimizer thing, and although I can't say for certain I suspect the reasoning might be as follows:
In the first case, Oracle needs to read the database to access column y, which it would prefer not to do if it doesn't have to, so it prefers to evaluate suk_instn_id_wrap(3) first and avoid reading the database. Of course, it turns out to be false and so has to evaulate the first expression anyway. Bad luck.
In the second case, Oracle knows that y is null, so in that case neither side of the OR condition costs any more in terms of database access. In this case, perhaps it defaults to the original order of the expressions. You might think that the second condition would be better since there is only one function call, but perhaps that is not considered.
Related
I have an old client software which has a connected oracle database for persistence. As interface the client software only allows the call of functions and procedures. I have nearly full access to the database, i.e., I can define functions and procedures. Because of the interface, only functions can return values and I cannot use the OUT parameter option of procedures.
Now I simply want to read a value from a table:
SELECT value FROM myTable WHERE id = 42;
And increase the value afterwards:
UPDATE myTable SET value = value + 1 WHERE id = 42;
I could use a function for the select statement and a procedure for the update and call both successively. The problem here is the non-existence of transactions on the client side. Thus, between select and update another thread could get wrong values.
So my question is, how can I use both calls in a transaction without using transactions...
Tried Approaches:
Use anonymous PL/SQL Blocks -> the syntax is not supported by the client.
Put both calls in a single function -> DML is not allowed in a select statement.
PRAGMA AUTONOMOUS_TRANSACTION -> I heard it is a bad thing and should not be used.
You can do DML inside a function as demonstrated below, but I stress - take heed of the other comments. Look at using a sequence (even multiple sequences), because doing DML inside a function is generally a bad idea, because the number of executions of a function call (if called from SQL) is not deterministic. Also, there are scalability issues if used in a high volume. And in a multi-user environment, you need to handle locking/serialization otherwise you'll multiple sessions getting the same integer value returned.
So...after all that, you still want to head this path :-(
SQL> create table t ( x int );
Table created.
SQL> insert into t values (0);
1 row created.
SQL>
SQL> create or replace
2 function f return int is
3 pragma autonomous_transaction;
4 retval int;
5 begin
6 update t
7 set x = x + 1
8 returning x into retval;
9 commit;
10 return retval;
11 end;
12 /
Function created.
SQL>
SQL> select f from dual;
F
----------
1
1 row selected.
SQL> select * from t;
X
----------
1
1 row selected.
SQL> select f from dual;
F
----------
2
1 row selected.
SQL> select * from t;
X
----------
2
1 row selected.
SQL> select f from dual;
F
----------
3
1 row selected.
SQL> select * from t;
X
----------
3
1 row selected.
I have three functions which are all doing the same. I like to know whether SELECT ... FROM EMP WHERE DEPT_ID = v_dept returns any row.
Which one would be the fastest way?
CREATE OR REPLACE FUNCTION RecordsFound1(v_dept IN EMP.DEPT_ID%TYPE) RETURN BOOLEAN IS
n INTEGER;
BEGIN
SELECT COUNT(*) INTO res FROM EMP WHERE DEPT_ID = v_dept;
RETURN n > 0;
END;
/
CREATE OR REPLACE FUNCTION RecordsFound2(v_dept IN EMP.DEPT_ID%TYPE) RETURN BOOLEAN IS
CURSOR curEmp IS
SELECT DEPT_ID FROM EMP WHERE DEPT_ID = v_dept;
dept EMP.DEPT_ID%TYPE;
res BOOLEAN;
BEGIN
OPEN curEmp;
FETCH curEmp INTO dept;
res := curEmp%FOUND;
CLOSE curEmp;
RETURN res;
END;
/
CREATE OR REPLACE FUNCTION RecordsFound3(v_dept IN EMP.DEPT_ID%TYPE) RETURN BOOLEAN IS
dept EMP.DEPT_ID%TYPE;
BEGIN
SELECT DEPT_ID INTO dept FROM EMP WHERE DEPT_ID = v_dept;
RETURN TRUE;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN FALSE;
WHEN TOO_MANY_ROWS THEN
RETURN TRUE;
END;
/
Assume table EMP is very big and condition WHERE DEPT_ID = v_dept could match on thousands of rows.
Usually I would expect RecordsFound2 to be the fastest, because it has to fetch (maximum) only one single row. So in terms of I/O it should be the best.
For the non-believers: the exists() version:
CREATE OR REPLACE FUNCTION RecordsFound0(v_dept IN EMP.DEPT_ID%TYPE) RETURN BOOLEAN IS
BEGIN
RETURN EXISTS( SELECT 1 FROM EMP WHERE DEPT_ID = v_dept);
END;
The Postgresql version:
CREATE OR REPLACE FUNCTION RecordsFound0(v_dept IN EMP.DEPT_ID%TYPE) RETURNS BOOLEAN AS
$func$
BEGIN
RETURN EXISTS( SELECT 1 FROM EMP WHERE DEPT_ID = v_dept);
END
$func$ LANGUAGE plpgsql;
And in Postgres the function can be implemented in pure sql, without the need for plpgsql(in Postgres the select does not need a ... FROM DUAL
CREATE OR REPLACE FUNCTION RecordsFound0s(v_debt IN EMP.DEPT_ID%TYPE) RETURNS BOOLEAN AS
$func$
SELECT EXISTS( SELECT NULL FROM EMP WHERE DEPT_ID = v_debt);
$func$ LANGUAGE sql;
Note: the unary EXISTS(...) operator yields a Boolean, which is exactly what you want.
Note2: I hope I have the Oracle syntax correct. (keywords RETURN <-->RETURNS and AS <-->IS)
Your solution 1 Count all occurrences:
You have the DBMS do much more work than needed. why let it scan the table and count all occurences when you only want to know whether at least one exists or not? This is slow. (But on a small emp table with an index on dept_id this may still look fast :-)
Your solution 2 Open a Cursor and only fetch the first record
A good idea and probably rather fast, as you stop, once you found a record. However, the DBMS doesn't know that you only want to look for the mere existence and may decide for a slow execution plan, as it expects you to fetch all matches.
Your solution 3 Fetch the one record or get an exception
This may be a tad faster, as the DBMS expects to find one record only. However, it must test for further matches in order to raise TOO_MANY_ROWS in case. So in spite of having found a record already it must look on.
solution 4 Use COUNT and ROWNUM
By adding AND ROWNUM = 1 you show the DBMS that you want one record only. At a minimum the DBMS knows it can stop at some point, at best it even notices that it is only one record needed. So depending on the implementation the DBMS may find the optimal execution plan.
solution 5 Use EXISTS
EXISTS is made to check for mere existence, so the DBMS can find the optimal execution plan. EXISTS is an SQL word, not a PL/SQL word and the SQL engine doesn't know BOOLEAN, so the function gets a bit clumsy:
CREATE OR REPLACE FUNCTION RecordsFound1(v_dept IN EMP.DEPT_ID%TYPE) RETURN BOOLEAN IS
v_1_is_yes_0_is_no INTEGER;
BEGIN
SELECT COUNT(*) INTO v_1_is_yes_0_is_no
FROM DUAL
WHERE EXISTS (SELECT * FROM EMP WHERE DEPT_ID = v_dept);
RETURN n = 1;
END;
The absolute fastest ways is to not call the count function at all.
A typical pattern is
count the number of rows
if cnt = 0 then do something
else read chunk of data and process
Simple read the data and than perform the count test on it.
you can break a SQL by adding an extra condition to your where-clause:
where ...
and rownum = 1;
this stops immediatly if at least one record is found and it is as fast as the "exists" operator.
See the followint sample code:
create or replace function test_record_exists(pi_some_parameter in varchar2) return boolean is
l_dummy varchar2(10);
begin
select 'x'
into l_dummy
from <your table>
where <column where you want to filter for> = pi_some_parameter
and rownum = 1;
return (true);
exception
when no_data_found then
return (false);
end;
If you use:
select count(*)
from my_table
where ...
and rownum = 1;
... then the query will:
be executed in the most efficient fashion
always return a single row
return either 0 or 1
This three factors make it very fast and very easy to use in PL/SQL as you do not have to concern yourself with whether a row is returned or not.
The returned value is also amenable to use as a true/false boolean, of course.
If you wanted to list the departments that either do or do not have any records in the emp table then I would certainly use EXISTS, as the semi-(anti)join is the most efficient means of executing the query:
select *
from dept
where [NOT] exists (
select null
from emp
where emp.dept_id = dept.id);
DECLARE
dynaCol varchar2(200);
varCol varchar2(100);
BEGIN
dbms_output.put_line(dynaCol);
select name into varCol from GSSP_ETL.DEMO_TABLE_CHECK where ID='1'|| (SELECT q'$ and optional='N'$' NAME FROM DUAL) ;
dbms_output.put_line(varCol);
END;
Though I have a row for id=1 and optional=N I still get no data found error.
SELECT q'$ and optional='N'$' NAME FROM DUAL gives me and optional='N', so there is nothing wrong to use like this.
Please help if anyone know the reason or better way to append the and in where clause.
Your original query is looking for the row with ID = "1 and optional = 'N'" which causes the problem.
You should use dynamic SQL if you want to have variable WHERE clause:
SQL> create table t (id, name, optional)
2 as
3 select 1, 'XXX','N' from dual
4 /
SQL> select * from t;
ID NAM O
---------- --- -
1 XXX N
SQL> set serveroutput on
SQL> declare
2 dynaCol varchar2(200) := q'[ and optional='N']';
3 varCol varchar2(100);
4 p_id int := 1;
5 begin
6
7 execute immediate
8 ' select name from t where id = :x '||dynaCol
9 into varCol using p_id;
10 dbms_output.put_line(varCol);
11 end;
12 /
XXX
Sorry i couldn't comment due to my low reputation..
No,its not possible to do with out immediate i think so,the best way is what Dmitry did.
Its possible to send whole select statement dynamically but a part with out execute immediate..
...You got close.
Since you're using PL/SQL, why not consider creating a persistent object such as a PL/SQL Stored Procedure instead? There will be less code on the JBOSS side and the database schema layout will be further separated from the code you intended on accessing the database.
The Demonstration Table
This is an interpretation from pieces of your original post. This is the table I used to test out my recommendations.
CREATE TABLE "DEMO_TABLE_CHECK"
( "ID" NUMBER(10,0) NOT NULL ENABLE,
"NAME" VARCHAR2(40) NOT NULL ENABLE,
"OPTIONAL" VARCHAR2(5) NOT NULL ENABLE,
CONSTRAINT "DEMO_TABLE_CHECK_PK" PRIMARY KEY ("ID") ENABLE
)
/
My test data:
The Procedure Source Code
CREATE or REPLACE PROCEDURE proc_example(p_id IN number,
p_opt IN varchar2 default null)
IS
Result varchar2(100);
BEGIN
SELECT name
INTO Result
FROM GSSP_ETL.DEMO_TABLE_CHECK
WHERE id = p_id
AND optional = nvl(p_opt, optional);
dbms_output.put_line(Result);
END proc_example;
Notice that this example solution actually does away with the dynamic SQL calls proposed in previous solutions. The realm of PL/SQL procedural code is likely to accommodate for pushing around and concatenating strings of SQL commands.
An Example Call and the Output
begin
proc_example(p_id=> 1, p_opt=>'N');
end;
-- output:
ALPHA
begin
proc_example(p_id=> 1, p_opt=> null);
proc_example(p_id=> 1);
end;
-- output:
ALPHA
ALPHA
All this PL/SQL code, which was originally in an a block passed from the part of your program that accessed the database will now reside on the database.
Discussion of the PL/SQL Procedure Design
The optional part of the query, represented by p_opt, has a default designation. This means if there is no value for that parameter, then the procedure will ignore it and assume it is equal to the defined default value. No errors will be thrown.
AND optional = nvl(p_opt, optional)
This line is a replacement for the add-on SQL string. the input parameter p_opt, whether it was supplied (such as = 'N') or skipped ( implied to = null ), the SQL script includes or excludes the effect of this operator based on the "switching" parameter supplied.
Closing Comments:
If you want to see a better differentiation of results based on that last, dynamic SQL command, you might want to try scenarios where the first criteria is actually ambiguous, such as multiple instances if the ID column. (If ID = 1 was true for more than one record...) But of that set of results, have the second criteria identify something unique when in combination with the first.
I have been getting an intermittent issue when executing to_number function in the where clause on a varchar2 column if number of records exceed a certain number n. I used n as there is no exact number of records on which it happens. On one DB it happens after n was 1 million on another when it was 0.1. million.
E.g. I have a table with 10 million records say Table Country which has field1 varchar2 containing numberic data and Id
If I do a query as an example
select *
from country
where to_number(field1) = 23
and id >1 and id < 100000
This works
But if I do the query
select *
from country
where to_number(field1) = 23
and id >1 and id < 100001
It fails saying invalid number
Next I try the query
select *
from country
where to_number(field1) = 23
and id >2 and id < 100001
It works again
As I only got invalid number it was confusing, but in the log file it said
Memory Notification: Library Cache Object loaded into SGA
Heap size 3823K exceeds notification threshold (2048K)
KGL object name :with sqlplan as (
select c006 object_owner, c007 object_type,c008 object_name
from htmldb_collections
where COLLECTION_NAME='HTMLDB_QUERY_PLAN'
and c007 in ('TABLE','INDEX','MATERIALIZED VIEW','INDEX (UNIQUE)')),
ws_schemas as(
select schema
from wwv_flow_company_schemas
where security_group_id = :flow_security_group_id),
t as(
select s.object_owner table_owner,s.object_name table_name,
d.OBJECT_ID
from sqlplan s,sys.dba_objects d
It seems its related to SGA size, but google did not give me much help on this.
Does anyone have any idea about this issue with TO_NUMBER or oracle functions for large data?
which has field1 varchar2 containing
numberic data
This is not good practice. Numeric data should be kept in NUMBER columns. The reason is simple: if we don't enforce a strong data type we might find ourselves with non-numeric data in our varchar2 column. If that were to happen then a filter like this
where to_number(field1) = 23
would fail with ORA-01722: invalid number.
I can't for certain sure say this is what is happening in your scenario, because I don't understand why apparently insignificant changes in the filters of ID have changed the success of the query. It would be instructive to see the execution plans for the different versions of the queries. But I think it is more likely to be a problem with your data than a bug in the SGA.
Assuming you know that the given range of ids will always result in field1 containing numeric data, you could do this instead:
select *
from (
select /*+NO_MERGE*/ *
from country
where id >1 and id < 100000
)
where to_number(field1) = 23;
Suggest doing the following to determine for sure whether there are records containing non-numeric data. As others have said, variations in the execution plan and order of evaluation could explain why the error does not appear consistently.
(assuming SQLPlus as the client)
SET SERVEROUTPUT ON
DECLARE
x NUMBER;
BEGIN
FOR rec IN (SELECT id, field1 FROM country) LOOP
BEGIN
x := TO_NUMBER( rec.field1 );
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line( rec.id || ' ' || rec.field1 );
END;
END LOOP;
END;
/
An alternative workaround to your original issue would be to rewrite the query to avoid implicit type conversion, e.g.
SELECT id, TO_NUMBER( field1 )
FROM county
WHERE field1 = '23'
AND <whatever condition on id you want, if any>
Consider writing an IS_NUMBER PL/SQL function:
CREATE OR REPLACE FUNCTION IS_NUMBER (p_input IN VARCHAR2) RETURN NUMBER
AS
BEGIN
RETURN TO_NUMBER (p_input);
EXCEPTION
WHEN OTHERS THEN RETURN NULL;
END IS_NUMBER;
/
SQL> SELECT COUNT(*) FROM DUAL WHERE IS_NUMBER ('TEST') IS NOT NULL;
COUNT(*)
----------
0
SQL> SELECT COUNT(*) FROM DUAL WHERE IS_NUMBER ('123.45') IS NOT NULL;
COUNT(*)
----------
1
Let's say I have a function call on a select or where clause in Oracle like this:
select a, b, c, dbms_crypto.hash(utl_raw.cast_to_raw('HELLO'),3)
from my_table
A similar example can be constructed for MS SQLServer.
What's the expected behavior in each case?
Is the HASH function going to be called once for each row in the table, or DBMS will be smart enough to call the function just once, since it's a function with constant parameters and no side-effects?
Thanks a lot.
The answer for Oracle is it depends. The function will be called for every row selected UNLESS the Function is marked 'Deterministic' in which case it will only be called once.
CREATE OR REPLACE PACKAGE TestCallCount AS
FUNCTION StringLen(SrcStr VARCHAR) RETURN INTEGER;
FUNCTION StringLen2(SrcStr VARCHAR) RETURN INTEGER DETERMINISTIC;
FUNCTION GetCallCount RETURN INTEGER;
FUNCTION GetCallCount2 RETURN INTEGER;
END TestCallCount;
CREATE OR REPLACE PACKAGE BODY TestCallCount AS
TotalFunctionCalls INTEGER := 0;
TotalFunctionCalls2 INTEGER := 0;
FUNCTION StringLen(SrcStr VARCHAR) RETURN INTEGER AS
BEGIN
TotalFunctionCalls := TotalFunctionCalls + 1;
RETURN Length(SrcStr);
END;
FUNCTION GetCallCount RETURN INTEGER AS
BEGIN
RETURN TotalFunctionCalls;
END;
FUNCTION StringLen2(SrcStr VARCHAR) RETURN INTEGER DETERMINISTIC AS
BEGIN
TotalFunctionCalls2 := TotalFunctionCalls2 + 1;
RETURN Length(SrcStr);
END;
FUNCTION GetCallCount2 RETURN INTEGER AS
BEGIN
RETURN TotalFunctionCalls2;
END;
END TestCallCount;
SELECT a,TestCallCount.StringLen('foo') FROM(
SELECT 0 as a FROM dual
UNION
SELECT 1 as a FROM dual
UNION
SELECT 2 as a FROM dual
);
SELECT TestCallCount.GetCallCount() AS TotalFunctionCalls FROM dual;
Output:
A TESTCALLCOUNT.STRINGLEN('FOO')
---------------------- ------------------------------
0 3
1 3
2 3
3 rows selected
TOTALFUNCTIONCALLS
----------------------
3
1 rows selected
So the StringLen() function was called three times in the first case. Now when executing with StringLen2() which is denoted deterministic:
SELECT a,TestCallCount.StringLen2('foo') from(
select 0 as a from dual
union
select 1 as a from dual
union
select 2 as a from dual
);
SELECT TestCallCount.GetCallCount2() AS TotalFunctionCalls FROM dual;
Results:
A TESTCALLCOUNT.STRINGLEN2('FOO')
---------------------- -------------------------------
0 3
1 3
2 3
3 rows selected
TOTALFUNCTIONCALLS
----------------------
1
1 rows selected
So the StringLen2() function was only called once since it was marked deterministic.
For a function not marked deterministic, you can get around this by modifying your query as such:
select a, b, c, hashed
from my_table
cross join (
select dbms_crypto.hash(utl_raw.cast_to_raw('HELLO'),3) as hashed from dual
);
For SQL server, it will be evaluated for every single row.
You will be MUCH better off by running the function once and assigning to a variable and using the variable in the query.
short answer....it depends.
If the function is accessing data ORACLE does not know if it is going to be the same for each row, therefore, it needs to query for each. If, for example, your function is just a formatter that always returns the same value then you can turn on caching (marking it as Deterministic) which may allow for you to only do the function call once.
Something you may want to look into is ORACLE WITH subquery:
The WITH query_name clause lets you
assign a name to a subquery block. You
can then reference the subquery block
multiple places in the query by
specifying the query name. Oracle
optimizes the query by treating the
query name as either an inline view or
as a temporary table
I got the quoted text from here, which has plenty of examples.