Mittwoch, November 21, 2018

Column Groups und NULL Werte

Bereits vor einigen Wochen hat Jonathan Lewis auf eine Problem hingewiesen, das sich bei Verwendung von Column Groups ergeben kann: wenn der Anteil der NULL-Werte in einer Spalte sehr groß ist, dann ist das für den Optimizer über die NUM_NULLS-Information erkennbar. Da Column Groups intern über eine virtuelle Spalte abgebildet werden, die die Informationen zu den in der Gruppe enthaltenen Spalten zusammenfasst, verliert die NUM_NULLS-Information auf dieser Ebene ihre Aussagekraft, da die Spaltenkombination insgesamt nicht mehr als NULL betrachtet werden kann. Dadurch ergeben sich im Beispiel extreme Fehleinschätzungen bei der Cardinality.

Dienstag, November 06, 2018

Join Cardinality und Histogramme

Jonathan Lewis hat zuletzt eine Artikelserie veröffentlicht, die sich damit beschäftigen, wie sich die Existenz unterschiedlicher Histogramm-Typen auf die Bestimmung der Join-Cardinality auswirkt. Seine Überlegungen basieren dabei auf einem umfangreicheren Artikel von Chinar Aliyev aus dem Jahr 2016. Da der Herr Lewis da natürlich sehr viele Details beleuchtet, verzichte ich weitgehend auf die Zusammenfassung und beschränke mich auf die Erfassung der Links, um die Hoffnung zu erhalten, sie bei Bedarf wiederfinden zu können:
Wahrscheinlich wird die Artikelserie noch umfangreicher - und vielleicht erweitere ich dann auch die vorliegende Liste.

Montag, November 05, 2018

DAX Studio für Microsoft BI

Zwar sind die Zeiten, in denen ich mich mit Microsofts BI-Landschaft intensiver beschäftigt habe, schon lange vorbei, aber sollte ich jemals wieder damit anfangen, dann wäre das DAX Studio sicher ein Werkzeug, das zu berücksichtigen wäre. Vincent Rainardi verweist in seinem zugehörigen Artikel auch auf eine nützliche Liste weiterer BI Tools, die unter https://www.sqlbi.com/tools/ gepflegt wird. Ich erinnere mich noch gut daran, wie ich nach der Abwanderung von Mosha Pasumansky auf eine Weiterentwicklung des ungeheuer nützlichen MDX Studios hoffte, aber leider ist daraus offenbar nie etwas geworden.

Mittwoch, Oktober 31, 2018

Indizierung von Foreign Keys in Oracle und Postgres

Dass nicht indizierte Foreign Keys in Oracle massive Locking-Probleme hervorrufen können, habe ich vermutlich zum ersten Mal vor mehr als 15 Jahren bei Tom Kyte gelesen (ohne dass ich dazu gerade eine passende Textstelle liefern könnte). Franck Pachot hat jetzt darüber geschrieben, wie das Verhalten unter entsprechenden Bedingungen in Postgres aussieht - eine Frage, die eigentlich auf der Hand liegt, der nachzugehen mir bisher aber noch nicht in den Sinn gekommen war. Das Ergebnis lautet: bei Postgres ist die Indizierung der Foreign Keys nicht zur Vermeidung von Locks erforderlich, sondern nur zur Optimierung der Navigation von Parent zu Child. Grundlage dieses Verhaltens ist die Verfügbarkeit von "shared row locks" in Postgres (die es in Oracle nicht gibt). Die Details der Erklärung erzähle ich hier nicht nach - mir genügt die Erinnerung, dass beide RDBMS sich in diesem Punkt unterschiedlich verhalten.

Montag, Oktober 08, 2018

Transport von SQL Patches

Marco Mischke erläutert in seinem Blog, dass "SQL Patches" bei einer Migration mit Hilfe von "data pump" nicht transportiert werden, da sie nicht zum Schema der Applikation gehören. Das Package DBMS_SQLDIAG enthält aber Prozeduren, mit deren Hilfe man die Patches in einer Tabelle ablegen kann, die sich dann ihrerseits mit "data pump" transportieren lässt. Das ist also kein besonderes Hindernis, aber ein Punkt, den man im Rahmen einer Migration im Auge behalten muss.

Donnerstag, Oktober 04, 2018

Performance von lokalen und globalen Indizes partitionierter Tabellen

Richard Foote hat eine neue Artikel-Serie gestartet, in der er sich mit der Performance lokaler und globaler Indizes für partitionierte Tabellen beschäftigt. Da ich auch zu den Leute gehöre, die globale Indizes weitgehend vermeiden, finde ich Argumente, die für die globalen Indizes sprechen, grundsätzlich interessant:
  • “Hidden” Efficiencies of Non-Partitioned Indexes on Partitioned Tables Part I (The Jean Genie): liefert ein sehr einfaches initiales Beispiel: eine Tabelle enthält Daten für acht Jahre und einen Index für die Spalte TOTAL_SALES, die für den Wert 42 insgesamt 10 Datensätze liefert. Bei Verwendung einer nicht partitionierten Tabelle erfordert ein Zugriff auf diese 10 Datensätze 14 consistent gets (4 für den Durchgang durch die Indexstruktur und 10 für die Datensätze) - und die gleichen 14 LIOs sind auch erforderlich, wenn man nur einen Datensatz für ein bestimmtes Jahr auswählt, da die Datumsangabe nicht Teil des Index ist und daher erst bei der Filterung berücksichtigt wird. Für den globalen Index auf einer partitionierten Tabelle ergeben sich die gleichen 14 LIOs für Fall 1, aber für den zweiten Fall sinkt die Zahl der LIOs auf 5, da hier für den Optimizer klar ist, dass nur die Partition des fraglichen Jahres den gewünschten Datensatz enthalten kann - es erfolgt also ein "partition pruning".
  • “Hidden” Efficiencies of Non-Partitioned Indexes on Partitioned Tables Part II (Aladdin Sane): bis zu Oracle 8 bestand die rowid grundsätzlich aus 6 byte und enthielt "file number", "block number" und "row number". Seit der Einführung des Partitioning in Oracle 8 kann ein partitioniertes Objekt in mehreren Tablespaces abgelegt werden, was dazu führte, dass die "file number" nicht mehr eindeutig war. Daher wurde die rowid für globale Indizes um die "data object id" erweitert (und damit auf 10 byte erweitert). Diese zusätzliche Information macht das "partition pruning" möglich, da der Index in jedem einzelnen Schlüssel die dafür erforderliche Information enthält.
  • “Hidden” Efficiencies of Non-Partitioned Indexes on Partitioned Tables Part III” (Ricochet): weist darauf hin, dass ein lokaler Index durch die kürzere rowid (ohne die "data object id" und daher nur 6 byte lang) etwas kleiner ist als ein entsprechender globaler Index. Für einen Zugriff, der sich auf eine Partition beschränkt, ist der lokale Index daher etwas effizienter als der globale Index (4 vs. 5 LIOs). Beim Partitions-übergreifenden Zugriff ist der globale Index dagegen deutlich effizienter, da sich das Lesen auf eine Index-Struktur beschränkt und nicht über mehrere interne Indizes erfolgen muss.
  • “Hidden” Efficiencies of Non-Partitioned Indexes on Partitioned Tables Part IV” (Hallo Spaceboy) : zeigt, dass man das BLEVEL eines globalen Index dadurch reduzieren kann, dass man ihn partitioniert (nach einer anderen Spalte als dem partition key).
Wie üblich gilt, dass ich die folgenden Artikel ergänzen werde, wenn ich nicht die Lust daran verliere.

    Montag, Oktober 01, 2018

    Overhead für Extended Trace im SQL Server

    Da ich ein großer Freund des Tracings von Datenbank-Operationen bin, finde ich Untersuchungen wie die von Nenad Noveljic zum Thema "SQL Server Extended Events Trace Overhead" sehr interessant. Im Artikel wird ein Testszenario vorgestellt, das eine relativ zweckfreie Schleife zunächst ohne und dann mit dem Event "sql_batch_completed" gegenüberstellt. Für diese stark CPU-hungrige Operation ergibt sich eine Verlangsamung um ca. 15% nach der Aktivierung des Events: die Laufzeit steigt von 61 auf 74 Sekunden - wobei die Erhöhung auf CPU-Last zurückzuführen ist, die nicht in den Event-Statistiken enthalten ist (sondern in sys.dm_exec_requests.cpu_time). Andere Events - wie etwa logout - haben nahezu keine Auswirkungen auf die Performance. Ein entsprechender Test mit Oracle und einem 10046er Trace zeigt keine entsprechende Wirkung - wobei allerdings anzumerken wäre, dass dort ein Trace mit rowsource statistics unter Umständen recht massive negative Effekte hervorrufen kann, wie Jonathan Lewis gerade mal wieder in Erinnerung gerufen hat.

    Dienstag, September 18, 2018

    ORA-01723 für DATE-Angaben

    Nach langer Zeit mal wieder etwas Selbsterlebtes, das einen Eintrag zu verdienen scheint - wie man eine CTAS-Operation über das nls_date_format torpedieren kann:

    SQL> alter session set nls_date_format = 'dd.mm.yyyy hh24:mi:ss';
    
    Session altered.
    
    SQL> create table t as select to_date(null) col1 from dual;
    create table t as select to_date(null) col1 from dual
                             *
    ERROR at line 1:
    ORA-01723: zero-length columns are not allowed
    
    SQL> alter session set nls_date_format = 'DD-MON-RR';
    
    SQL> create table t as select to_date(null) col1 from dual;
    
    Table created.
    

    Somit wird die Tabelle T mit dem einen nls_date_format problemlos angelegt, während das andere Format einen Fehler "ORA-01723: zero-length columns are not allowed" hervorruft. Ist das ein Problem? Vermutlich nicht: sinnvoller wäre hier ohnehin der Einsatz eines CAST-Aufrufs, denn die folgende Variante funktioniert in beiden Fällen:

    SQL> create table t as select cast(null as date) col1 from dual;