views:

169

answers:

1

Hi,

I have a self referencing table in Oracle 9i, and a view that gets data from it:

CREATE OR REPLACE VIEW config AS
SELECT c.node_id,
       c.parent_node_id,
       c.config_key,
       c.config_value,
       (SELECT c2.config_key 
          FROM vera.config_tab c2 
         WHERE c2.node_id = c.parent_node_id) AS parent_config_key,
       sys_connect_by_path(config_key, '.') path,
       sys_connect_by_path(config_key, '->') php_notation
  FROM config_tab c
CONNECT BY c.parent_node_id = PRIOR c.node_id
 START WITH c.parent_node_id IS NULL
 ORDER BY LEVEL DESC

The table stores configuration for PHP application. Now I need to use same config in oracle view.

I would like to select some values from the view by path, but unfortunately this takes 0,15s so it's unacceptable cost.

SELECT * FROM some_table
 WHERE some_column IN (
   SELECT config_value FROM config_tab WHERE path = 'a.path.to.config'
 )

At first I thought of a function index on sys_connect_by_path, but it is impossible, as it needs also CONNECT BY clause.

Any suggestions how can I emulate an index on the path column from the 'config' view?

+2  A: 

Hi SWilk,

If your data doesn't change frequently in the config_tab, you could use a materialized view with the same query as your view. You could then index the path column of your materialized view.

CREATE MATERIALIZED VIEW config
   REFRESH COMPLETE ON DEMAND 
   AS <your_query>;

CREATE INDEX ix_config_path ON config (path);

Since this is a complex query, you would need to do a full refresh of your materialized view every time the base table is updated so that the data in the MV doesn't become stale.

Update

  • Your column path will be defined as a VARCHAR2(4000). You could limit the size of this column in order to index it. In your query, replace sys_connect_by_path(...) by SUBSTR(sys_connect_by_path(..., 1, 1000) for example.
  • You won't be able to use REFRESH ON COMMIT on a complex MV. A simple trigger won't work. You will have to modify the code that updates your base table to include a refresh somehow, I don't know if this is practical in your environment.
  • You could also use a trigger that submits a job that will refresh the MV. The job will execute once you commit (this is a feature of dbms_job). This is more complex since you will have to check that you only trigger the job once per transaction (using a package variable for example). Again, this is only practical if you don't update the base table frequently.
Vincent Malgrat
Thanks, This seems good idea. I tried it, but I have two problems:1. I can't create index on "path" column:ORA-01450: maximum key length (3118) exceeded. Seems that the column is to large to be indexed... Any ideas how to get this around?2. I can't refresh the mv in a trigger after insert or update or delete, cause a transaction is in progress (I am editing config from pl/sql developer). Is there a way to refresh the mv after commit? the ON COMMIT clause did not work (ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view)
SWilk
@SWilk: I updated my answer, hope this helps.
Vincent Malgrat
Thank you. The config is rarely changed, so your solution fits very well, but I need to ensure that nobody forgets to refresh mv. I think I will finally implement a web interface for the config changes, but I will look at the jobs subject, it might require less work. Thank you again :)
SWilk