tag:blogger.com,1999:blog-171437622024-02-28T13:35:57.203+01:00MP Oracle BlogNotizen zu DatenbankthemenMartin Preisshttp://www.blogger.com/profile/06388592214305009761noreply@blogger.comBlogger1104125tag:blogger.com,1999:blog-17143762.post-25486245504105185622023-04-16T11:35:00.003+02:002023-04-16T11:35:32.621+02:00if (not) exists für DDL in Oracle 23c<p style="text-align: justify;">Noch ein nützliches Detail, das es in Oracle 23c geschafft hat - und das bei anderen RDBMS schon länger im Spiel war: die Möglichkeit, Fehler in SQL Skripten zu vermeiden, die sich aus der Existenz oder Nicht-Existenz von Objekten ergeben:</p><p></p><ul style="text-align: left;"><li style="text-align: justify;">create ... if not exists</li><li style="text-align: justify;">drop ... if exists</li></ul><div style="text-align: justify;">Bisher benötigte man in solchen Fällen eine relativ aufwändige Prüfung, die man sich nun sparen kann. Natürlich birgt das auch ein gewisses Risiko, da man damit natürlich wunderbar produktive Objekte beseitigen kann, aber bei DDL sollte man ohnehin immer vorsichtig sein. Eine detaillierte Erläuterung liefert - natürlich - Tim Hall: <a href="https://oracle-base.com/articles/23c/if-not-exists-ddl-clause-23c">https://oracle-base.com/articles/23c/if-not-exists-ddl-clause-23c</a>.</div><p></p>Martin Preisshttp://www.blogger.com/profile/06388592214305009761noreply@blogger.com0tag:blogger.com,1999:blog-17143762.post-996082816293566672023-04-12T17:04:00.007+02:002023-04-12T17:07:16.748+02:00Group By Erweiterung in Oracle 23c<p style="text-align: justify;">Nein, ich schreibe hier nichts mehr, aber eben habe ich eine interne SQL Schulung gehalten und darin behauptet, dass Oracle eine Positionsangabe im group by nicht unterstützt:</p><p></p><blockquote>cdb$root@SYS:some-system > select username, count(*) from v$session group by 1;<br />select username, count(*) from v$session group by 1<br /> *<br />FEHLER in Zeile 1:<br />ORA-00979: Kein GROUP BY-Ausdruck</blockquote><p></p><p style="text-align: justify;">Mit Oracle 23c ändert sich das offenbar, wie Dani Schnider in <a href="https://danischnider.wordpress.com/2023/04/07/group-by-extensions-in-oracle-23c/">https://danischnider.wordpress.com/2023/04/07/group-by-extensions-in-oracle-23c/</a> erläutert. Natürlich spendiert Oracle dafür auch noch einen Parameter group_by_position_enabled, den man auf true setzen muss, wenn man das Feature auch noch nutzen möchte.<br /></p>Martin Preisshttp://www.blogger.com/profile/06388592214305009761noreply@blogger.com0tag:blogger.com,1999:blog-17143762.post-66179329569206745282022-05-27T16:34:00.005+02:002022-12-02T16:35:46.044+01:00Postgres Links: selten genutzte Features und Tipps zur Massendatenbehandlung<p style="text-align: justify;">Nur, damit ich die Artikel - vielleicht - wieder finde: </p><p style="text-align: justify;">Der Artikel "Lesser Known PostgreSQL Features" von <a href="https://hakibenita.com/postgresql-unknown-features">Haki Benita</a> stellt - nun ja: ein paar weniger bekannter Postgres Features vor... An mir vorbei gegangen war unter anderem gen_random_uuid, womit man seit Version 13 uuid-s erzeugen kann, ohne die uuid.ossp extension installieren zu müssen.</p><p style="text-align: justify;">Auf interessant ist "Common DB schema change mistakes" von <a href="https://postgres.ai/blog/20220525-common-db-schema-change-mistakes">Nikolay Samokhvalov</a>, der erläutert, was man bei Massendatenoperationen besser vermeiden sollte. Hier finde ich den Hinweis auf "drop index concurrently" interessant: das hatte ich nicht mehr in Erinnerung.<br /></p>Martin Preisshttp://www.blogger.com/profile/06388592214305009761noreply@blogger.com0tag:blogger.com,1999:blog-17143762.post-87055716773317641922022-02-25T16:43:00.003+01:002022-02-25T16:43:46.250+01:00Oracle Transformationen<p style="text-align: justify;"> Jonathan Lewis hat bei Redgate eine Serie zu Transformationen des Oracle Optimizers gestartet:</p><ul style="text-align: left;"><li style="text-align: justify;"><a href="https://www.red-gate.com/simple-talk/databases/oracle-databases/transformations-by-the-oracle-optimizer/">Transformations by the Oracle Optimizer</a>: erläutert das unnesting von IN-Listen, die zu inline Views umgewandelt werden, die dann in einen Join einbezogen werden können. Außerdem werden die query blocks angesprochen, die in den Ausführungsplänen erscheinen und zur Hint-Definition eingesetzt werden können.<br /></li><li style="text-align: justify;"><a href="https://www.red-gate.com/simple-talk/databases/oracle-databases/the-effects-of-null-with-not-in-on-oracle-transformations/">The effects of NULL with NOT IN on Oracle transformations</a>: erklärt (noch mal), warum es nicht immer möglich ist, NOT IN zu NOT EXISTS umzuwandeln, und inwieweit NOT NULL Constraints das ändern können. Zur Analyse des Verhaltens können die Suffixe "NA" (null-aware/accepting) und "SNA" (single null-aware/accepting) bei den Joins im Ausführungsplan herangezogen werden.</li></ul><p>Früher habe ich an dieser Stelle gerne behauptet, dass ich folgende Artikel später ergänzen würde - aber da das schon optimistisch war, als ich noch mehr Zeit hatte, spare ich mir diese Absichtserklärung jetzt. <br /></p>Martin Preisshttp://www.blogger.com/profile/06388592214305009761noreply@blogger.com0tag:blogger.com,1999:blog-17143762.post-20207131479926800092021-12-30T15:40:00.004+01:002021-12-30T15:40:55.331+01:00Rekursive CTEs<p style="text-align: justify;"> Ja, mir ist schon klar, dass 2021 nicht das beste Jahr für diesen Blog war - wofür der Grund der übliche war: jede Menge Arbeit. Und, ich nehme es vorweg, 2022 wird nicht viel besser. Aber völlig einschlafen soll er auch nicht, so dass ich vor dem Jahresende schnell noch <a href="https://dev.to/yugabyte/learn-how-to-write-sql-recursive-cte-in-5-steps-3n88">Franck Pachot</a> mit seinem Artikel "Learn how to write SQL recursive CTE in 5 steps" erwähne, in dem er hübsch kompakt erklärt, wie man sich eine rekursive CTE aufbaut und wozu die Einzelteile gut sind. Auch als Apostel des yubabytedb Kults bleibt der Herr Pachot extrem lesenswert.<br /></p>Martin Preisshttp://www.blogger.com/profile/06388592214305009761noreply@blogger.com0tag:blogger.com,1999:blog-17143762.post-34546570611711667572020-11-26T09:52:00.003+01:002020-11-26T09:52:22.737+01:00Oracle Performance-Gründe gegen "select *"<p style="text-align: justify;">Nichts Neues ist, dass der Zugriff auf Tabellen ohne eine explizite Angabe der relevanten Spalten - also mit: select * from table - aus sehr vielen Gründen in der Regel keine gute Idee ist. <a href="https://tanelpoder.com/posts/reasons-why-select-star-is-bad-for-sql-performance/">Tanel Poder</a> hat jetzt eine schöne Liste mit Punkten erstellt, die im Oracle-Kontext die Performance-Aspekte des Themas betreffen. Einige dieser Punkte sind auch für andere RDBMS in gleicher oder ähnlicher Weise relevant.</p>Martin Preisshttp://www.blogger.com/profile/06388592214305009761noreply@blogger.com0tag:blogger.com,1999:blog-17143762.post-42338777241909010702020-10-23T13:46:00.004+02:002020-10-23T13:46:42.836+02:00Postgres Performance seit Version 8.3<p style="text-align: justify;">Ja, es ist eine Weile her, seit ich hier zuletzt etwas geschrieben habe. Und so richtig viel Neues kommt auch jetzt nicht. Nur der Link auf eine interessante Untersuchung von <a href="https://www.2ndquadrant.com/en/blog/tpc-h-performance-since-postgresql-8-3/">Tomas Vondra</a> von 2ndQuadrant, der die Performance von Postgres beim TPC-H Benchmark für die Versionen 8.3 bis 13 untersucht. Die Ergebnisse sind in vielen Details sehr interessant und zeigen unter anderem die Rolle, die die Parallelisierung bei den neueren Releases spielt. Aus den Graphen würde ich ableiten, dass die letzten Releases seit Version 10 keine extrem spektakulären Performance-Verbesserungen mehr gebracht haben, aber durchaus eine positive Entwicklung. Das mag aber auch an der Skalierung liegen, da die Verbesserungen davor sehr deutlich sichtbar waren.</p>Martin Preisshttp://www.blogger.com/profile/06388592214305009761noreply@blogger.com0tag:blogger.com,1999:blog-17143762.post-21181646728478393432020-07-09T08:55:00.002+02:002020-07-09T08:55:52.508+02:00Join-Performance in relationalen Datenbanken<div style="text-align: justify;">
Damit ich den Artikel in Zukunft wiederfinde, um darauf verweisen zu können: <a href="https://blog.dbi-services.com/the-myth-of-nosql-vs-rdbms-joins-dont-scale/">Franck Pachot</a> beschäftigt sich in seinem jüngsten Artikel mit dem Mythos der langsamen Joins in relationalen Datenbanken. Die dahinter stehende Vorstellung, die in den Kreisen der reinen NoSQL Enthusiasten häufig geäußert wird, ist, dass Join-Operationen in relationalen Datenbanken langsam sein müssen, da hier große Datenmengen verknüpft werden müssen, was zu einer hohen Komplexität, CPU-Last etc. führen muss, die sich abhängig von der Größe der Tabellen erhöhen.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Franck Pachot erklärt, dass diese Annahme übersieht, dass alle relationalen Datenbanken über B*Tree Indizes verfügen und die den key-values Stores vergleichbaren kleineren Suchoperationen in aller Regel mit Nested Loop Joins abbilden, wodurch die Größe der Objekte keine Rolle spielt. Er zeigt auch - am Beispiel von Postgres -, dass der Optimizer des RDBMS hier nicht als Black Box agiert (auch das ein üblicher Einwand gegen die relationalen Datenbanken), sondern Pläne über Explain Kommandos darstellen kann.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Sehr schön am Artikel ist auch, dass er sehr sachlich bleibt - und Alex DeBrie, der Autor des Artikels aus der NoSQL-Gemeinde, auf den Franck Pachot sich bezieht (und in dem sich der Abschnitt "Why relational databases don't scale" findet) in Twitter den schönen Satz "really great post by Franck Pachot, and I'm grateful for the conversation" schreiben kann. Wobei ich insgesamt den Eindruck habe, dass heute solche technischen Auseinandersetzungen in vielen Fällen etwas zivilisierter ausgetragen werden, als das in früheren Zeiten (sagen wir: vor 10, 15 Jahren) der Fall war.</div>
Martin Preisshttp://www.blogger.com/profile/06388592214305009761noreply@blogger.com0tag:blogger.com,1999:blog-17143762.post-33518850705501973762020-05-15T14:35:00.003+02:002020-05-15T14:35:39.667+02:00Plan Prädikate in AWR mit Oracle 20<div style="text-align: justify;">
Nur damit der Blog nicht völlig einschläft: <a href="https://blog.dbi-services.com/20c-awr-now-stores-explain-plan-predicates/">Franck Pachot</a> weist in seinem Blog darauf hin, dass Oracle 20 nun endlich die "predicate information" in AWR abspeichert, wie das seit Generationen gewünscht worden war - insbesondere vom Herrn Pachot, der dazu im Jahr des Herrn 2014 eine Idee im <a href="https://community.oracle.com/ideas/2494">Oracle Developer Forum</a> untergebracht hatte; wie ich sehe, sind die von mir dort vorgeschlagenen Verbesserungen alle noch nicht umgesetzt... Leider komme ich heute kaum noch ins Forum und schon gar nicht, um dort etwas zu schreiben. Oder auch: Tempora mutantur et nos mutamur in illis.</div>
Martin Preisshttp://www.blogger.com/profile/06388592214305009761noreply@blogger.com2tag:blogger.com,1999:blog-17143762.post-9349541410353761032020-04-06T08:58:00.002+02:002020-04-06T08:58:52.619+02:00dense_rank zur Bestimmung von Maximalwerten pro Gruppe<div style="text-align: justify;">
Wie man nicht nur am Alter des letzten Eintrags, sondern auch am Titel des vorliegenden erkennen kann, komme ich derzeit selten dazu, mir Gedanken über technische Fragen und ihre schriftliche Zusammenfassung zu machen: und da werden die Titel dann offenbar sperriger...</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<a href="https://jonathanlewis.wordpress.com/2020/03/12/dense_rank/">Jonathan Lewis</a> behandelt in seinem Blog eine Einsatzmöglichkeit der analytischen dense_rank Funktion, der mir gelegentlich auch schon geholfen hätte. Die Fragestellung ist dabei die der jüngsten Bestellung, die in einer Transaktionstabelle für jeden einzelnen Kunden vorliegt. Dazu werden diverse Varianten mit korrelierten und nicht korrelierten Subqueries überprüft, die jeweils das Problem mit sich bringen, dass die Transaktionstabelle zweimal gelesen werden muss. Vermeidbar ist das mit analytischen Funktionen, wobei zunächst eine inline-View mit einem analytischen MAX verwendet wird, bei dem dann in der rahmenden Query eine Filterung auf diese maximalen Werte erfolgt - und das wäre wahrscheinlich auch die Lösung gewesen, die mir in diesem Fall eingefallen wäre. Noch effizienter ist hier aber ein "max (...) keep (dense_rank last order by ...)". Voraussetzung ist dabei, dass es möglich ist, einen unique constraint über die Gruppierungsspalte und die order by Spalte zu legen, da es sonst zu falschen Ergebnissen kommen kann. Grundsätzlich kann die Strategie aber sehr nützlich sein, wenn die Zahl der Datensätze in der Transaktionstabelle im Verhältnis zur Anzahl der (distinkten) Werte in der gruppierten Ergebnismenge der Query hoch ist.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Wenn ich das noch mal überfliege, wird mir klar, dass nicht nur die Titel unter der selteneren Übung beim Zusammenfassen leiden...</div>
Martin Preisshttp://www.blogger.com/profile/06388592214305009761noreply@blogger.com0tag:blogger.com,1999:blog-17143762.post-5996460313714702352020-02-10T08:47:00.000+01:002020-02-10T08:47:59.664+01:00MVCC Verhalten bei Oracle, Postgres und dem SQL Server<div style="text-align: justify;">
<a href="https://blog.dbi-services.com/how-sql-server-mvcc-compares-to-oracle-and-postgresql/">Franck Pachot</a> zeigt in einem aktuellen Artikel, dass der SQL Server in Sachen MVCC immer noch deutlich anders agiert als Oracle und Postgres. Zur Erinnerung: Microsofts Interpretation des Isolation Levels "read committed" hat in der Vergangenheit immer wieder zu Diskussionen geführt, da hier nicht wie bei Postgres und Oracle ein für den Startzeitpunkt der Query konsistenter Zustand gelesen wird, sondern zum Zeitpunkt des Zugriffs abgerufen wird, was per commit festgeschrieben wurde. Dadurch kann eine lang laufende Query inkonsistente Ergebnisse liefern. Es gibt zwar immer wieder Interpreten, die das für eine sehr plausible Implementierungs-Variante halten, aber zu diesen gehöre ich nicht. Noch problematischer ist wahrscheinlich, dass sich trotzdem im SQL Server mit diesem Isolation Level immer wieder Situationen ergaben, in denen writer durch reader und reader durch writer blockiert wurden, was dazu führte, dass manche SQL Server Applikation auf das Isolation Level "read uncommitted" (aka dirty read) umstellte, was zwar Sperren vermied, aber die Frage der Konsistenz der Daten noch weiter in den Hintergrund drängte. Aus diesem Grund hat Microsoft schließlich das Isolation Level "read committed snapshot" eingeführt, das die Bereitstellung lesekonsistenter Ergebnisse sicherstellt (und aus meiner Sicht stärker der Postgres-Implementierung von MVCC ähnelt als der von Oracle, da hier explizit historische Daten vorgehalten werden, so lange sie von Transaktionen benötigt werden).</div>
<br />
<div style="text-align: justify;">
Frank Pachot zeigt nun in seinem Artikel, dass selbst der Isolation Level "read committed snapshot" stärkere Locking-Effekte hervorrufen kann als die MVCC-Implementierungen von Oracle und Postgres. Vermeiden kann man diese Effekte nur durch ergänzende Indizes (und unter Umständen auch Hints). Die Ursache dafür fasst der Herr Pachot so schön zusammen, dass ich sie hier einfach zitiere:</div>
<blockquote class="tr_bq" style="text-align: justify;">
A DML statement has two phases: one to find the rows and the second one to modify them. A DELETE or UPDATE in Oracle and Postgres runs the first in snapshot mode: non-blocking MVCC. The second must, of course, modify the current version. This is a very complex mechanism because it may require a retry (restart) when the current version does not match the consistent snapshot that was used for filtering. Both PostgreSQL and Oracle can ensure this write consistency without the need to block the reads. SQL Server has implemented MVCC more recently and provides non-blocking reads only for the SELECT reads. But a read can still be in blocking situation for the query phase of an update statement.</blockquote>
<div style="text-align: justify;">
Mir zumindest war bisher nicht aufgefallen, dass <span style="text-align: justify;">"read committed snapshot"</span><span style="text-align: justify;"> hier weiterhin ein anderes Verhalten zeigt, als "read committed" in Postgres und Oracle.</span></div>
Martin Preisshttp://www.blogger.com/profile/06388592214305009761noreply@blogger.com0tag:blogger.com,1999:blog-17143762.post-58184431474858556072020-01-17T15:06:00.000+01:002020-01-17T15:06:12.957+01:00Falsche Ergebnisse bei Group By Elimination in Oracle 12.2<div style="text-align: justify;">
<a href="https://jonathanlewis.wordpress.com/2020/01/17/group-by-elimination/">Jonathan Lewis</a> weist in seinem Blog auf einen Fehler bei der in Oracle 12c eingeführten Group By Elimination hin. Diese dient eigentlich dazu, überflüssige Sortierungen zu vermeiden, wenn ein eindeutiger Schlüssel sicherstellt, dass die Gruppierung nicht erforderlich ist. Leider existiert in 12.2 ein Bug, der dazu führt, dass etwa für ein to_char(business_date, 'YYYY') die Group By Elimination wirksam wird, da die Spalte "business_date" der PK der Tabelle ist. Vom Ergebnis her wäre hier aber eine Gruppierung erforderlich, da die to_char Funktion Duplikate liefert. Letztlich liefert die Operation somit falsche Ergbenisse. Für to_char ist der Bug in Oracle 18 behoben, aber die Funktion extract liefert weiterhin falsche Ergebnisse.</div>
Martin Preisshttp://www.blogger.com/profile/06388592214305009761noreply@blogger.com0tag:blogger.com,1999:blog-17143762.post-31350966591036610092019-12-20T14:48:00.001+01:002019-12-20T14:48:41.846+01:00Caching von PL/SQL Funktionsaufrufen<div style="text-align: justify;">
<a href="https://hourim.wordpress.com/2019/12/18/scalar-subquery-caching-the-select-from-dual-trick/">Mohamed Houri</a> zeigt in seinem Blog einen nützlichen Trick: er stellt den Fall einer Query vor, in der ein Wert mit dem Ergebnis eines Funktionsaufrufs verglichen wird:</div>
<blockquote class="tr_bq">
<code class="sql plain" style="background-attachment: initial !important; background-clip: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial !important; background-repeat: initial !important; background-size: initial !important; border-radius: 0px !important; border: 0px !important; bottom: auto !important; box-shadow: none !important; box-sizing: content-box !important; direction: ltr !important; display: inline !important; float: none !important; font-family: Monaco, Consolas, "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important; font-size: 12.8px; height: auto !important; left: auto !important; line-height: 1.1em !important; margin: 0px !important; outline: 0px !important; overflow: visible !important; padding: 0px !important; position: static !important; right: auto !important; top: auto !important; vertical-align: baseline !important; white-space: pre; width: auto !important;">a.xy_bat_id = f_get_id(</code><code class="sql string" style="background-attachment: initial !important; background-clip: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial !important; background-repeat: initial !important; background-size: initial !important; border-radius: 0px !important; border: 0px !important; bottom: auto !important; box-shadow: none !important; box-sizing: content-box !important; color: blue !important; direction: ltr !important; display: inline !important; float: none !important; font-family: Monaco, Consolas, "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important; font-size: 12.8px; height: auto !important; left: auto !important; line-height: 1.1em !important; margin: 0px !important; outline: 0px !important; overflow: visible !important; padding: 0px !important; position: static !important; right: auto !important; top: auto !important; vertical-align: baseline !important; white-space: pre; width: auto !important;">'BJOBD176'</code><code class="sql plain" style="background-attachment: initial !important; background-clip: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial !important; background-repeat: initial !important; background-size: initial !important; border-radius: 0px !important; border: 0px !important; bottom: auto !important; box-shadow: none !important; box-sizing: content-box !important; direction: ltr !important; display: inline !important; float: none !important; font-family: Monaco, Consolas, "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important; font-size: 12.8px; height: auto !important; left: auto !important; line-height: 1.1em !important; margin: 0px !important; outline: 0px !important; overflow: visible !important; padding: 0px !important; position: static !important; right: auto !important; top: auto !important; vertical-align: baseline !important; white-space: pre; width: auto !important;">)</code></blockquote>
<div style="text-align: justify;">
Dieser Zugriff ruft beim Abrufen von 18605 Datensätzen aus der zugehörigen Tabelle 18605 recursive calls hervor - und darüber hinaus sehr viele consistent gets. Eine Untersuchung mit SQL Trace zeigt, dass fast die gesamte Laufzeit auf diesen wiederholt ausgeführten Funktionsaufruf entfällt. Offenbar wird hier für jeden Datensatz rekursiv die vollständige Query ausgeführt, die in der Funktion gekapselt ist. Um das Verhalten zu ändern, genügt es, den Aufruf in ein "select from dual" zu integrieren, also:</div>
<blockquote class="tr_bq">
<code class="sql plain" style="background-attachment: initial !important; background-clip: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial !important; background-repeat: initial !important; background-size: initial !important; border-radius: 0px !important; border: 0px !important; bottom: auto !important; box-shadow: none !important; box-sizing: content-box !important; direction: ltr !important; display: inline !important; float: none !important; font-family: Monaco, Consolas, "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important; font-size: 12.8px; height: auto !important; left: auto !important; line-height: 1.1em !important; margin: 0px !important; outline: 0px !important; overflow: visible !important; padding: 0px !important; position: static !important; right: auto !important; top: auto !important; vertical-align: baseline !important; white-space: pre; width: auto !important;">a.xy_bat_id = (</code><code class="sql keyword" style="background-attachment: initial !important; background-clip: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial !important; background-repeat: initial !important; background-size: initial !important; border-radius: 0px !important; border: 0px !important; bottom: auto !important; box-shadow: none !important; box-sizing: content-box !important; color: rgb(0, 102, 153) !important; direction: ltr !important; display: inline !important; float: none !important; font-family: Monaco, Consolas, "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important; font-size: 12.8px; font-weight: bold !important; height: auto !important; left: auto !important; line-height: 1.1em !important; margin: 0px !important; outline: 0px !important; overflow: visible !important; padding: 0px !important; position: static !important; right: auto !important; top: auto !important; vertical-align: baseline !important; white-space: pre; width: auto !important;">select</code><span style="background-color: white; font-family: Monaco, Consolas, "Bitstream Vera Sans Mono", "Courier New", Courier, monospace; font-size: 12.8px; white-space: pre;"> </span><code class="sql plain" style="background-attachment: initial !important; background-clip: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial !important; background-repeat: initial !important; background-size: initial !important; border-radius: 0px !important; border: 0px !important; bottom: auto !important; box-shadow: none !important; box-sizing: content-box !important; direction: ltr !important; display: inline !important; float: none !important; font-family: Monaco, Consolas, "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important; font-size: 12.8px; height: auto !important; left: auto !important; line-height: 1.1em !important; margin: 0px !important; outline: 0px !important; overflow: visible !important; padding: 0px !important; position: static !important; right: auto !important; top: auto !important; vertical-align: baseline !important; white-space: pre; width: auto !important;">f_get_id(</code><code class="sql string" style="background-attachment: initial !important; background-clip: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial !important; background-repeat: initial !important; background-size: initial !important; border-radius: 0px !important; border: 0px !important; bottom: auto !important; box-shadow: none !important; box-sizing: content-box !important; color: blue !important; direction: ltr !important; display: inline !important; float: none !important; font-family: Monaco, Consolas, "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important; font-size: 12.8px; height: auto !important; left: auto !important; line-height: 1.1em !important; margin: 0px !important; outline: 0px !important; overflow: visible !important; padding: 0px !important; position: static !important; right: auto !important; top: auto !important; vertical-align: baseline !important; white-space: pre; width: auto !important;">'BJOBD176'</code><code class="sql plain" style="background-attachment: initial !important; background-clip: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial !important; background-repeat: initial !important; background-size: initial !important; border-radius: 0px !important; border: 0px !important; bottom: auto !important; box-shadow: none !important; box-sizing: content-box !important; direction: ltr !important; display: inline !important; float: none !important; font-family: Monaco, Consolas, "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important; font-size: 12.8px; height: auto !important; left: auto !important; line-height: 1.1em !important; margin: 0px !important; outline: 0px !important; overflow: visible !important; padding: 0px !important; position: static !important; right: auto !important; top: auto !important; vertical-align: baseline !important; white-space: pre; width: auto !important;">) </code><code class="sql keyword" style="background-attachment: initial !important; background-clip: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial !important; background-repeat: initial !important; background-size: initial !important; border-radius: 0px !important; border: 0px !important; bottom: auto !important; box-shadow: none !important; box-sizing: content-box !important; color: rgb(0, 102, 153) !important; direction: ltr !important; display: inline !important; float: none !important; font-family: Monaco, Consolas, "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important; font-size: 12.8px; font-weight: bold !important; height: auto !important; left: auto !important; line-height: 1.1em !important; margin: 0px !important; outline: 0px !important; overflow: visible !important; padding: 0px !important; position: static !important; right: auto !important; top: auto !important; vertical-align: baseline !important; white-space: pre; width: auto !important;">from</code><span style="background-color: white; font-family: Monaco, Consolas, "Bitstream Vera Sans Mono", "Courier New", Courier, monospace; font-size: 12.8px; white-space: pre;"> </span><code class="sql plain" style="background-attachment: initial !important; background-clip: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial !important; background-repeat: initial !important; background-size: initial !important; border-radius: 0px !important; border: 0px !important; bottom: auto !important; box-shadow: none !important; box-sizing: content-box !important; direction: ltr !important; display: inline !important; float: none !important; font-family: Monaco, Consolas, "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important; font-size: 12.8px; height: auto !important; left: auto !important; line-height: 1.1em !important; margin: 0px !important; outline: 0px !important; overflow: visible !important; padding: 0px !important; position: static !important; right: auto !important; top: auto !important; vertical-align: baseline !important; white-space: pre; width: auto !important;">dual)</code></blockquote>
<div style="text-align: justify;">
Damit wird ein "scalar subquery caching" hervorgerufen: dadurch dass die Funktion für den gegebenen Eingabewert im Rahmen der gleichen Query immer den gleichen Wert zurückgeben muss, kann der Optimizer das Ergebnis durch einmalige Ausführung bestimmen und für alle folgenden Datensätze wiederverwenden. Dieser Trick ist nicht neu - Mohamed verweist in diesem Zusammenhang auf Tom Kyte -, aber unter den entsprechenden Umständen kann er extrem nützlich sein.</div>
Martin Preisshttp://www.blogger.com/profile/06388592214305009761noreply@blogger.com0tag:blogger.com,1999:blog-17143762.post-68640460803020830472019-11-28T08:45:00.001+01:002019-11-28T17:17:44.561+01:00B*Tree Index Optimierungen in Postgres 12<div style="text-align: justify;">
<a href="https://www.cybertec-postgresql.com/en/b-tree-index-improvements-in-postgresql-v12/">Laurenz Albe</a> erläutert in seinem Artikel im Cybertec Blog ein paar interessante Optimierungen für B*Tree Indizes, die mit Postgres 12 eingeführt wurden. Ein erster Punkt ist, dass Indizes, die als non-unique definiert sind, in Postgres 12 deutlich kompakter erstellt werden. Ursache dafür ist, dass die TID (also die tuple Id, sprich: die physikalische Satzadresse) in den Schlüssel aufgenommen wurde, was eine bessere interne Sortierung der Einträge mit sich bringt und dafür sorgt, dass page splits bei der Erweiterung des Index seltener als 50:50 Splits in der Mitte der Struktur und häufiger als 90:10 Splits am rechten Ende der Index-Struktur erfolgen. Eine weitere Verbesserung betrifft auch die eindeutigen Indizes: die interne Speicherung in den Index Blocks wurde durch eine komprimierte Ablage der Informationen optimiert, was eine Reduzierung der Anzahl der Ebenen in der Index-Struktur mit sich bringt und damit die Zahl der Lesezugriffe beim Zugriff auf einen einzelnen Index-Eintrag reduzieren kann. Die Änderung der internen Index-Struktur ist als Version 4 der B*Tree Index-Implementierung definiert und bedeutet auch, dass Indizes nach einer Migration via pg_upgrade neu aufgebaut werden müssen, um optimiert abgelegt werden zu können. Neben diesen beiden detailliert ausgeführten Verbesserungen werden noch folgende Änderungen erwähnt, die Postgres 12 mit sich bringt:</div>
<ul>
<li style="text-align: justify;">Reduzierung des locking overhead bei Inserts</li>
<li style="text-align: justify;">Einführung einer "redindex concurrently" Option (was aus meiner Sicht ein extrem nützliches Feature ist)</li>
<li style="text-align: justify;">Performance-Verbesserungen für index-only scans bei Indizes mit vielen Attributen</li>
<li style="text-align: justify;">Ergänzung einer dictionary view pg_stat_progress_create_index, mit der sich der Fortschritt beim Index-(Neu-)Aufbau überprüfen lässt</li>
</ul>
<div style="text-align: justify;">
Insgesamt sind das diverse hochinteressante Features, die Postgres 12 interessant machen können.</div>
Martin Preisshttp://www.blogger.com/profile/06388592214305009761noreply@blogger.com0tag:blogger.com,1999:blog-17143762.post-53453900116840342962019-10-28T10:48:00.001+01:002019-10-28T10:48:56.715+01:00Speicher-Fragmentierung für Linux-Server<div style="text-align: justify;">
Nikolay Savvinov hat zuletzt mehrere interessante Artikel zum Thema der Memory Fragmentation auf Linux-Systemen veröffentlicht, die ich hier einfach mal verlinke, ohne mich allzu intensiv mit den Inhalten zu beschäftigen:</div>
<ul>
<li style="text-align: justify;"><a href="https://savvinov.com/2019/10/03/how-to-hang-a-server-with-a-single-ping-and-other-fun-things-we-learned-in-a-18c-upgrade/">How to hang a server with a single ping, and other fun things we learned in a 18c upgrade</a>: behandelt die Probleme eines Upgrades eines alten und komplexen Systems von Oracle 11 auf 18, die sich zunächst in einem hohen Load Average manifestierten und mit ps analysiert werden konnten, wobei vor allem der "wait channel" (wchan) ausgewertet wurde. Das Ergebnis deutete auf Memory Fragmentierung hin, da vor allem der Channel cma_acquie_dev sichtbar wurde, wobei CMA für "Contiguos Memory Allocator" steht. Zur Behebung der Symptome diente zunächst die Deaktivierung von NUMA, aber die eigentliche Ursache waren wohl rds-ping Operationen. Wer angesichts dieser Zusammenfassung etwas ratlos bleibt (zumindest im Bereich der Auflösung), darf das gerne auf meine mangelnde Sachkenntnis in diesen Bereichen zurückführen.</li>
<li style="text-align: justify;"><a href="https://savvinov.com/2019/10/14/memory-fragmentation-the-silent-performance-killer/">Memory fragmentation: the silent performance killer</a>: erklärt Memory Fragementation und zeigt, mit welchen Mitteln man die zugehörigen Effekte in Linux-Systemen analysieren kann. Hier versuche ich mich erst gar nicht an der Zusammenfassung, sondern zitiere "As usual, exact solution will depend on the specific scenario of the problem, but it would typically involve changing VM settings (such as vm.min_kbytes_free) or adjusting memory cgroup configuration."</li>
<li style="text-align: justify;"><a href="https://savvinov.com/2019/10/23/where-did-my-ram-go/">Where did my RAM go?</a>: liefert ein wenig R code zur Visualisierung der Performance-Informationen.</li>
</ul>
<div style="text-align: justify;">
Da ich in Linux-Zusammenhängen häufiger über Memory-Fragen stolpere, werden ich das Werkzeug möglicherweise gelegentlich zum Einsatz bringen.</div>
Martin Preisshttp://www.blogger.com/profile/06388592214305009761noreply@blogger.com0tag:blogger.com,1999:blog-17143762.post-48295662724792083182019-10-18T11:24:00.001+02:002019-10-18T11:24:40.961+02:00Erläuterungen zu Execution Plans in Postgres<div style="text-align: justify;">
Damit ich es wieder finde: <a href="https://www.pgmustard.com/blog/2019/9/17/postgres-execution-plans-field-glossary">David Conlin</a> hat einen Glossar zu den Angaben in den Execution Plans des explain Befehls in Postgres veröffentlicht. Darin finden sich auch Link zu älteren Artikeln von <a href="https://www.depesz.com/tag/unexplainable/">Hubert Lubaczewski</a> (aka DEPESZ). Obwohl ich ziemlich oft auf Ausführungspläne schaue, gibt es in diesem Bereich immer wieder Überraschungen - und da ist jeder erhellende Beitrag nützlich. Interessant ist etwa der Hinweis, dass die summierten Angaben bei "Materialize" Knoten aufgrund von Rundungen manchmal nicht zu den untergeordneten Werten passen. Oder die exakte Erklärung für "Actual Startup Time" (= Laufzeit bis der erste Datensatz zurückgeliefert ist). Oder die exakten Erklärungen zu den Buffer-Angaben.</div>
Martin Preisshttp://www.blogger.com/profile/06388592214305009761noreply@blogger.com0tag:blogger.com,1999:blog-17143762.post-59388899282677354882019-09-19T16:01:00.001+02:002019-09-19T16:01:53.960+02:00Keine non-pdb Systeme mehr mit Oracle 20<div style="text-align: justify;">
Es kommt nicht überraschend, sei hier aber erwähnt: mit Oracle 20 wird die non-CDB Architektur nicht mehr zur Verfügung stehen. Dafür ist mit Oracle 19 in SE2 und EE die Verwendung von drei (vom User erzeugten) pluggable databases auch ohne Multitenant-Lizenz erlaubt. So nachzulesen bei <a href="https://mikedietrichde.com/2019/09/17/3-pdbs-included-with-oracle-19c-and-desupport-of-non-cdbs-with-20c/">Mike Dietrich</a>.</div>
Martin Preisshttp://www.blogger.com/profile/06388592214305009761noreply@blogger.com0tag:blogger.com,1999:blog-17143762.post-12967804312131418062019-08-14T08:43:00.001+02:002019-08-14T08:43:26.553+02:00Löschung von automatisch erzeugten Indizes in Oracle 19c<div style="text-align: justify;">
Die automatische Generierung von Indizes durch das "Auto Indexing" in Oracle 19c sieht auf den ersten Blick wie ein ausgesprochen interessantes Feature aus. Das ist sie zweifellos auch. Wie gut sie funktioniert, ist ein anderes Thema. <a href="https://medium.com/@FranckPachot/how-to-drop-an-index-created-by-oracle-19c-auto-indexing-62e49ce47f14">Franck Pachot</a> zeigt, dass es zumindest nicht ganz leicht ist, die automatisch generierten Indizes wieder los zu werden: mit einem simplen "drop index" klappt das schon mal nicht - und ob manuelle Anpassungen in sys.ind$ tatsächlich eine gute Idee sind, wage ich (wie auch der Herr Pachot) zu bezweifeln.</div>
Martin Preisshttp://www.blogger.com/profile/06388592214305009761noreply@blogger.com0tag:blogger.com,1999:blog-17143762.post-69424878483999564692019-08-08T09:17:00.001+02:002019-08-08T09:17:33.974+02:00AWK-Skripte zur Auswertung von CBO Traces<div style="text-align: justify;">
Die durch das Trace Event 10053 erzeugten Optimizer Traces sind eine großartige Hilfe, wenn es darum geht, die Entscheidungen des Optimizers nachzuvollziehen. Leider sind die Ausgaben aber so umfangreich und unübersichtlich, dass es unter Umständen ziemlich lange dauert, bis man die relevanten Details daraus exzerpiert hat. Zu Vereinfachung des Vorgehens hat Nenad Noveljic ein paar Skripte auf AWK-Basis veröffentlicht:</div>
<ul>
<li style="text-align: justify;"><a href="https://nenadnoveljic.com/blog/extracting-query-block-optimizer-trace/">EXTRACTING QUERY BLOCK INFORMATION FROM OPTIMIZER TRACE</a>: erläutert das Skript opt_qb.awk, mit dessen Hilfe man Informationen zu einem bestimmten Query-Block anzeigen lassen kann.</li>
<li style="text-align: justify;"><a href="https://nenadnoveljic.com/blog/extracting-join-order-optimizer-trace/">EXTRACTING JOIN ORDER FROM OPTIMIZER TRACE</a>: erläutert das Skript opt_jo.awk, das die Informationen zu einer bestimmten Join Reihenfolge liefert.</li>
</ul>
<div style="text-align: justify;">
Ob ich diese Skripte tatsächlich nutzen werde, weiß ich noch nicht: aber die Idee einer solchen Filterung erscheint mir ausgesprochen einleuchtend.</div>
Martin Preisshttp://www.blogger.com/profile/06388592214305009761noreply@blogger.com0tag:blogger.com,1999:blog-17143762.post-21388129619466313022019-07-29T16:13:00.001+02:002019-07-29T16:16:24.180+02:00Prepared Statements und Bindewerte in Postgres<div style="text-align: justify;">
<a href="https://medium.com/@FranckPachot/postgresql-bind-variable-peeking-fb4be4942252">Franck Pachot</a> erläutert in seinem Blog die unterschiedlichen Straegien bei der Verwendung von Bindevariablen bei Oracle und Postgres. Interessant sind aus meiner Sicht vor allem die Aussagen zu Postgres:</div>
<ul>
<li style="text-align: justify;">normalerweise werden Queries bei jeder Ausführung neu optimiert.</li>
<li style="text-align: justify;">Ausnahme von dieser Regel sind Statements, die prepared wurden.</li>
<ul>
<li style="text-align: justify;">Für diese gilt, dass sie bei den ersten fünf Ausführungen jeweils neu kompiliert werden. </li>
<li style="text-align: justify;">Ab der sechsten Ausführung wird dann ein generischer Plan verwendet, der sich aber nicht um die bis dahin verwendeten Bindewerte kümmert (also kein "bind peeking" verwendet).</li>
<li style="text-align: justify;">Dieser generische Plan verwendet eine Selektivität, die sich ergibt, wenn man die - per default vorhandenen - Histogramme ignoriert.</li>
<li style="text-align: justify;">Ob der generische Plan verwendet wird, hängt auch noch davon ab, ob der Optimizer ihn als effizienter betrachtet als die "custom plans" (die im Beispiel bei den ersten fünf Ausführungen verwendet wurden). Ist das nicht der Fall, bleibt der Optimizer bei den "custom plans".</li>
</ul>
<li style="text-align: justify;">in Postgres 12 lässt sich das Verhalten des Optimizers stärker beeinflussen. Hier wird der PLAN_CACHE_MODE eingeführt, der die Werte AUTO (= das beschriebene Verfahren), FORCE_CUSTOM_PLAN und FORCE_GENERIC_PLAN annehmen kann.</li>
<li style="text-align: justify;">Postgres unterstützt kein "plan sharing" über de Grenzen von Sessions hinaus: es gibt keinen Shared Pool.</li>
</ul>
<div style="text-align: justify;">
Insgesamt ist das wieder mal ein sehr schönes Beispiel dafür, wie gut sich die Konzepte unterschiedlicher RDBMS erläutern lassen, wenn man sie miteinander vergleicht.</div>
Martin Preisshttp://www.blogger.com/profile/06388592214305009761noreply@blogger.com0tag:blogger.com,1999:blog-17143762.post-84947715409615677852019-07-19T13:41:00.001+02:002019-07-19T13:41:47.468+02:00Informationen zur Hint-Verwendung in 19c<div style="text-align: justify;">
Eine schöne Ergänzung für dbms_xplan in 19c ist die Ergänzung des Formats HINT_REPORT - im verknüpften Artikel beschrieben von <a href="https://gavinsoorma.com/2019/06/oracle-19c-new-feature-hint-usage-report/">Gavin Soorma</a>. Das Format liefert Informationen zu den im Statement angegebenen Hints und erklärt, warum ein Hint nicht berücksichtigt wurde. Diese Information musste man bislang über ein CBO Trace (Event 10053) ermitteln.</div>
Martin Preisshttp://www.blogger.com/profile/06388592214305009761noreply@blogger.com0tag:blogger.com,1999:blog-17143762.post-47698748200366894662019-06-26T13:48:00.000+02:002019-06-26T13:48:54.512+02:00Fehler beim Mview-Refresh mit Lateral Join in 12.2<div style="text-align: justify;">
Da ich in den ODC Foren (früher OTN Foren) kaum etwas wiederfinde, hier ein <a href="https://community.oracle.com/thread/4275121">Link</a> auf eine Frage, die ich zu einem Verhalten gestellt habe, das mir zuletzt beim Refresh einer Materialized View mit Lateral Join begegnet war:</div>
<br />
<div class="codesnippet">
<pre>-- 12.2.0.1 SE
create table t(
col1 number
, col2 varchar2(4000)
);
insert into t(col1, col2) values(1, '[{"type":1,"target":42}]');
insert into t(col1, col2) values(2, '[{"type":1,"target":42},{"type":2,"target":43}]');
create materialized view t_mv
as
select t.col1, t.col2, r.val
from t,
LATERAL (SELECT MIN(val) AS val
FROM JSON_TABLE ( t.col2, '$[*].target'
COLUMNS (val NUMBER PATH '$')
)
) r;
SQL> create materialized view t_mv
2 as
3 select t.col1, t.col2, r.val
4 from t,
5 LATERAL (SELECT MIN(val) AS val
6 FROM JSON_TABLE ( t.col2, '$[*].target'
7 COLUMNS (val NUMBER PATH '$')
8 )
9 ) r;
Materialized view created.
SQL> exec dbms_mview.refresh('t_mv')
BEGIN dbms_mview.refresh('t_mv'); END;
*
ERROR at line 1:
ORA-00942: table or view does not exist
ORA-06512: at "SYS.DBMS_SNAPSHOT_KKXRCA", line 2952
ORA-06512: at "SYS.DBMS_SNAPSHOT_KKXRCA", line 2370
ORA-06512: at "SYS.DBMS_SNAPSHOT_KKXRCA", line 85
ORA-06512: at "SYS.DBMS_SNAPSHOT_KKXRCA", line 245
ORA-06512: at "SYS.DBMS_SNAPSHOT_KKXRCA", line 2352
ORA-06512: at "SYS.DBMS_SNAPSHOT_KKXRCA", line 2908
ORA-06512: at "SYS.DBMS_SNAPSHOT_KKXRCA", line 3191
ORA-06512: at "SYS.DBMS_SNAPSHOT_KKXRCA", line 3221
ORA-06512: at "SYS.DBMS_SNAPSHOT", line 15
ORA-06512: at line 1
</pre>
</div>
<br />
<div style="text-align: justify;">
Die Anlage der MView ist also zunächst erfolgreich, aber der complete refresh scheitert mit einem unerwarteten ORA-942-Fehler - obwohl die Tabelle im gleichen Schema liegt wie die MView. Ein Blick ins SQL Trace zur Operation zeigt, dass der Fehler offenbar die Statistikerstellung für ein transientes Objekt betrifft: eine VW_LAT, die natürlich nicht persitiert wird:</div>
<br />
<div class="codesnippet">
<pre>kkzfThrowError: error_pos = kkzfGetNumRows:OCIStmtPrepare2 errcode = 942 msgbuf =ORA-00942: table or view does not exist
kkzfPrintError: error = 31933 btm = 0 emsglen = 137
ORA-31933: error occurred during refresh statistics processing at kkzfGetNumRows:OCIStmtPrepare2
ORA-00942: table or view does not exist
kkzfSetupStatsCtxDrv:1: stats is disabled on error 31933
kkzfSetupStatsCtxDrv:1: error = 31933 is cleared
kkzdQueryObjNumByName:------ORA-1403 data not found ---
kkzdQueryObjNumByName:usrid= 104, oname=VW_LAT_9DCD9C42
kkzdQueryObjNumByName:---------------------------------
</pre>
</div>
<br />
Im Forum gab es - wie üblich - ein paar gute Hinweise:<br />
<ul style="text-align: justify;">
<li>Andrew Sayer lieferte den Vorschlag, einen optimizer_features_enable Hint zu ergänzen, um das Optimizer-Verhalten auf eine andere Version zu setzen (mit 11.1.0.7 funktioniert der refresh).</li>
<li>Dontatello Settembrino merkte an, dass der refresh funktioniert, wenn man die Operation als SYS ausführt (was kein workaround ist, aber eine interessante Beobachtung).</li>
<li>Mustafa Kalayci fand heraus, dass der Refresh in 12.2 funktioniert, wenn man ihn in eine skalare Subquery einbaut.</li>
</ul>
Damit habe ich ausreichend viele Varianten, um dem Problem aus dem Weg zu gehen. Martin Preisshttp://www.blogger.com/profile/06388592214305009761noreply@blogger.com0tag:blogger.com,1999:blog-17143762.post-44535356831239400852019-06-12T13:30:00.001+02:002019-06-12T13:30:07.904+02:00Partitionierung mit Postgres 12<div style="text-align: justify;">
Daniel Westermann hat im dbi Services Blog eine interessante Serie begonnen, die sich mit den aktuellen Möglichkeiten der Partitionierung in Postres 12 (das sich noch im Beta-Status befindet) beschäftigt:</div>
<ul>
<li style="text-align: justify;"><a href="https://blog.dbi-services.com/postgresql-partitioning-1-preparing-the-data-set/">PostgreSQL partitioning (1): Preparing the data set</a>: beschreibt die Erzeugung einer Testtabelle aus einer öffentlich verfügbaren Datenquelle der US-Regierung. Die Ladeoperation erfolgt über ein copy-Kommando. Zu dieser Tabelle wird eine Materialized View erzeugt. Wenn eine MV einen unique index besitzt kann der Refresh concurrently erfolgen (also parallele Zugriffe während des Refreshs erlauben).</li>
<li style="text-align: justify;"><a href="https://blog.dbi-services.com/postgresql-partitioning-2-range-partitioning/">PostgreSQL partitioning (2): Range partitioning</a>: zeigt das Vorgehen zur Anlage einer Tabelle mit Range-Partitionierung auf Jahresbasis, bei dem zunächst die partitionierte Tabelle und anschließend separat die Partitionen angelegt werden. Die ranges werden in der Definition der Partition explizit angegeben - also nicht über ein Pattern. Außerdem muss die Obergrenze bereits den ersten Tag des Folgejahres angeben, da die "to" Angabe eine exklusives Limit darstellt. Zusätzlich zu den definierten range-Partitionen kann eine default Partition erzeugt werden, die alle Datensätze aufnimmt, die keinem der definierten ranges entsprechen.</li>
<li style="text-align: justify;"><a href="https://blog.dbi-services.com/postgresql-partitioning-3-list-partitioning/">PostgreSQL partitioning (3): List partitioning</a>: zeigt das Verhalten von List Partitionierung, dass anscheinend keine besonderen Überraschungen bietet.</li>
<li style="text-align: justify;"><a href="https://blog.dbi-services.com/postgresql-partitioning-4-hash-partitioning/">PostgreSQL partitioning (4): Hash partitioning</a>: beschäftigt sich mit Hash Partitionierung. Dazu wird in der Partitions-Definition eine modulus-Funktion auf einen Spaltenwert verwendet, um die Zuordnung zu bestimmen. In diesem Fall ist die Anlage einer default-Partition nicht möglich (oder sinnvoll). Da die Verteilungsfunktion manuell definiert wird, liegt es beim Verwender, dafür zu sorgen, dass sich eine plausible und gleichmäßige Verteilung auf die Partitionen ergibt. Hier scheint das Verfahren noch nicht allzu elaboriert zu sein.</li>
<li style="text-align: justify;"><a href="https://blog.dbi-services.com/postgresql-partitioning-5-partition-pruning/">PostgreSQL partitioning (5): Partition pruning</a>: behandelt das Partition Pruning, also die Möglichkeit des Optimizers, für eine Abfrage irrelevante Partitionen beim Zugriff ausklammern zu können. In Postgres 10 funktionierte das nur, wenn eine Einschränkung bereits in der Planning Phase bestimmbar war. In Postgres 11 kann das Pruning auch erfolgen, wenn die Einschränkung erst bei der Execution erkennbar wird.</li>
<li style="text-align: justify;"><a href="https://blog.dbi-services.com/postgresql-partitioning-6-attaching-and-detaching-partitions/">PostgreSQL partitioning (6): Attaching and detaching partitions</a>: zeigt, wie man Partitionen über attach in eine partitionierte Tabelle eingliedern - bzw. über deatch daraus lösen kann. Die abgehängte Tabelle kann dann nach belieben weiter verwendet werden.</li>
<li style="text-align: justify;"><a href="https://blog.dbi-services.com/postgresql-partitioning-7-indexing-and-constraints/">PostgreSQL partitioning (7): Indexing and constraints</a>: erklärt, wie die Beschränkungen der Partitionierung reduziert werden konnten. In Postgres 10 konnte man keinen Primary Key auf einer partitionierten Tabelle anlegen, was inzwischen möglich ist. Möglich ist auch die Anlage eines Index, der auf eine einzelne Partition beschränkt bleibt. Was noch nicht funktioniert ist die Anlage eines partitionierten Index (in Oracle wäre das ein lokaler Index) mit der Option concurrently. Man kann den Index aber beschränkt auf der Ebene der partitionierten Tabelle anlegen, was ihn in einem invaliden Zustand bringt, und dann ein create index concurrently auf Partitionsebene starten. Anschließend können diese Index-Partitionen an den partitionierten Index attached werden (was diesen in den Zustand valid überführt). Auch die Anpassung von Constraints (etwa not null) kann individuell auf Partitions-Ebene erfolgen.</li>
</ul>
<div style="text-align: justify;">
Die Serie wird fortgesetzt und ich versuche - wie üblich - die folgenden Artikel hier zu ergänzen.</div>
<ul>
</ul>
Martin Preisshttp://www.blogger.com/profile/06388592214305009761noreply@blogger.com0tag:blogger.com,1999:blog-17143762.post-46903512407162920512019-05-28T15:19:00.000+02:002019-05-28T15:19:08.787+02:00Indizierung von NULL-Werten<div style="text-align: justify;">
Randolf Geist hat nach einer längeren Pause zuletzt wieder begonnen Blog-Artikel zu veröffentlichen, was mir sehr gut gefällt. In zwei Artikeln behandelt er die Effekte der Indizierung von NULL-Werten, insbesondere in Kombination mit IN/OR Prädikaten:</div>
<ul>
<li style="text-align: justify;"><a href="https://oracle-randolf.blogspot.com/2019/05/indexing-null-values-part-1.html">Indexing Null Values - Part 1</a>: zeigt zunächst ein Beispiel, in dem ein Index auf einem einzelnen Attribut durch Ergänzung einer Konstante dazu gebracht wird, auch NULL-Werte zu indizieren, was ein übliches Verfahren für derartige Fälle ist. Dieser Index wird dann auch für IS NULL Prädikate verwendet. Im Plan ist erwartungsgemäß zu sehen, dass ein weiteres Prädikat mit einer IN-Liste als Filter-Prädikat für die Tabelle auftaucht. Wenn man den Index neu erzeugt und dieses zweite Attribut als zweite Spalte des Index verwendet, führt die gleiche Query zu einem access-Prädikat für die NULL-Prüfung und einem Filter-Prädikat für die IN-Liste beim Index-Zugriff. Diese Filterung auf dem Index ist zwar günstiger als die Filterung in der Tabelle, aber eigentlich sollte es möglich sein, beide Prädikate im access zu verwenden. Dieser Effekt ist dann offenbar auch dafür verantwortlich, dass hier kein "inlist iterator" verwendet werden kann: anscheinend erlaubt die Implementierung hier keine Kombination der Prädikate aus IS NULL Prüfung und IN/OR Einschränkungen. Dieses Verhalten ändert sich, wenn man die Reihenfolge der Spalten im Index ändert und die Spalte der IS NULL Bedingung ans Ende der Spaltenliste setzt.</li>
<li style="text-align: justify;"><a href="https://oracle-randolf.blogspot.com/2019/05/indexing-null-values-part-2.html">Indexing Null Values - Part 2</a>: behandelt das Verhalten von Bitmap Indizes in vergleichbaren Fällen. Hier ist der Bitmap Index mit mehreren Spalten natürlich eine unübliche Wahl - obwohl er bei entsprechender Datenverteilung ausgesprochen kompakt sein kann. Sichtbar wird, dass Oracle hier sehr merkwürdige Cost-Angaben erzeugt, die wenig mit dem tatsächlichen Aufwand beim Zugriff haben. Auch scheint die Darstellung von Filter- und Acces-Prädikaten im Plan nicht unbedingt viel mit der tatsächlichen Arbeit der runtime engine zu tun zu haben. Plausible Werte erhält man nur mit der vorgesehenen Verwendung einspaltiger Bitmap Indizes, obwohl der mehrspaltige Bitmap Index im Beispiel tatsächlich geringfügig effizienter ist.</li>
</ul>
<div style="text-align: justify;">
Sollte die Serie fortgesetzt werden, ergänze ich die zugehörigen Artikel - vielleicht. </div>
<ul>
</ul>
Martin Preisshttp://www.blogger.com/profile/06388592214305009761noreply@blogger.com0tag:blogger.com,1999:blog-17143762.post-67896912195622400242019-05-28T10:05:00.001+02:002019-05-28T10:05:12.136+02:00dbms_job Umwandlung bei der Migration zu Oracle 19<div style="text-align: justify;">
<a href="https://mikedietrichde.com/2019/05/24/dbms_job-behavior-change-in-oracle-19c-during-upgrade/">Mike Dietrich</a> informiert in seinem Blog darüber, dass mit Oracle 19 die mit dbms_job definierten Jobs in Aufträge des dbms_scheduler umgewandelt werden. Da dbms_scheduler seit den Tagen von Oracle 10 existiert, in so ziemlich allen mir erinnerlichen Punkten robuster und flexibler als dbms_job ist, bessere Überwachungsmechanismen besitzt und da dbms_job mit Oracle 12.2.0.1 endlich als deprecated klassifiziert wurde, halte ich das persönlich erst mal für eine sinnvolle Entwicklung.</div>
<div style="text-align: justify;">
Trotzdem muss man im Rahmen des Upgrades natürlich darauf achten, dass es bei der Konvertierung nicht zu unerwarteten Effekten kommt. Zur Umwandlung sind noch folgende Punkte relevant:</div>
<ol style="text-align: justify;">
<li>während des Upgrades auf 19c wird zu jedem dbms_job-Job ein entsprechender dbms_scheduler-Job erstellt</li>
<li>das dbms_job-Interface funktioniert weiterhin, aber es wird immer zur Anlage von scheduler-Jobs führen</li>
<li>ein zugehöriger Check in preupgrade.jar prüft auf Inkonsistenzen</li>
</ol>
<div style="text-align: justify;">
Somit kann man also dbms_job weiterhin verwenden, aber unter der Haube wird alles in scheduler-Jobs umgewandelt. Ob die weitere Verwendung von dbms_job unter diesen Umständen besonders sinnvoll ist, sei dahingestellt - aber diese Frage stellte sich ja schon von dem Umzug auf Version 19.</div>
Martin Preisshttp://www.blogger.com/profile/06388592214305009761noreply@blogger.com0