Oracle: matching SYS_GUID’s
Answering questions asked on the site.
Habib asks:
I have a table with a
PRIMARY KEYdefined asConsumerID RAW(16) NOT NULL.However, when I issue a query like this:
SELECT * FROM Consumers WHERE ConsumerID = '1DFFC45DEDEB42B8B79B060D544C2ACB', it uses a
TABLE ACCESS FULLand is very slow.How can I improve this query?
Defining a PRIMARY KEY column as RAW(16) almost always means using system generated GUID's returned by SYS_GUID as PRIMARY KEY's.
Let's create a sample table and see what's going on:
CREATE TABLE t_guid ( id RAW(16) NOT NULL, value VARCHAR2(50) NOT NULL ) / ALTER TABLE t_guid ADD CONSTRAINT pk_guid_id PRIMARY KEY (id) / INSERT INTO t_guid SELECT SYS_GUID(), 'Value ' || level FROM dual CONNECT BY level <= 1000000 / COMMIT /
We have a 1,000,000 rows here filled with SYS_GUID's.
If we issue the query like this:
SELECT value FROM t_guid WHERE id = 'FA6BD2195B2D48C48A9D9D067ECDC670'
| VALUE | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Value 1 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 1 row fetched in 0.0001s (1.5624s) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
, we see that it runs for more than 1.5 seconds.
The detailed EXPLAIN PLAN for this statement shows the following:
EXPLAIN PLAN FOR SELECT value FROM t_guid WHERE id = 'FA6BD2195B2D48C48A9D9D067ECDC670' SELECT * FROM TABLE(DBMS_XPLAN.display())
Plan hash value: 1229846465
----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 29 | 1337 (3)| 00:00:17 |
|* 1 | TABLE ACCESS FULL| T_GUID | 1 | 29 | 1337 (3)| 00:00:17 |
----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(RAWTOHEX("ID")='FA6BD2195B2D48C48A9D9D067ECDC670')
We see that the filter uses the following condition: RAWTOHEX("ID")='FA6BD2195B2D48C48A9D9D067ECDC670'
This condition, of course, is not sargable.
When comparing two values of different types, one type should be cast to the other.
A RAW may be cast to a VARCHAR2 by applying function RAWTOHEX, which returns a hexadecimal representation of bytes in the RAW data.
Reverse operation is performed by function HEXTORAW, which takes a string of hexadecimal bytes represenation as an argument and returns a RAW value composed of these bytes.
But this is not possible by any VARCHAR2 value, since only the strings that contain an even number of hexadecimal digits can be supplied as an argument to this function.
That's why Oracle doesn't perform implicit conversion from VARCHAR2 to RAW.
When comparing two values, one of type VARCHAR2, the other one of type RAW, it's the RAW that is cast to VARCHAR2 (which is always possible) and not vice versa.
To make the condition sargable, we should make an explicit cast:
SELECT value
FROM t_guid
WHERE id = HEXTORAW('FA6BD2195B2D48C48A9D9D067ECDC670')
| VALUE | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Value 1 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 1 row fetched in 0.0001s (0.0256s) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
SELECT STATEMENT TABLE ACCESS BY INDEX ROWID, 20090520_guid.T_GUID INDEX UNIQUE SCAN, 20090520_guid.PK_GUID_ID
, which uses an INDEX UNIQUE SCAN and therefore is instant.
Hope that helps.
I'm always glad to answer the questions regarding database queries.
Subscribe in a reader
Thank you! Your post helped me to understand why the explain plans were showing so lousy performance for simple queries.
Sherpam
21 Dec 15 at 23:08