Mittwoch, April 27, 2011

Datentypen in Oracle und SQL Server

Der Titel des Eintrags mag ein wenig in die falsche Richtung weisen. Worum es geht, ist Folgendes: vor kurzem ist mir klar geworden, dass ich (mal wieder?) den Standardfehler der Datenbankentwicklung gemacht habe - nämlich das Verhalten eines RDBMS als das eines anderen RDBMS anzunehmen. Im gegebenen Fall wusste ich, dass Oracle in der Frage numerischer Datentypen nicht sonderlich sensibel ist: intern ist (fast) alles NUMBER und benötigt deshalb auch den gleichen Platz auf der Platte. Dazu ein Beispiel. Zunächst lege ich vier Tabellen an, die sich jeweils nur im Namen und im Datentyp der rn-Spalte unterscheiden:

create table test_ora_number
as
select cast(rownum as number) rn
  from dual
connect by level <= 100000

create table test_ora_number_pc
as
select cast(rownum as number(12,4)) rn
  from dual
connect by level <= 100000

create table test_ora_int
as
select cast(rownum as int) rn
  from dual
connect by level <= 100000;

create table test_ora_binarydouble
as
select cast(rownum as BINARY_DOUBLE) rn
  from dual
connect by level <= 100000

In USER_SEGMENTS sehen alle Fälle gleich aus, aber USER_TABLES liefert (nach Statistikerstellung) unterschiedliche Angaben (die auch den Aussagen von Tom Kytes show_space-Prozedur entsprechen):

select segment_name
     , blocks
     , bytes 
  from user_segments
 where segment_name like 'TEST_ORA%';

SEGMENT_NAME                       BLOCKS      BYTES
------------------------------ ---------- ----------
TEST_ORA_BINARYDOUBLE                 128    2097152
TEST_ORA_INT                          128    2097152
TEST_ORA_NUMBER                       128    2097152
TEST_ORA_NUMBER_PC                    128    2097152

select table_name
     , blocks
     , blocks * 16384 bytes
  from user_tables 
 where table_name like 'TEST_ORA%';

TABLE_NAME                         BLOCKS      BYTES
------------------------------ ---------- ----------
TEST_ORA_BINARYDOUBLE                 103    1687552
TEST_ORA_INT                           83    1359872
TEST_ORA_NUMBER                        83    1359872
TEST_ORA_NUMBER_PC                     83    1359872

Demnach umfassen die Varianten NUMBER, NUMBER mit Angabe von Precision und Scale und INT den gleichen Speicherplatz. Nur der präzisere Datentyp BINARYDOUBLE belegt mehr Platz (was mir neu war - ich aber nachvollziehen kann. Nun zum SQL Server. Ich hatte bis vor kurzem angenommen, dass der Fall hier ähnlich gelagert wäre - aber das trifft nicht zu. Auch dazu ein Beispiel. Zunächst lege ich wieder Testtabellen an - diesmal mit rekursiven CTEs, ein Trick, den ich mir bei Jonathan Lewis ausgeborgt habe:

DECLARE @div INT = 50 ;
DECLARE @mod INT = 2000 ;
DECLARE @limit INT = @div * @mod ;
DECLARE @driver INT = 1000 ;

WITH    
generator
AS 
( 
SELECT 1 AS id
 UNION ALL
SELECT id + 1
  FROM generator
 WHERE id < @driver
)
SELECT cast(id as decimal) id
  INTO test_decimal
  FROM ( SELECT TOP ( @limit )
                @driver * ( g1.id - 1 ) + g2.id id
           FROM generator g1
          CROSS JOIN 
                generator g2
        ) iv
OPTION ( MAXRECURSION 0, FORCE ORDER );

Anschließend ermittele ich die Objektgrößen über sp_spaceused:

EXEC sp_spaceused test_decimal
EXEC sp_spaceused test_int
EXEC sp_spaceused test_bigint

name            rows     reserved    data  index_size  unused
-------------------------------------------------------------
test_decimal  100000      1800 KB 1792 KB        8 KB    0 KB
test_int      100000      1352 KB 1296 KB        8 KB   48 KB
test_bigint   100000      1736 KB 1696 KB        8 KB   32 KB

Demnach gibt es hier einen deutlichen Unterschied zwischen den verwendeten Datentypen: je kleiner der Typ, desto kompakter wird die Tabelle - und es ist wichtig, einen geeigneten Typ zu wählen - in DWH-Zusammenhängen in der Regel den kleinsten geeigneten Typ.

Keine Kommentare:

Kommentar veröffentlichen