oracle not using defined indexes - oracle

As seen below there is a simple join between my Tables A And B.
In addition, there is a condition on each table which is combined with Or operator.
SELECT /*+ NO_EXPAND */
* FROM IIndustrialCaseHistory B ,
IIndustrialCaseProduct A
where (
A.ProductId IN ('9_2') OR
contains(B.KeyWords,'%some text goes here%' ) <=0
)
and ( B.Id = A.IIndustrialCaseHistoryId)
on ProductId defined a b-tree index and for KeyWords there is a function index.
but I dont know why my execution plan dose not use these indexes and performs table access full?
as I found in this URL NO_EXPAND optimization hint could couse using indexes in execution plan(The NO_EXPAND hint prevents the cost-based optimizer from considering OR-expansion for queries having OR conditions or IN-lists in the WHERE clause ). But I didn't see any use of defined indexes
whats is oracle problem with my query?!

Unless there is something magical about the contains() function that I don't know about, Oracle cannot use an index to find a matching value that leads with a wildcard, i.e. a text string value within a varchar2 column but not starting in the first position with that value. [OR B.KeyWords LIKE'%some text goes here%' -- as opposed to -- OR B.KeyWords LIKE'Some text starts here%' -- optimizable via index.] The optimizer will default back to the full table scan in that case.
Also, although it may not be material, why use IN() if there is only one value in the list? Why not A.ProductId = '9_2' ?

Related

ORA-13249: SDO_NN cannot be evaluated without using index

I get an error when I run the following Sql Script in ORACLE
SELECT D.ID
FROM DOOR D
JOIN STREET S ON (S.ID=D.STREET_ID)
WHERE
SDO_NN(D.LOCATION,SDO_UTIL.FROM_WKTGEOMETRY('POINT (11112.0111 321314.2222)'),'sdo_num_res=6') = 'TRUE'
or
S.ID IN (17);
but when I change 'or' to 'and' or delete 'or S.ID IN (17)' I get no error.
SELECT D.ID
FROM DOOR D
JOIN STREET S ON (S.ID=D.STREET_ID)
WHERE
SDO_NN(D.LOCATION,SDO_UTIL.FROM_WKTGEOMETRY('POINT (11112.0111 321314.2222)'),'sdo_num_res=6') = 'TRUE'
and
S.ID IN (17);
Type of Location field in DOOR Table is MDSYS.SDO_GEOMETRY
and
Type of ID field in STREET Table is NUMBER
I want first SQL to work.
Can anyone help with the solution?
Would hint do any good?
SELECT /*+ LEADING(d) USE_NL(d s) INDEX s spatial_index) */
D.ID
FROM DOOR D JOIN STREET S ON (S.ID = D.STREET_ID)
WHERE SDO_NN (
D.LOCATION,
SDO_UTIL.FROM_WKTGEOMETRY ('POINT (11112.0111 321314.2222)'),
'sdo_num_res=6') =
'TRUE'
AND S.ID IN (17);
The problem here is that the SDO_NN operator (as opposed to the SDO_RELATE or SDO_WITHIN_DISTANCE operators) can only be solved via a spatial index.
With the first query, no actual filter is applied to select from the DOOR table, so the query optimizer naturally uses the spatial index.
With the second query, you restrict the DOOR table to only those rows (doors) for a given street (STREET.ID=17). That makes the optimizer choose the index that applies that filter (probably the index on STREET.STREET_ID.
The or vs and makes for a very different query and a very different result. With or, you are asking for the 6 closest doors to the selected point, from any street + all the doors on street 17 irrespective of proximity.
With and you are asking for the 6 closest doors on street 17 only. Which makes much more sense than the first syntax.
In order to make that work in your case, you need to use a hint to tell the optimizer to use the spatial index instead of the index on DOOR.STREET_ID:
SELECT /*+ INDEX (d <spatial index name>) */ D.ID
FROM DOOR D
JOIN STREET S ON (S.ID=D.STREET_ID)
WHERE SDO_NN(D.LOCATION,SDO_UTIL.FROM_WKTGEOMETRY('POINT (11112.0111 321314.2222)'),'sdo_num_res=6') = 'TRUE'
AND S.ID IN (17);
Notice the syntax of the hint: d refers to the alias that refers to the DOOR table, since this is the one you are doing the spatial filtering on. <spatial index name> is the name of the spatial index you have defined on the DOOR table (not the string "spatial_index".
Hints have a "fragile" syntax: if you make any mistake, the hint will just be ignored. Also they are not orders to the optimizer: they are just suggestions. If the optimizer finds a hint impossible to apply (like you ask for an index that does not exist), it will also ignore it.
One way to confirm that hints have been accepted and used by the optimizer is to look at the query plan it has produced. If you use sqlplus or sqlcl, you can use the EXPLAIN PLAN command that will show you the plan without actually executing the statement. If you use SQLDeveloper, that has specific buttons to show query plans.
EDIT: The INDEX hint might not be sufficient. It may be that the optimizer needs to be told also how to implement the join, as #littlefoot has indicated. So you may also need the LEADING and USE_NL hints. But try first with only the INDEX hint.

Oracle no index for function calls in sql query

I have a table with name t(abc varchar2(50),xyz varchar2(50), ..etc) and index enabled on column abc. Oracle uses the index for userfunction(a) which takes long time. This is a dynamic query formed can have another conditions that must use index on abc so I don't want to use no_index hint.
select *from t
where
userfunction(a) = 0
and exists (select 1 from tab where t.abc='' ...etc)
and ..etc
I tried to re-write the query with nested query by moving the function to nested query, but oracle is re-writing and still executing userfunction(a) at the first and the query is taking long time.
select *from (
select *from t
where
and exists (select 1 from tab where t.abc='' ...etc)
..etc
)
userfunction(a) = 0
Also tried using WITH clause but no luck.
Any idea of oracle not to use index for user function calls or certain condition in where clause?
Your query is not logically consistent: you reference table t and tab, but only desribe t. Assuming you meant t when you wrote tab, I can't see why you need the exists clause with a sub-query. EXISTS will just return TRUE when the expression in the subselect evaluates to TRUE; so why not just use that expression in the main query on t?

Avoid duplicate values for certain column in DAX query

I am using the following statement to get a result table:
EVALUATE
(
CALCULATETABLE
(
ADDCOLUMNS (
'Case',
"Casenumber", RELATED( 'CaseDetails'[nr]),
),
'Case'[Date] <= value(#dateto) )
)
However, I want to only get one record pr casenumber. In SQL I would solve this with a GROUP BY statement, but how should I do this in DAX? Case also has a dimkey, so several cases with the same casenumber can have different dimkeys.
Try this:
EVALUATE
CALCULATETABLE(
SUMMARIZE(
Case
,<comma-separated list of fields from Case you want>
,"CaseNumber"
,RELATED(CaseDetails[nr])
)
,Case[Date] <= VALUE(#dateto)
)
SUMMARIZE() takes a table as its first argument, then a comma-separated list of fields from that table and any tables that it is related to where it is on the many side (thus in a star schema, SUMMARIZE()ing the fact table will allow you to refer directly to any dimension table field), followed by a comma-separated list of , pairs where is a quoted field name and is a scalar value which is evaluated in the row context of the table in the first argument.
If you don't need to rename CaseDetails[nr], then the query would look like this (just for an illustrative example):
EVALUATE
CALCULATETABLE(
SUMMARIZE(
Case
,<comma-separated list of fields from Case you want>
,CaseDetails[nr]
)
,Case[Date] <= VALUE(#dateto)
)
In such a query, all fields will come through with column headings in the format of 'table'[field], so there is no ambiguity if you have identical field names in multiple related tables.
Edit to address new information in original:
SUMMARIZE(), just like SQL's GROUP BY clause will not eliminate distinct values from the resultset. If there is a field that is a higher cardinality than the field you want to group by, you will always see duplicates.
Is your [DimKey] necessary in the resultset? If yes, then there's no way to decrease the size of your resultset smaller than the number of distinct values of [DimKey].
If [DimKey] is unnecessary, simply omit it from the list of fields in SUMMARIZE().
If you want only a specific [DimKey], e.g. the most recent (assuming it's an IDENTITY field and the max value is the latest), then you can bring it in with another ADDCOLUMNS() wrapped around your current SUMMARIZE():
EVALUATE
ADDCOLUMNS(
SUMMARIZE(
Case
,<comma-separated list of fields from Case except for [DimKey]>
,"CaseNumber"
,RELATED(CaseDetails[nr])
)
,"MaxDimKey"
,CALCULATE(MAX(Case[DimKey]))
)

Oracle SQL Query Performance, Function based Indexes

I have been trying to fine tune a SQL Query that takes 1.5 Hrs to process approx 4,000 error records. The run time increases along with the number of rows.
I figured out there is one condition in my SQL that is actually causing the issue
AND (DECODE (aia.doc_sequence_value,
NULL, DECODE(aia.voucher_num,
NULL, SUBSTR(aia.invoice_num, 1, 10),
aia.voucher_num) ,
aia.doc_sequence_value) ||'_' ||
aila.line_number ||'_' ||
aida.distribution_line_number ||'_' ||
DECODE (aca.doc_sequence_value,
NULL, DECODE(aca.check_voucher_num,
NULL, SUBSTR(aca.check_number, 1, 10),
aca.check_voucher_num) ,
aca.doc_sequence_value)) = " P_ID"
(P_ID - a value from the first cursor sql)
(Note that these are standard Oracle Applications(ERP) Invoice tables)
P_ID column is from the staging table that is derived the same way as above derivation and compared here again in the second SQL to get the latest data for that record. (Basically reprocessing the error records, the value of P_ID is something like "999703_1_1_9995248" )
Q1) Can I create a function based index on the whole left side derivation? If so what is the syntax.
Q2) Would it be okay or against the oracle standard rules, to create a function based index on standard Oracle tables? (Not creating directly on the table itself)
Q3) If NOT what is the best approach to solve this issue?
Briefly, no you can't place a function-based index on that expression, because the input values are derived from four different tables (or table aliases).
What you might look into is a materialised view, but that's a big and potentially difficult to solve a single query optimisation problem with.
You might investigate decomposing that string "999703_1_1_9995248" and applying the relevant parts to the separate expressions:
DECODE(aia.doc_sequence_value,
NULL,
DECODE(aia.voucher_num,
NULL, SUBSTR(aia.invoice_num, 1, 10),
aia.voucher_num) ,
aia.doc_sequence_value) = '999703' and
aila.line_number = '1' and
aida.distribution_line_number = '1' and
DECODE (aca.doc_sequence_value,
NULL,
DECODE(aca.check_voucher_num,
NULL, SUBSTR(aca.check_number, 1, 10),
aca.check_voucher_num) ,
aca.doc_sequence_value)) = '9995248'
Then you can use indexes on the expressions and columns.
You could separate the four components of the P_ID value using regular expressions, or a combination of InStr() and SubStr()
Ad 1) Based on the SQL you've posted, you cannot create function based index on that. The reason is that function based indexes must be:
Deterministic - i.e. the function used in index definition has to always return the same result for given input arguments, and
Can only use columns from the table the index is created for. In your case - based on aliases you're using - you have four tables (aia, aila, aida, aca).
Req #2 makes it impossible to build a functional index for that expression.

Using spatial locator for Oracle

I am able to run the below query but when i swap the parameter for sdo_nn, I get an error of SDO_NN cannot be evaluated without using index
Works:
SELECT
c.customer_id,
c.first_name,
c.last_name,
sdo_nn_distance (1) distance
FROM stores s,
customers c
AND sdo_nn
(c.cust_geo_location, s.store_geo_location, 'sdo_num_res=1', 1)= 'TRUE'
ORDER BY distance;
Does not work:
SELECT
c.customer_id,
c.first_name,
c.last_name,
sdo_nn_distance (1) distance
FROM stores s,
customers c
AND sdo_nn
(s.store_geo_location,c.cust_geo_location, 'sdo_num_res=1', 1)= 'TRUE'
ORDER BY distance;
Anyone can explain to me why the sequence matter?
From Oracle's online docs sdo_nn needs the first parameter to be spatially indexed. the second parameter does not have that necessity/constraint.
So when swapping parameters you need to make sure the "now first" parameter (i.e. s.store_geo_location) is spatially indexed. See this on how to create a spatial index in Oracle.
Add the compiler hint to tell the query parser what index to use e.g.:
select
/*+ index(tableName,sdoIndexName) */
...
WARNING Compiler hints fail SILENTLY

Resources