Home > database >  ORA-13249: SDO_NN cannot be evaluated without using index
ORA-13249: SDO_NN cannot be evaluated without using index

Time:12-08

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?

CodePudding user response:

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);

CodePudding user response:

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.

  • Related