Alter session slows down the query through Hibernate - sql

I'm using Oracle 11gR2 and Hibernate 4.2.1.
My application is a searching application.
Only has SELECT operations and all of them are native queries.
Oracle uses case-sensitive sort by default.
I want to override it to case-insensitive.
I saw couple of option here http://docs.oracle.com/cd/A81042_01/DOC/server.816/a76966/ch2.htm#91066
Now I'm using this query before any search executes.
ALTER SESSION SET NLS_SORT='BINARY_CI'
If I execute above sql before execute the search query, hibernate takes about 15 minutes to return from search query.
If I do this in Sql Developer, It returns within couple of seconds.
Why this kind of two different behaviors,
What can I do to get rid of this slowness?
Note: I always open a new Hibernate session for each search.
Here is my sql:
SELECT *
FROM (SELECT
row_.*,
rownum rownum_
FROM (SELECT
a, b, c, d, e,
RTRIM(XMLAGG(XMLELEMENT("x", f || ', ') ORDER BY f ASC)
.extract('//text()').getClobVal(), ', ') AS f,
RTRIM(
XMLAGG(XMLELEMENT("x", g || ', ') ORDER BY g ASC)
.extract('//text()').getClobVal(), ', ') AS g
FROM ( SELECT src.a, src.b, src.c, src.d, src.e, src.f, src.g
FROM src src
WHERE upper(pp) = 'PP'
AND upper(qq) = 'QQ'
AND upper(rr) = 'RR'
AND upper(ss) = 'SS'
AND upper(tt) = 'TT')
GROUP BY a, b, c, d, e
ORDER BY b ASC) row_
WHERE rownum <= 400
) WHERE rownum_ > 0;
There are so may fields comes with LIKE operation, and it is a dynamic sql query. If I use order by upper(B) asc Sql Developer also takes same time.
But order by upper results are same as NLS_SORT=BINARY_CI. I have used UPPER('B') indexes, but nothings gonna work for me.
A's length = 10-15 characters
B's length = 34-50 characters
C's length = 5-10 characters
A, B and C are sort-able fields via app.
This SRC table has 3 million+ records.
We finally ended up with a SRC table which is a materialized view.
Business logic of the SQL is completely fine.
All of the sor-table fields and others are UPPER indexed.

UPPER() and BINARY_CI may produce the same results but Oracle cannot use them interchangeably. To use an index and BINARY_CI you must create an index like this:
create index src_nlssort_index on src(nlssort(b, 'nls_sort=''BINARY_CI'''));
Sample table and mixed case data
create table src(b varchar2(100) not null);
insert into src select 'MiXeD CAse '||level from dual connect by level <= 100000;
By default the upper() predicate can perform a range scan on the the upper() index
create index src_upper_index on src(upper(b));
explain plan for
select * from src where upper(b) = 'MIXED CASE 1';
select * from table(dbms_xplan.display(format => '-rows -bytes -cost -predicate
-note'));
Plan hash value: 1533361696
------------------------------------------------------------------
| Id | Operation | Name | Time |
------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| SRC | 00:00:01 |
| 2 | INDEX RANGE SCAN | SRC_UPPER_INDEX | 00:00:01 |
------------------------------------------------------------------
BINARY_CI and LINGUISTIC will not use the index
alter session set nls_sort='binary_ci';
alter session set nls_comp='linguistic';
explain plan for
select * from src where b = 'MIXED CASE 1';
select * from table(dbms_xplan.display(format => '-rows -bytes -cost -note'));
Plan hash value: 3368256651
---------------------------------------------
| Id | Operation | Name | Time |
---------------------------------------------
| 0 | SELECT STATEMENT | | 00:00:02 |
|* 1 | TABLE ACCESS FULL| SRC | 00:00:02 |
---------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(NLSSORT("B",'nls_sort=''BINARY_CI''')=HEXTORAW('6D69786564
2063617365203100') )
Function based index on NLSSORT() enables index range scans
create index src_nlssort_index on src(nlssort(b, 'nls_sort=''BINARY_CI'''));
explain plan for
select * from src where b = 'MIXED CASE 1';
select * from table(dbms_xplan.display(format => '-rows -bytes -cost -note'));
Plan hash value: 478278159
--------------------------------------------------------------------
| Id | Operation | Name | Time |
--------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| SRC | 00:00:01 |
|* 2 | INDEX RANGE SCAN | SRC_NLSSORT_INDEX | 00:00:01 |
--------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access(NLSSORT("B",'nls_sort=''BINARY_CI''')=HEXTORAW('6D69786564
2063617365203100') )

I investigated and found that The parameters NLS_COMP y NLS_SORT may affect how oracle make uses of execute plan for string ( when it is comparing or ordering).
Is not necesary to change NLS session. adding
ORDER BY NLSSORT(column , 'NLS_SORT=BINARY_CI')
and adding a index for NLS is enough
create index column_index_binary as NLSSORT(column , 'NLS_SORT=BINARY_CI')
I found a clue to a problem in this issue so i'm paying back.
Why oracle stored procedure execution time is greatly increased depending on how it is executed?

Related

What is SYS_OP_UNDESCEND and SYS_OP_DESCEND in Oracle Explain Plan?

I have an Oracle explain plan that looks like this:
Plan hash value: 2484140766
--------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 180K| 84M| 5 (0)| 00:00:01 |
|* 1 | COUNT STOPKEY | | | | | |
| 2 | VIEW | | 180K| 84M| 5 (0)| 00:00:01 |
|* 3 | TABLE ACCESS BY INDEX ROWID | OSTRICH | 6500K| 793M| 5 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN DESCENDING| OSTRICH_ENDDATE_IDX_2 | 1 | | 4 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(ROWNUM<=180000)
3 - filter("OSTRICH_STATUS_ID"=2)
4 - access(SYS_OP_DESCEND("END_DATE")>=SYS_OP_DESCEND(SYSDATE#!))
filter(SYS_OP_UNDESCEND(SYS_OP_DESCEND("END_DATE"))<=SYSDATE#!)
I have been trying to understand what is happening with these 2 lines at the bottom:
4 - access(SYS_OP_DESCEND("END_DATE")>=SYS_OP_DESCEND(SYSDATE#!))
filter(SYS_OP_UNDESCEND(SYS_OP_DESCEND("END_DATE"))<=SYSDATE#!)
What do SYS_OP_UNDESCEND and SYS_OP_DESCEND mean?
The index that the explain plan references is (I think) called a descending index. (I do not know a lot about Oracle indexing.) The DDL for that index is:
CREATE INDEX
OSTRICH_ENDDATE_IDX_2
ON
OSTRICH
(
"END_DATE" DESC
);
The actual query looks like this:
SELECT
l.id,
l.end_date,
l.status
FROM
(
SELECT
*
from OSTRICH l2
where END_DATE <= SYSDATE
and OSTRICH_STATUS_ID = 2
order by l2.END_DATE
) l
WHERE ROWNUM <= 180000;
What do SYS_OP_UNDESCEND and SYS_OP_DESCEND mean? This query is taking much longer than I would expect, and I am trying to understand what impact the descending and undescending has on the query?
Oracle implements the descending index "as if" it were a function-based index. Function-based indexes are invoked when a query uses the function call; thus an FBI on upper(col1) would be used when the WHERE clause filters on upper(col1) = 'WHATEVER'.
In this case I think the SYS_OP_DESCEND is the "function" Oracle uses when creating a descending index I think it is then invoking SYS_OP_UNDESCEND because your WHERE clause is unsuited to a descending index. It's not surprising the performance sucks.
There are very few use cases where a descending index is a good idea. Why are you using one on this column on this table?
Assuming there is a good reason for using the index and you can't just drop it, your best bet for improved performance would be to not use the index for this query. Doing something like this should prevent the optimiser not using the index:
SELECT
l.id,
l.end_date,
l.status
FROM
(
SELECT /*+ NO_INDEX(l2 OSTRICH_ENDDATE_IDX_2) */
*
from OSTRICH l2
where END_DATE <= SYSDATE
and OSTRICH_STATUS_ID = 2
order by l2.END_DATE
) l
WHERE ROWNUM <= 180000;
SYS_OP_UNDESCENDand SYS_OP_DESCEND are internal functions used by the CBO that appear in the EXPLAIN PLAN when a function based index is used or a sort operation inside an index clause has been specified.
In your case, you are using an INDEX with a SORT clause
CREATE INDEX
OSTRICH_ENDDATE_IDX_2
ON
OSTRICH
(
"END_DATE" DESC
);
Your plan shows these two operations:
access(SYS_OP_DESCEND("END_DATE")>=SYS_OP_DESCEND(SYSDATE#!))
filter(SYS_OP_UNDESCEND(SYS_OP_DESCEND("END_DATE"))<=SYSDATE#!)
The first operation is the access, based on the desc index clause of the index itself, and the second the filter. Both appear because the query is done against the nature of the index.
I would never use this clause in any index unless the access is done in that way always, which is quite rare because sorting in different ways is what normally SQL is used for.
There is also this bug: ( fixed in 20.1 )
Bug 27589260 wrong sort order due to virtual column replacement in function based index
That degrades the performance of the query when a virtual column is present in the table and a function based index has been used.

Is a 3 column SQL index used when the middle column can be anything?

Trying to prove something out currently to see if adding an index is necessary.
If I have an index on columns A,B,C and I create a query that in the where clause is only explicitly utilizing A and C, will I get the benefit of the index?
In this scenario imagine the where clause is like this:
A = 'Q' AND (B is not null OR B is null) AND C='G'
I investigated this in Oracle using EXPLAIN PLAN and it doesn't seem to use the index. Also, from my understanding of how indexes are created and used it won't be able to benefit because the index can't leverage column B due to the lack of specifics.
Currently looking at this in either MSSQL or ORACLE. Not sure if one optimizes differently than the other.
Any advice is appreciated! Thank you!
Connected to Oracle Database 12c Enterprise Edition Release 12.1.0.2.0
SQL> create table t$ (a integer not null, b integer, c integer, d varchar2(100 char));
Table created
SQL> insert into t$ select rownum, rownum, rownum, lpad('0', '1', 100) from dual connect by level <= 1000000;
1000000 rows inserted
SQL> create index t$i on t$(a, b, c);
Index created
SQL> analyze table t$ estimate statistics;
Table analyzed
SQL> explain plan for select * from t$ where a = 128 and c = 128;
Explained
SQL> select * from table(dbms_xplan.display());
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 3274478018
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 13 | 4 (0)
| 1 | TABLE ACCESS BY INDEX ROWID BATCHED| T$ | 1 | 13 | 4 (0)
|* 2 | INDEX RANGE SCAN | T$I | 1 | | 3 (0)
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("A"=128 AND "C"=128)
filter("C"=128)
15 rows selected
Any question?
If you look at the B + tree structure of the index, then the answer is as follows
The left-hand side of the index, including the first inequality, will go to Seek Predicate, the rest in Predicate in queryplan.
For example read http://use-the-index-luke.com/sql/where-clause/the-equals-operator/concatenated-keys

Need to speed up my UPDATE QUERY based on the EXPLAIN PLAN

I am updating my table data using a temporary table and it takes forever and it still has not completed. So I collected an explain plan on the query. Can someone advise me on how to tune the query or build indexes on them.
The query:
UPDATE w_product_d A
SET A.CREATED_ON_DT = (SELECT min(B.creation_date)
FROM mtl_system_items_b_temp B
WHERE to_char(B.inventory_item_id) = A.integration_id
and B.organization_id IN ('102'))
where A.CREATED_ON_DT is null;
Explain plan:
Plan hash value: 1520882583
-----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------
| 0 | UPDATE STATEMENT | | 47998 | 984K| 33M (2)|110:06:25 |
| 1 | UPDATE | W_PRODUCT_D | | | | |
|* 2 | TABLE ACCESS FULL | W_PRODUCT_D | 47998 | 984K| 9454 (1)| 00:01:54 |
| 3 | SORT AGGREGATE | | 1 | 35 | | |
|* 4 | TABLE ACCESS FULL| MTL_SYSTEM_ITEMS_B_TEMP | 1568 | 54880 | 688 (2)| 00:00:09 |
-----------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("A"."CREATED_ON_DT" IS NULL)
4 - filter("B"."ORGANIZATION_ID"=102 AND TO_CHAR("B"."INVENTORY_ITEM_ID")=:B1)
Note
-----
- dynamic sampling used for this statement (level=2)
For this query:
UPDATE w_product_d A
SET A.CREATED_ON_DT = (SELECT min(B.creation_date)
FROM mtl_system_items_b_temp B
WHERE to_char(B.inventory_item_id) = A.integration_id
and B.organization_id IN ('102'))
where A.CREATED_ON_DT is null;
You have a problem. Why are you creating a temporary table with the wrong type for inventory_item_id? That is likely to slow down any access. So, let's fix the table first and then do the update:
alter table mtl_system_items_b_temp
add better_inventory_item_id varchar2(255); -- or whatever the right type is
update mtl_system_items_b_temp
set better_inventory_item_id = to_char(inventory_item_id);
Next, let's define the appropriate index:
create index idx_mtl_system_items_b_temp_3 on mtl_system_items_b_temp(better_inventory_item_id, organization_id, creation_date);
Finally, an index on w_product_d can also help:
create index idx_ w_product_d_1 w_product_d(CREATED_ON_DT);
Then, write the query as:
UPDATE w_product_d p
SET CREATED_ON_DT = (SELECT min(t.creation_date)
FROM mtl_system_items_b_temp t
WHERE t.better_nventory_item_id) = p.integration_id and
t.organization_id IN ('102')
)
WHERE p.CREATED_ON_DT is null;
Try a MERGE statement. It will likely go faster because it can read all the mtl_system_items_b_temp records at once rather than reading them over-and-over again for each row in w_product_d.
Also, your tables look like they're part of an Oracle e-BS environment. In the MTL_SYSTEM_ITEMS_B in such an environment, the INVENTORY_ITEM_ID and ORGANIZATION_ID columns are NUMBER. You seem to be using VARCHAR2 in your tables. Whenever you don't use the correct data types in your queries, you invite performance problems because Oracle must implicitly convert to the correct data type and, in doing so, loses its ability to use indexes on the column. So, make sure your queries treat each column correctly according to it's datatype. (E.g., if a column is a NUMBER use COLUMN_X = 123 instead of COLUMN_X = '123'.
Here's the MERGE example:
MERGE INTO w_product_d t
USING ( SELECT to_char(inventory_item_id) inventory_item_id_char, min(creation_date) min_creation_date
FROM mtl_system_items_b_temp
WHERE organization_id IN ('102') -- change this to IN (102) if organization_id is a NUMBER field!
) u
ON ( t.integration_id = u.inventory_item_id_char AND t.created_on_dt IS NULL )
WHEN MATCHED THEN UPDATE SET t.created_on_dt = nvl(t.created_on_date, u.min_creation_date) -- NVL is just in case...

performance difference between to_char and to_date [duplicate]

This question already has answers here:
How to optimize an Oracle query that has to_char in where clause for date
(6 answers)
Closed 9 years ago.
I have simple SQL query.. on Oracle 10g. I want to know the difference between these queries:
select * from employee where id = 123 and
to_char(start_date, 'yyyyMMdd') >= '2013101' and
to_char(end_date, 'yyyyMMdd') <= '20121231';
select * from employee where id = 123 and
start_date >= to_date('2013101', 'yyyyMMdd') and
end_date <= to_date('20121231', 'yyyyMMdd');
Questions:
1. Are these queries the same? start_date, end_date are indexed date columns.
2. Does one work better over the other?
Please let me know. thanks.
The latter is almost certain to be faster.
It avoids data type conversions on a column value.
Oracle will estimate better the number of possible values between two dates, rather than two strings that are representations of dates.
Note that neither will return any rows as the lower limit is probably intended to be higher than the upper limit according to the numbers you've given. Also you've missed a numeral in 2013101.
One of the biggest flaw when you converting, casting or transforming to expression (i.e. "NVL", "COALESCE" etc.) columns in WHERE clause is that CBO will not be able to use index on that column. I slightly modified your example to show the difference:
SQL> create table t_test as
2 select * from all_objects;
Table created
SQL> create index T_TEST_INDX1 on T_TEST(CREATED, LAST_DDL_TIME);
Index created
Created table and index for our experiment.
SQL> execute dbms_stats.set_table_stats(ownname => 'SCOTT',
tabname => 'T_TEST',
numrows => 100000,
numblks => 10000);
PL/SQL procedure successfully completed
We are making CBO think that our table kind of big one.
SQL> explain plan for
2 select *
3 from t_test tt
4 where tt.owner = 'SCOTT'
5 and to_char(tt.last_ddl_time, 'yyyyMMdd') >= '20130101'
6 and to_char(tt.created, 'yyyyMMdd') <= '20121231';
Explained
SQL> select * from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 2796558804
----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 3 | 300 | 2713 (1)| 00:00:33 |
|* 1 | TABLE ACCESS FULL| T_TEST | 3 | 300 | 2713 (1)| 00:00:33 |
----------------------------------------------------------------------------
Full table scan is used which would be costly on big table.
SQL> explain plan for
2 select *
3 from t_test tt
4 where tt.owner = 'SCOTT'
5 and tt.last_ddl_time >= to_date('20130101', 'yyyyMMdd')
6 and tt.created <= to_date('20121231', 'yyyyMMdd');
Explained
SQL> select * from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 1868991173
-------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 3 | 300 | 4 (0)| 00:00:01 |
|* 1 | TABLE ACCESS BY INDEX ROWID| T_TEST | 3 | 300 | 4 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | T_TEST_INDX1 | 8 | | 3 (0)| 00:00:01 |
---------------------------------------------------------------------------------------------
See, now it's index range scan and the cost is significantly lower.
SQL> drop table t_test;
Table dropped
Finally cleaning.
for output (displaying) purpose use to_char
for "date" handling (insert, update, compare etc) use to_date
I don't have any performance link to share, but using to_date in above Query should run faster!
While the to_char it will first cast the date and then for making the compare it will need to resolve it as date type. There will be a small performance loss.
As using to_date it will not need to cast first, it will use date type directly.

Is there any DB server that can optimize the following query?

Let's say I have the table my_table(id int not null primary key, datafield varchar(100)). Query
SELECT * from my_table where id = 100 performs an index seek. If I change it to
SELECT * from my_table where id+1 = 101
it scans the whole index (index scan) (at least it does it in SQL Server and Mysql). Is there any DB server which 'understands' that id +1 = 101 is the same as id = 101-1 ? I do realize that it's not a typical database operation, and server doesn't have to perform any math in such cases, but I wonder if it's implemented anywhere?
Thanks
UPDATE
So far I've tried SQL Server 2008 Enterprise, Mysql 5.1, 5.5. SQL Server shows clustered index seek and clustered index scan respectively. Mysql explain shows ref:const, key:primary, rows:1 and ref:null, key:null,rows: #total number of rows in the table
id +1 = 101 is the same as id = 101-1
No it isn't. What if the +1 overflows the id?
I tried this with PostgreSQL 9.0 and it does not use an index unless I create one on (id - 1).
So with the following index definition
create index idx_minus on my_table ( (id - 1) );
PostgreSQL uses an index for the query
select *
from my_table
where id - 1 = 12345
Interesting.
You can add Oracle Release 10.2.0.1.0 to your list (not able to rewrite the query).
create table t(
id
,x
,padding
,primary key (id)
) as
select rownum as id
,'x' as x
,lpad('x', 100, 'x') as padding
from dual
connect by level <= 50000;
Query 1.
select id
from t
where id = 100 + 1;
----------------------------------------+
| Id | Operation | Name |
-----------------------------------------
| 0 | SELECT STATEMENT | |
|* 1 | INDEX UNIQUE SCAN| SYS_C006659 |
-----------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("ID"=101)
Query 2.
select id
from t
where id + 1 = 101;
--------------------------------------------
| Id | Operation | Name |
--------------------------------------------
| 0 | SELECT STATEMENT | |
|* 1 | INDEX FAST FULL SCAN| SYS_C006659 |
--------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("ID"+1=101)
Query 3.
select x
from t
where id + 1 = 101;
------------------------------------------
| Id | Operation | Name | Rows |
------------------------------------------
| 0 | SELECT STATEMENT | | 1 |
|* 1 | TABLE ACCESS FULL| T | 1 |
------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("ID"+1=101)
Why not just do this instead (assuming you don't want the server to do the math for calculating the actual ID you're looking for)?
SELECT * FROM my_table WHERE id = (101 - 1)