Go to content Go to navigation and search

Home

Current Oracle Spatial Blogs

Search

RSS / Atom

Email me

textpattern

Oracle's SQL/MM Compliant Types

· Nov 9, 01:28 pm by Simon Greener

Folks, there has been much made that Oracle Spatial is proprietary and that alternate implementations *had* to be done because the Sdo_Geometry implementation is not SQL/MM compliant.

What many do not know is that, since 10gR1, Oracle has implemented a *wrapper* set of objects over the top of their Sdo_Geometry implementation (limitations in Oracle objects made this more difficult in earlier versions).

To illustrate this I have constructed a very simple sql script which I now include in-line. If you cut it out you will discover that it works. However, you have to install my packages into a schema call CODESYS to make it work as I do use the QuadTree generation code in the TESSELATE package to generate the ST_Polygon data.

My tests all ran fine on XE and 10gR2.

In summary. If you want to use SQL/MM compliant types then you can. There are some idiosyncrasies (standards are notorious for the "wiggle-room" they provide implementors) and some limitations that can and will be fixed but, as someone once famous said: "it just works!" (Aside: Though Oracle could have saved themselves a lot of grief by naming Sdo_Geometry as ST_Geometry 10 years ago.)

set timing on
set linesize 120
set pagesize 1000
drop table my_points;
create table my_points ( 
  point_id     RAW(64) primary key ,
  geometry     mdsys.ST_Point
) PCTUSED 99;

Declare
  Type row_t is table of my_points%ROWTYPE index by PLS_INTEGER;
  v_Rows      row_t;
  v_Row       my_points%ROWTYPE;
  v_pt        MDSYS.SDO_Point_Type := MDSYS.SDO_Point_Type(NULL,NULL,NULL);
  v_lo        MDSYS.SDO_Point_Type := MDSYS.SDO_POINT_TYPE(100000,5200000,NULL);
  v_hi        MDSYS.SDO_Point_Type := MDSYS.SDO_POINT_TYPE(300000,5300000,NULL);
  v_pk        RAW(64);
  v_RowCount  PLS_INTEGER := 1;
  v_Commit    PLS_INTEGER := 2000;
  v_MaxPts    PLS_INTEGER := 200000;
  bulk_errors EXCEPTION;
  PRAGMA      EXCEPTION_INIT (bulk_errors, -24381);
Begin
  << point_generation_loop >>
  For i In 1..v_MaxPts Loop
     v_pt.x := dbms_random.value(v_lo.x,v_hi.x);
     v_pt.y := dbms_random.value(v_lo.y,v_hi.y);
     v_Row.geometry := MDSYS.ST_Point(v_pt.x,v_pt.y);
     v_Row.point_id := codesys.tesselate.Space_Key(v_pt,v_lo,v_hi);
     v_Rows(v_RowCount) := v_Row;
     If ( Mod(v_RowCount,v_Commit) = 0 ) Then
       ForAll i In 1..v_RowCount 
          Save Exceptions
          Insert Into my_points Values v_Rows(i);
       Commit;
       v_rowCount := 0;
     End If;
     v_rowCount := v_rowCount + 1;
  End Loop point_generation_loop;
  Exception
    When BULK_ERRORS Then
       << bulk_errors_loop >>
      For j In 1 .. SQL%BULK_EXCEPTIONS.COUNT Loop
        DBMS_OUTPUT.PUT_LINE ( 'Error from element #' ||
                    TO_CHAR(SQL%BULK_EXCEPTIONS(j).error_index) || ': ' ||
                    SQLERRM(SQL%BULK_EXCEPTIONS(j).error_code));
      End Loop bulk_errors_loop;
End;
/

Prompt Get Extent of data to test the insert ...
SELECT min( a.geometry.st_x() ) as minx,
       max( a.geometry.st_x() ) as maxx,
       min( a.geometry.st_y() ) as miny,
       max( a.geometry.st_y() ) as maxy,
       count(*) as NumRecs
  FROM my_points a;

Prompt Create Oracle Metadata Entry...
DELETE FROM user_sdo_geom_metadata 
WHERE table_name = 'MY_POINTS';
COMMIT;
INSERT INTO user_sdo_geom_metadata (
table_name,
column_name,
diminfo,
srid)
SELECT 'MY_POINTS','GEOMETRY.GEOM', 
MDSYS.SDO_DIM_ARRAY( 
MDSYS.SDO_DIM_ELEMENT('X', minx, maxx, 0.5), MDSYS.SDO_DIM_ELEMENT('Y', miny, maxy, 0.5)), 
NULL
  FROM (
  SELECT TRUNC( min( a.geometry.st_x() ) - 1,0) as minx,
         ROUND( max( a.geometry.st_x() ) + 1,0) as maxx,
         TRUNC( min( a.geometry.st_y() ) - 1,0) as miny,
         ROUND( max( a.geometry.st_y() ) + 1,0) as maxy
    FROM my_points a);

Prompt Create RTree index on point data ...
DROP INDEX my_points_geometry;

CREATE INDEX my_points_geometry ON my_points(geometry.geom) 
INDEXTYPE IS MDSYS.SPATIAL_INDEX
PARAMETERS('sdo_indx_dims=2, layer_gtype=point');

set serveroutput on size 100000
Prompt Now create some polygons by quadtree tesselation of the space my_points occupies
DECLARE
  v_quadid  INTEGER;
  v_diminfo MDSYS.SDO_DIM_ARRAY;
BEGIN
  SELECT diminfo 
    INTO v_diminfo
    FROM user_sdo_geom_metadata 
   WHERE table_name = 'MY_POINTS';
  codesys.Tesselate.SetGeom2SQLMM;
  codesys.Tesselate.Initialise(5000,
                               'MY_POINTS',
                               'GEOMETRY.GEOM',
                               20);
  v_QuadId := codesys.Tesselate.QuadTree( v_diminfo );
END;
/
Prompt Create Primary Key on Quad.Quad_Id data ...
ALTER TABLE quad 
  ADD CONSTRAINT quad_quad_id_pk PRIMARY KEY (quad_id);

Prompt Create RTree index on st_polygon data ...
DROP INDEX quad_geometry;

CREATE INDEX quad_geometry ON quad(geometry.geom) 
INDEXTYPE IS MDSYS.SPATIAL_INDEX
PARAMETERS('sdo_indx_dims=2, layer_gtype=polygon');

Prompt Some ST_Polygon inspectors...
SELECT q.geometry.ST_CoordDim()
  FROM quad q
 WHERE rownum < 2;

SELECT q.geometry.ST_Dimension()
  FROM quad q
 WHERE rownum < 2;

SELECT distinct q.geometry.ST_Area()
  FROM quad q;

SELECT q.geometry.ST_Envelope()
  FROM quad q
 WHERE rownum < 2;

SELECT q.geometry.ST_IsSimple()
  FROM quad q
 WHERE rownum < 2;

SELECT q.geometry.ST_ExteriorRing()
  FROM quad q
 WHERE rownum < 2;

SELECT distinct q.geometry.ST_ExteriorRing().ST_Length()
  FROM quad q;

SELECT q.geometry.ST_Buffer(10)
  FROM quad q
 WHERE rownum < 2;

SELECT q.geometry.ST_NumInteriorRing()
  FROM quad q
 WHERE rownum < 2;

SELECT q.geometry.ST_Centroid()
  FROM quad q
 WHERE rownum < 2;

set serveroutput on size 100000
declare
  v_coord      number;
  v_point      mdsys.st_point;
  v_linestring mdsys.st_linestring;
begin
  SELECT q.geometry.ST_ExteriorRing()
    INTO v_linestring
    FROM quad q
   WHERE rownum < 2;
  For v_i in 1..v_linestring.ST_NumPoints() Loop
    v_point := v_linestring.st_PointN(v_i);
    dbms_output.put_line('x='||v_point.St_X()||' y='||v_point.st_y());
  End Loop;
end;
/

Prompt This is where the problem is... no public methods for executing spatial searches using ST_* methods...
Prompt If I am wrong... email me and I will change this posting.
explain plan

set statement_id = 'SQL/MM QUERY'
for
SELECT count(*)
  FROM quad q,
       my_points p
 WHERE q.quad_id = 43
   AND p.geometry.ST_Within(q.geometry) = 1;

SELECT * 
   FROM TABLE(dbms_xplan.display('PLAN_TABLE','SQL/MM QUERY','TYPICAL'));

Prompt One can get around it mixing Sdo_Geometry searching and ST_* geometric methods ...

set timing on
SELECT count(*)
   FROM quad q,
       my_points p
 WHERE q.quad_id = 43
   AND SDO_Filter(p.geometry.geom,q.geometry.geom) = 'TRUE'
   AND p.geometry.ST_Within(q.geometry) = 1;

Prompt Perhaps in 11g?
Prompt Until then, I am looking at building a custom domain index and methods that use it... perhaps I will get this done in the next 3 months.. I have made a start but...
post this at del.icio.uspost this at Diggpost this at Technoratipost this at Newsvinepost this at Ma.gnoliapost this at Furlpost this at Blinklistpost this at Spurlpost this at Wistspost this at Simpypost this at Redditpost this at Farkpost this at Blogmarkspost this at Yahoo! my webpost this at Mr. Wongpost this at Windows Livepost this at Google Bookmarkspost this to Twitter
  Textile Help