Freitag, 6. September 2013

Mal schnell ein Beispieldatenset mit 1 Mio Punkten erzeugen

In Vorbereitung von Tests mit dem neuen Oracle DB 12c Spatial Feature "Vector Performance Accelerator" wollte ich mir ein ausreichend großes Datenset erzeugen.
Die Punktgeometrien zu generieren und in eine SDO_GEOMETRY-Tabelle zu laden, ist an sich keine schwere Übung. Die Frage ist hauptsächlich, wie lange man für das Schreiben der Daten in die Tabelle braucht.
Der herkömmliche Ansatz über ein PL/SQL-Skript (Skript 1) mit  insert into <table> values ... in einer FOR Loop verlangt bei größeren Punktmengen - zumindest in meiner recht beschränkten virtuellen Testumgebung auf dem Laptop - doch einige Geduld.

set serveroutput on

drop table gps_positions purge;
 

create table gps_positions (
  id number,
  position sdo_geometry,
  constraint gps_position_pk primary key (id) enable);


declare
  l_curr_lon   number;
  l_curr_lat   number;
  i number;
  j number;
  id number;
  l_start_time number;
  l_end_time number;
begin
  l_start_time := dbms_utility.get_time;
 
  id := 1;

  -- 100 x 1000 points
  for i in 1 .. 100 loop
    for j in 1 .. 1000 loop
      l_curr_lat := round(dbms_random.value(-122,-74),10);
      l_curr_lon := round(dbms_random.value(24,49),10);
      insert into gps_positions (id, position) values (id, mdsys.sdo_geometry (2001, 4326, sdo_point_type (l_curr_lat, l_curr_lon, null), null, null));
      id := id + 1;
      -- dbms_output.put_line('GPS Point: ' ||id|| '   Lat: ' || l_curr_lat || '   Lon: ' || l_curr_lon);
      commit;
    end loop;
  end loop;
  commit;
 
  l_end_time := dbms_utility.get_time;
  dbms_output.put_line(to_char((l_end_time-l_start_time)/100));
end;
/

(Skript 1)

Ich bin aber gerade nicht geduldig, zumal wenn ich jetzt schon weiß, dass es durchaus mehr als 100K Punkte für die anstehenden Tests sein dürfen.
Also ist es an der Zeit, ein Bulk Insert mittels FORALL in meinem PL/SQL Skript auszuprobieren.
Und hier ist das veränderte Skript zum Erzeugen der Punkte in meiner GPS Tabelle.

set serveroutput on

drop table gps_positions purge;
 

create table gps_positions (
  id number,
  position sdo_geometry,
  constraint gps_position_pk primary key (id) enable);
 
declare
  type t_gps_positions is table of gps_positions%ROWTYPE;
  l_tab t_gps_positions := t_gps_positions();
 

  -- Sample size
  l_size number    := 100000;

  l_start_time number;
  l_end_time number; 

  l_lastid number;
  l_curr_lon number;
  l_curr_lat number;
  l_geom sdo_geometry;
begin

  -- Fetch last id from gps_positions table
  select nvl(max(id),1) + 1 into l_lastid from gps_positions;

  l_start_time := dbms_utility.get_time;
 
  -- Populate sample as collection
  for i in 1 .. l_size LOOP
 
    l_curr_lat := round(dbms_random.value(-122,-74),10);
    l_curr_lon := round(dbms_random.value(24,49),10);
    l_geom := mdsys.sdo_geometry (2001, 4326, sdo_point_type (l_curr_lat, l_curr_lon, null), null, null);
   
    l_tab.extend;
    l_tab(l_tab.last).id := l_lastid;
    l_tab(l_tab.last).position := l_geom;
   
    l_lastid := l_lastid + 1;
   
  end loop;
 
  -- Ingest table with point geometries
  forall i in l_tab.first .. l_tab.last
    insert /*+ APPEND_VALUES */ into gps_positions values l_tab(i);
   
  commit;
 
  l_end_time := dbms_utility.get_time;
  dbms_output.put_line(to_char((l_end_time-l_start_time)/100));

end;
/

(Skript 2)

Das Ganze dauert nur mehr gute 10 Sekunden, was 17x mal schneller war als beim 1. Ansatz.
Einen zusätzlichen Performanceschub zum reinen FORALL bringt hier übrigens noch der APPEND_VALUES Hint im insert.

Es versteht sich von selbst, dass anschließend noch die SDO-Metadaten registriert und der räumliche Index angelegt werden müssen.

Über die eigentlichen Tests mit dem Vector Performance Accelerator wird es dann in meinem nächsten Blogeintrag gehen.