Dienstag, Februar 26, 2013

Neues vom SQL Developer

Obwohl ich normalerweise mein Vertrauen in sqlplus setze, besitzt der SQL Developer durchaus mein Wohlwollen. Daher hier ein paar aktuelle Links zum Thema:
  • Tanel Poder hat snapper in Version 4.03 für den SQL Developer veröffentlicht. Wenn ich mir die Parametrisierung für snapper endlich mal merken könnte, wäre es mein bevorzugtes Analyse-Tool...
  • (That) Jeff Smith zeigt, wie man im SQL Developer ein definiertes Datenmodell und eine angelegte Datenbank miteinander abgleichen und synchronisieren kann.
  • noch mal Jeff Smith (ja, wieder "That"), diesmal mit einer Einführung zur Navigation und Arbeit mit dem Data Modeler.
  • und noch mal Jeff Smith (dito) mit einer Erklärung zur Verwendung von Displays im Data Modeler - damit kann man parallel mehrere Diagramm-Stile darstellen (statt in der Anzeige den Stil umzustellen).

Montag, Februar 25, 2013

Page Dumps im SQL Server

Bei Brent Ozar gibt's eine nette Einführung zum Thema "Datenspeicherung im SQL Server" - inklusive eines halbstündigen Videos. Hier ein paar erinnerungswürdige Punkte:
  • die kleinste Speichereinheit im SQL Server sind die pages einer Größe von 8K: sie entsprechen also den Blocks in Oracle.
  • mit dem Befehl: DBCC IND kann man sich die pages anzeigen lassen, die zu einer Tabelle gehören.
  • mit DBCC PAGE lässt sich ein Dump mit den Details zu den Inhalten der page erzeugen: das Ergebnis entspricht ziemlich genau einem Block Dump in Oracle.
  • ebenfalls wie im Fall von Oracle kann man die Datendateien einer (geschlossenen) Datenbank per Hex-Editor öffnen und darin die unverschlüsselten Strings lesen und (abhängig von der gewählten Einstellung für Page Verify) sogar ändern (was ein guter Grund für die Verwendung von database encryption sein kann).
  • Datenänderungen werden (wie bei Oracle) im Cache durchgeführt. Die Persistierung erfolgt asynchron (beim nächsten Checkpoint).
  • die Inhalte des transaction log können mit Hilfe der table function fn_dblog angezeigt werden.
Nachdem ich zuletzt eher die Unterschiede zwischen Oracle und dem SQL Server im Bereich der Lesekonsistenz und der Isolation Levels betont habe, gibt's bei den hier angesprochenen Punkten eine recht große Ähnlichkeit der Konzepte - wobei die Speicherung auf Block-Ebene im Fall von Oracle wohl sehr viel komplizierter ist (ITLs + Verweise auf Undo-Segmente), da sie die Grundlage der erwähnten Versioning-Mechanismen ist.

Auflösung von deadlocks

Jonathan Lewis stellt in seinem Blog klar, dass Oracle keine automatische Auflösung von deadlocks durchführt, wie das gelegentlich behauptet wird:
Oracle may detect deadlocks automatically, but it doesn’t resolve them, it simply reports them (by raising error ORA-00060 and rolling back one statement) then leaves the deadlocked sessions stuck until the session that received the report resolves the problem or an external agent resolves the problem.
Ich denke, dass das letztlich in erster Linie eine Frage der Definition von "resolve" ist, aber gerade auch in terminologischen Fragen hat der Herr Lewis in den meisten Fällen sehr gute Argumente auf seiner Seite.

Donnerstag, Februar 21, 2013

Namenslängenbeschränkung für QB_NAME hint

Randolf Geist weist auf eine undokumentierte - und ein wenig willkürlich anmutende - Beschränkung für die Länge von query block Namen hin: ein QB_NAME mit mehr als 20 Zeichen wird ignoriert. In diesem Zusammenhang kommt mir mal wieder der Gedanke, dass es nett wäre, wenn z.B. DBMS_XPLAN eine Rückmeldung zu den übergebenen Hints liefern würde: auf diese Weise könnte man die Fälle, in denen die Befolgung eines Hints nicht möglich ist (weil er die Entscheidung, die ihm der Hint vorgibt, nicht treffen muss), von jenen trennen, bei denen man die Hint-Syntax verpfuscht hat.

Dienstag, Februar 19, 2013

Das Halloween Problem und der SQL Server

Im ausgesprochen lesenswerten SQL Performance Blog hat Paul White dieser Tage einige sehr interessante Artikel zum Halloween Problem und seinen Konsequenzen für den SQL Server geschrieben. Das angesprochene Phänomen ist ein klassisches Datenbank Problem, das seinen Namen erhielt, weil es erstmals an Halloween 1976 bei IBM beobachtet wurde und bei dem ein Update, das die Gehälter aller Angestellten, die weniger als 25000$ verdienten um 10% erhöhen sollte, so lange lief, bis jeder Mitarbeiter mindestens 25000$ erhielt - da das Problem festgestellt wurde, nehme ich allerdings nicht an, dass das Ergebnis eine praktische Konsequenz für die Gehaltsgruppe hatte. Ursache solcher Probleme sind Schleifen, die sich ergeben können, wenn das Update keinen eindeutig definierten Ausgangspunkt hat - also, wenn keine Lesekonsistenz auf Query-Ebene sichergestellt ist, wie sie z.B. durch Oracles Versionierungsstrategie gewährleistet wird. An dieser Stelle sei es mal wieder gesagt: ich denke, dass es eine ganze Reihe sehr guter RDBMS gibt, aber dass Oracle im Bereich von Lesekonsistenz und Transaktionssteuerung immer noch einen deutlichen Vorsprung gegenüber der Konkurrenz hat. Hier aber noch ein paar Anmerkungen zu den Artikeln des Herrn White:

The Halloween Problem – Part 1
Der SQL Standard verlangt drei vollständig getrennte Schritte bei der Durchführung von UPDATE-Operationen:
  1. A read-only search determines the records to be changed and the new column values
  2. Changes are applied to affected records
  3. Database consistency constraints are verified
Im SQL Server erfolgt die Verarbeitung aber aus Performance-Gründen iterativ und row basiert, so dass es Aufgabe des Optimizers ist, einen Plan zu finden, der keine logischen Probleme hervorruft. Gefährlich sind in diesem Zusammenhang in erster Linie Index-Zugriffe, bei denen die Änderung der Werte dazu führen würde, dass ein bereits berücksichtigter Satz mit seinem aktualisierten Wert "weiter hinten" im Index erneut auftaucht. Eine Möglichkeit zur Vermeidung einer Schleife ist der Einbau eines Zwischenschrittes TABLE SPOOL (EAGER SPOOL), der die Ergebnisse des ersten Schrittes temporär persistiert. Eine andere Variante ist die Verwendung des CLI beim Tabellenzugriff, sofern die entsprechenden Werte vom Update nicht betroffen sind.

Auch bei INSERT und DELETE spielt das Halloween-Problem eine Rolle. Bei einem INSERT, das die Zieltabelle auch als Datenbasis referenziert, sorgen wiederum TABLE SPOOL (EAGER SPOOL) Schritte dafür, dass die DML-Operation auf einer soliden Ausgangsbasis aufsetzt. Beim DELETE ist das Problem in vielen Fällen nicht relevant, da es keine Rolle spielt, ob ein Satz in einer Operation mehrfach gelöscht werden soll. Im Fall eines Self Joins in einer korrelierten Subquery kann es aber auch beim DELETE erforderlich sein, die Konsistenz der Basis über EAGER SPOOL sicherzustellen.

Für MERGE-Operationen gelten besondere Regeln und hier kommen spezielle Optimierungen ins Spiel. Ich halte es für wahrscheinlich, dass die komplexen Anforderungen der Lesekonsistenz für manche Instabilitäten der MERGE-Operationen im SQL Server verantwortlich sind. Da die angesprochenen Optimierungen relativ subtil sind, erspare ich mir an dieser Stelle eine umfangreiche Erläuterung.

The Halloween Problem – Part 4
Neben dem Spooling gibt es noch weitere Möglichkeiten, mit denen der Optimizer sicherstellen kann, dass die Ergebnisse der DML-Operationen konsistent bleiben: im Fall eines NL_Joins etwa ist ein EAGER SPOOL-Schritt erforderlich, aber ein HASH JOIN sorgt bereits der Aufbau der BUILT Tabelle für die einheitliche Basis, so dass keine temporäre Materialisierung von Zwischenergebnissen erforderlich ist. Gleiches gilt für den MERGE JOIN, bei dem die Sortierung dafür sorgt, dass keine Inkonsistenzen auftreten. Daraus ergibt sich allerdings, dass für vorsortierte Mengen nochmals eine - redundante - Sortierung durchgeführt wird, um das Halloween Problem zu vermeiden: eine NOSORT-Operation wie bei Oracle ist an dieser Stelle nicht möglich. Für den Optimizer besitzt jeder Operator Halloween Protection (HP) properties: eine required HP property und ein delivered HP property. Diese Eigenschaften werden bei der Plangestaltung berücksichtigt. Man kann das Verhalten des Optimizers über trace flags steuern - flag 8692 etwa erzwingt eine EAGER SPOOL Operation -, so dass sich die Performance-Auswirkungen unterschiedlicher Alternativen überprüfen lassen. Andere Fälle, in denen keine spezielle HP erforderlich ist, sind z.B. das Update mit Konstanten (bei denen keine Schleifen-Effekte auftreten können) - sofern die Konstanten als solche erkennbar sind (mit der Datenbank-Option FORCED PARAMETERIZATION kann man Literale durch Parameter ersetzen lassen: das Verfahren scheint also Oracles cursor_sharing = force zu entsprechen). Ein weitere Fall wäre das Update eines einzigen Satzes (TOP 1), wobei mir auf Anhieb nicht so ganz klar ist, wo man dergleichen ausführen möchte. Für die Isolation Level READ COMMITTED SNAPSHOT und SNAPSHOT ISOLATION (die in etwas Oracles Version von READ COMMITTED entsprechen) wäre HP eigentlich nicht relevant, aber da scheint die Implementierung noch ein paar Lücken zu haben.

Insgesamt macht mir die Serie noch einmal klar, welch dramatische Konsequenzen die unterschiedliche Interpretation von Lesekonsistenzregeln für die unterschiedlichen RDBMS mit sich bringt. Angesichts der großen Übereinstimmungen bei anderen Schlüsselkonzepten neige ich manchmal dazu, die Differenzen zu wenig zu beachten.

Montag, Februar 18, 2013

MERGE im SQL Server

Zu den Dingen, die ich in der Vergangenheit in Hinblick auf den SQL Server häufiger gelobt habe, gehört das extrem flexible und mächtige MERGE-Statement, mit dem man z.B. die Ergänzung einer Slowly Changing Dimension Typ 2 in einer einzigen Operation zusammenfassen kann (was in Oracle mehrere SQL-Schritte erfordert, da es dort unter anderem nicht die Unterscheidung zwischen NOT MATCHED BY SOURCE und NOT MATCHED BY TARGET gibt). Leider scheint diese Komplexität aber auf Kosten der Stabilität zu gehen, wie Aaron Bertrand dieser Tage festgestellt hat: Im vorgestellten Fall führen MERGEs auf indexed views (also jenen Objekten, die Oracle und andere als Materialized Views bezeichnen) unter bestimmten – und alles andere als exotischen – Bedingungen zu einem „wrong result“ bug (einem Fehlerfall, der in relationalen Datenbanken deutlich häufiger vorkommt, als man annehmen sollte). Und offenbar ist das nur einer von relativ vielen Bugs, die beim SQL Server im Zusammenhang mit MERGE auftreten (und die zumindest zum Teil mit den Problemen zu tun haben, die der SQL Server immer noch mit der Sicherstellung der Lesekonsistenz hat) und deshalb kommt der Herr Bertrand zu einem recht unerfreulichen Fazit:
Personally, I think the syntax is great (albeit daunting to learn), but every time an issue comes up, it erodes my confidence in the practicality of replacing existing DML with the new construct.
With that in mind, not to be Chicken Little, but I would not feel comfortable recommending anyone to use MERGE unless they implement extremely comprehensive testing. Some of these issues are also present with standard UPSERT methodologies, but the problems are more obvious there. MERGE, merely through its single-statement nature, makes you want to believe in magic. Maybe someday it will deliver, but right now I know it’s not going to be able to saw a person in half without some serious help.
Offen gesagt halte ich “extremely comprehensive testing“ für ziemlich viel verlangt, wenn man mit Grundfunktionalitäten der Datenbank arbeitet … - aber ich spare mir den Kommentar, das so was (bzw. Ähnliches) mit Oracle nicht passieren könnte (obwohl mir dort eine grundsätzliche Instabilität des MERGE-Befehls nicht bekannt ist).

SPM und adaptive cursor sharing

Beinahe hätte ich einen Artikel im Oracle Optimizer blog unerwähnt gelassen ... Diesmal schreibt Allison Lee (nehme ich jedenfalls an: im Artikel erscheint nur der Vorname) über das Zusammenspiel von adaptive cursor sharing (ACS) und SQL plan management (SPM). Grundsätzlich gilt dabei:
If a child cursor is bind-aware, the decision to share or not is made irrespective of whether the query is controlled by SPM. But once the query and its current bind values are sent to the optimizer for optimization, SPM constrains the optimizer's choice of plans, without regard to whether this query is being optimized due to ACS.
Zur Erläuterung der Aussage dient ein Beispiel mit einer Tabelle mit relativ starken skew Effekten (also einer deutlichen ungleich-Verteilung der Werte), auf die mit drei Queries mit unterschiedlichen Bindewerten zugegriffen wird, wobei der Hint BIND_AWARE dafür sorgt, dass ACS bei der ersten Ausführung wirksam  wird. Im Beispiel werden nur zwei der drei Pläne über die DBMS_SPM-Prozedur LOAD_PLANS_FROM_CURSOR_CACHE geladen. Daraus ergibt sich: " Now if we run the query with the three different bind values, SPM will constrain the optimizer to pick from the two accepted plans in the SQL plan baseline." Mein Eindruck ist, dass die beiden Features nicht unbedingt vollständig miteinander harmonieren: für eine Query, für die ACS wirksam ist, würde ich SPM nicht unbedingt in Betracht ziehen, sofern die Anzahl potentiell relevanter Pläne nicht sehr überschaubar ist.

Donnerstag, Februar 14, 2013

SCD-Typen

Zu meinen schlechten Angewohnheiten gehört es, Datenbankobjekte mit Nummern zu versehen, was man mit viel gutem Willen als Versionierungsversuch deuten mag - mit etwas Glück habe ich mir dann auch noch irgendwo notiert, wo die Unterschiede der Versionen liegen und warum ich sie nicht gleich gelöscht habe. Insofern darf ich mich wahrscheinlich nicht über den Herrn Kimball und seine Nummerierung von SCD-Typen beschweren. Falls ich dabei wieder mal den Überblick verlieren sollte, kann ich jedenfalls die Details im Design Tip #152 Slowly Changing Dimension Types 0, 4, 5, 6 and 7 von Margy Ross auf der Webseite der Kimball Group nachlesen. Dabei verwenden die Typen oberhalb von 3 eine Minidimension für häufig geänderte Werte und/oder kombinieren einfachere SCD-Typen.

Mittwoch, Februar 13, 2013

SQL Plan Management und Upgrades

Jonathan Lewis zeigt in seinem Blog ein Beispiel dafür, dass die Übernahme von outline data - also den Hints, die Oracle beim Speichern einer SQL Plan Baseline sichert - bei einem Versions-Upgrade nicht in jedem Fall das gewünschte Ergebnis bringt, da die Hints über Versionsgrenzen hinweg nicht unbedingt ausreichend präzise sind, um einen gewünschten Plan zu definieren. Im vorgestellten Beispiel führt das Upgrade von 10.2.0.3 auf 11.2.0.3 zu Problemen, da die Weiterentwicklung der NL Optimierungen (nlj_prefetch bzw. nlj_batching) dem CBO neue Entscheidungsmöglichkeiten gibt. Dieser Effekt ist vermutlich nicht auf das gegebene Beispiel beschränkt.

Samstag, Februar 09, 2013

Sichtbarkeit von dynamic sampling

Dom Brooks weist darauf hin, dass man der OTHER_XML-Spalte in V$SQL_PLAN entnehmen kann, ob für eine Query dynamic sampling verwendet wurde, wobei man in 11.1 nur eine ja/nein-Angabe und in 11.2 das sampling level findet. Außerdem liefert er Queries zum Auslesen der XML-Werte. Die OTHER_XML-Spalten diverser dictionary views mit Plan-Bezug enthalten eine ganze Menge wichtiger Informationen (Bindewerte, Outline-Hints etc.), aber mir persönlich wäre es lieber, wenn man dafür eine andere Repräsentation gewählt hätte.

Freitag, Februar 08, 2013

ANSI Join Syntax

Dieser Tage hat Charles Hooper einen Artikel zum Thema ANSI Join Syntax geschrieben, in dem er die in seinem Beispiel auftretenden Transformations-Entscheidungen des CBO genauer untersucht. Ich dachte kurz daran, einen Kommentar zum Thema zu schreiben, sah dann aber, dass so ziemlich alles, was ich anmerken wollte, bereits in einem älteren Artikel beim Herrn Hooper zu finden war, zu dem Tim Hall und William Robertson ein paar Kommentare zur Verteidigung der ANSI-Syntax beigesteuert hatten. Relevanter als die Frage der Lesbarkeit ist aber die lange Bug-Liste, die dort zur ANSI-Join-Syntax aufgeführt ist. Hier noch die Links zu den beiden Artikeln:

Montag, Februar 04, 2013

Bestimmung der compression von Tabellen

Heute wurde im OTN Forum die Frage gestellt: woran kann man erkennen, ob die Inhalte einer Partition komprimiert sind? Ich denke, das ist eine ziemlich interessante Frage, und eine, auf die Jonathan Lewis in einem Kommentar zu einem seiner Artikel zum Thema compression auf der AllThingsOracle-Seite vor kurzem eine kompakte Antwort gegeben hat:
Q: Is there a way to tell if the data is actually compressed?
A: In effect, no. 
You might be able to make a reasonable guess - check the average rows per block (num_rows / blocks), then cross-check with the avg_row_len (which reports the uncompressed row length, even when the rows are compressed). You could also use dbms_rowid to convert rowids to file and block numbers and check the number of rows per block individually - because you might have a table where half the data was compressed and half wasn't. 
In principle there is a method, mentioned originally in the 9.2 manuals I think. Column spare1 of table sys.seg$ has two bits set to show whether or not a segment is compressed and whether it holds any compressed data, so you could query seg$ (joined to various other data dictionary tables) to check. The tests are: bitand(spare1, 2048) = 2048 to check if the segment is defined with compression, and bitand(spare1, 4096) = 4096 to check if any of the data is compressed - unfortunately when I did a quick check of bit 4096 in a recent version of Oracle I found that it got set as soon as bit 2048 got set. Even it it worked, of course, all you could learn from a single bit was that some of the data in the table was compressed, you'd still have no idea about how much was compressed.
Auf Anhieb könnte man meinen, dass der Fall viel einfacher läge, denn schließlich enthält dba_tab_partitions eine Spalte COMPRESSION (und eine weiter Spalte COMPRESS_FOR). Aber darin steht nur der für folgende Operationen gültige Status: zum aktuellen Zustand gibt es keine Aussage. Dazu ein Beispiel:

drop table test_part_compress;

create table test_part_compress ( 
    startdate date
  , col1 number
  , col2 varchar2(100) 
)
partition by range (startdate) (
    partition p1 values less than (to_date('01.01.2013','dd.mm.yyyy'))
  , partition p2 values less than (to_date('01.02.2013','dd.mm.yyyy'))
) tablespace test_ts;

insert into test_part_compress
select to_date('31.12.2012', 'dd.mm.yyyy') + mod(rownum, 2) startdate
     , mod(rownum, 10) col1
     , lpad('*', 100, '*') col2
  from dual
connect by level <= 10000;

exec dbms_stats.gather_table_stats(user, 'test_part_compress')

select partition_name
     , num_rows
     , blocks
     , compression
     , compress_for
  from dba_tab_partitions
 where table_name = 'TEST_PART_COMPRESS';

PARTITION_NAME                   NUM_ROWS     BLOCKS COMPRESS COMPRESS_FOR
------------------------------ ---------- ---------- -------- ------------
P1                                   5000         84 DISABLED
P2                                   5000         84 DISABLED

alter table test_part_compress modify partition p1 compress;

exec dbms_stats.gather_table_stats(user, 'test_part_compress')

select partition_name
     , num_rows
     , blocks
     , compression
     , compress_for
  from dba_tab_partitions
 where table_name = 'TEST_PART_COMPRESS';

PARTITION_NAME                   NUM_ROWS     BLOCKS COMPRESS COMPRESS_FOR
------------------------------ ---------- ---------- -------- ------------
P1                                   5000         84 ENABLED  BASIC
P2                                   5000         84 DISABLED

alter table test_part_compress move partition p1;

exec dbms_stats.gather_table_stats(user, 'test_part_compress')

select partition_name
     , num_rows
     , blocks
     , compression
     , compress_for
  from dba_tab_partitions
 where table_name = 'TEST_PART_COMPRESS';

PARTITION_NAME                   NUM_ROWS     BLOCKS COMPRESS COMPRESS_FOR
------------------------------ ---------- ---------- -------- ------------
P1                                   5000          8 ENABLED  BASIC
P2                                   5000         84 DISABLED

alter table test_part_compress modify partition p1 nocompress;

select partition_name
     , num_rows
     , blocks
     , compression
     , compress_for
  from dba_tab_partitions
 where table_name = 'TEST_PART_COMPRESS';

PARTITION_NAME                   NUM_ROWS     BLOCKS COMPRESS COMPRESS_FOR
------------------------------ ---------- ---------- -------- ------------
P1                                   5000          8 DISABLED
P2                                   5000         84 DISABLED

Die Ergebnisse zeigen, dass die Angabe der COMPRESS oder NOCOMPRESS-Eigenschaft zunächst keine direkte Wirkung auf das vorliegende Segment hat: erst ein folgendes MOVE führt zur physikalischen Reorganisation. Das bedeutet dann auch, dass man im Fall der Partition P1 am Ende des Tests nicht ohne Weiteres bestimmen kann, ob ihr Inhalt komprimiert ist. Zur Bestimmung bleiben also die Vorschläge des Herrn Lewis. Dabei lasse ich die Prüfungen zur Größe außen vor, denn im Test ist der Unterschied zwischen 8 Blocks und 84 Blocks ausreichend deutlich:

select partition_name
     , header_file
     , header_block
  from dba_segments
 where segment_name = 'TEST_PART_COMPRESS';

PARTITION_NAME                 HEADER_FILE HEADER_BLOCK
------------------------------ ----------- ------------
P1                                       5       147272
P2                                       5       147104

select block#
     , blocks
     , bitand(spare1, 2048) compression_defined
     , bitand(spare1, 4096) compressed
  from sys.seg$ t
 where file# = 5
   and BLOCK# in (147104, 147272)

BLOCK#     BLOCKS COMPRESSION_DEFINED COMPRESSED
------ ---------- ------------------- ----------
147104         88                   0          0
147272         16                   0       4096

Das scheint also exakt die gewünschte Information zu liefern. Eine andere Variante zur Bestimmung nennt Randolf Geist in seinem Beitrag zum OTN-Thread: die Verwendung der Funktion dbms_compression.get_compression_type:

select subobject_name
     , data_object_id
  from dba_objects
 where object_name = 'TEST_PART_COMPRESS'
   and subobject_name is not null;

SUBOBJECT_NAME                 DATA_OBJECT_ID
------------------------------ --------------
P1                                     101978
P2                                     101977

select dbms_rowid.rowid_relative_fno(rowid) file_nr
     , dbms_compression.get_compression_type(user, 'TEST_PART_COMPRESS', rowid) compression_type
     , count(*) cnt
  from test_part_compress
 group by dbms_rowid.rowid_relative_fno(rowid)
        , dbms_compression.get_compression_type(user, 'TEST_PART_COMPRESS', rowid);

OBJECT COMPRESSION_TYPE        CNT
------ ---------------- ----------
101978                2       5000
101977                1       5000

Zumindest für den aktuell erreichten Zustand einer komprimierten Partition, deren Status aktuell NOCOMPRESS ist, sind die Tests demnach aussagekräftig: Für P1 wird der COMPRESSION_TYPE = 2 angegeben, der anscheinend für Basic und OLTP compression steht (sagt jedenfalls Kerry Osborne). Der Vollständigkeit halber hier noch ein paar weitere Schritte in die eingeschlagene Richtung:

-- move nach NOCOMPRESS-Definition
alter table test_part_compress move partition p1;
exec dbms_stats.gather_table_stats(user, 'test_part_compress')

PARTITION_NAME                   NUM_ROWS     BLOCKS COMPRESS COMPRESS_FOR
------------------------------ ---------- ---------- -------- ------------
P1                                   5000         74 DISABLED
P2                                   5000         84 DISABLED

-- beide Partitionen liefern jetzt COMPRESSION_TYPE = 1 (keine compression)
OBJECT COMPRESSION_TYPE        CNT
------ ---------------- ----------
101979                1       5000
101977                1       5000

BLOCK#     BLOCKS COMPRESSION_DEFINED COMPRESSED
------ ---------- ------------------- ----------
147096         80                   0          0
147104         88                   0          0

-- erneute COMPRESS-Definition
alter table test_part_compress modify partition p1 compress;
exec dbms_stats.gather_table_stats(user, 'test_part_compress')

PARTITION_NAME                   NUM_ROWS     BLOCKS COMPRESS COMPRESS_FOR
------------------------------ ---------- ---------- -------- ------------
P1                                   5000         74 ENABLED  BASIC
P2                                   5000         84 DISABLED

-- der COMPRESSION_TYPE bleibt 1, was plausibel ist, da die Daten ja tatsächlich nicht komprimiert sind
OBJECT COMPRESSION_TYPE        CNT
------ ---------------- ----------
101979                1       5000
101977                1       5000

-- aber seg$ behauptet, dass die Partition jetzt als COMPRESSed definiert ist und ihre Sätze komprimiert sind,
-- was nicht den Tatsachen entspricht
BLOCK#     BLOCKS COMPRESSION_DEFINED COMPRESSED
------ ---------- ------------------- ----------
147096         80                2048       4096
147104         88                   0          0

Auf Anhieb scheint dbms_compression.get_compression_type im gegebenen Fall also die brauchbarere Information zu liefern - und immerhin gibt es überhaupt Möglichkeiten, die unzureichende Aussage aus dba_tab_partitions zu ergänzen.

Samstag, Februar 02, 2013

Evolution der Execution Plans für skalare Subqueries

Jonathan Lewis hat dieser Tage ein Beispiel für die Weiterentwicklung der Darstellung von Ausführungsplänen für skalare Subqueries in den Version 8.1.7.4 bis 11.2.0.3. Deutlich wird dabei, dass die Plandarstellung immer besser und vollständiger wird, dass es aber auch in aktueller Releases Fälle gibt, in denen der Plan, die tatsächliche Verarbeitung nicht exakt repräsentiert. Wenn ich drüber nachdenke, ist das wahrscheinlich keine große Überraschung.