{"id":395,"date":"2017-12-14T23:18:08","date_gmt":"2017-12-14T21:18:08","guid":{"rendered":"http:\/\/blog.sqlora.com\/de\/?p=395"},"modified":"2018-04-04T16:24:40","modified_gmt":"2018-04-04T14:24:40","slug":"polymorphic-table-functions","status":"publish","type":"post","link":"https:\/\/blog.sqlora.com\/de\/polymorphic-table-functions\/","title":{"rendered":"Polymorphe Tabellenfunktionen"},"content":{"rendered":"<p>Letzten Monat konnte ich an der DOAG-Jahreskonferenz in N\u00fcrnberg teilnehmen. Wie immer ein tolles Event, gro\u00dfartige Community und exzellente Vortr\u00e4ge. Und es scheint so zu sein, als h\u00e4tte ich mein Lieblings-Feature der neuen Datenbank 18c gefunden. Keith Laker (<a href=\"https:\/\/twitter.com\/ASQLbarista\" rel=\"noopener\" target=\"_blank\">@ASQLBarista<\/a>), Oracle&#8217;s Produkt Manager f\u00fcr Analytisches SQL, sparch \u00fcber &#8222;Building Agile Self-Describing SQL Functions For Big Data&#8220;. Der Vortragstitel war sehr vielversprechend und nat\u00fcrlich war ich nicht entt\u00e4uscht. Danke f\u00fcr sehr interessante Pr\u00e4sentation!<\/p>\n<p>Dieser Beitrag ist wohl etwas ungew\u00f6hnlich, weil ich noch kein echtes Know-How teilen kann, sondern erstmal meine Begeisterung \u00fcber die M\u00e4chtigkeit und Flexibilit\u00e4t von dem neuen Feature. Worum geht es bei dem Begriff &#8222;Polymorphic Table Functions&#8220;?<!--more--><\/p>\n<p>Wahrscheinlich ist Ihnen bereits das Konzept von Tabellenfunktionen bekannt. Diese k\u00f6nnen &#8222;pipelined&#8220; sein oder nicht, das ist nicht relevant. Wichtig ist, man kann aus so einer Funktion in SQL &#8222;selektieren&#8220;: <\/p>\n<pre class=\"brush: sql; title: ; notranslate\" title=\"\">\r\nselect * from table(dbms_xplan.display);\r\n<\/pre>\n<p>Die zweite wichtige Eigenschaft, ist dass die Tabellenfunktionen immer einen definierten Tabellen-Datentyp zur\u00fcckgeben. Zum Beispiel, liefert <strong>dbms_xplan.display<\/strong> den Typ <strong>dbms_xplan_type_table<\/strong> zur\u00fcck, der als TABLE OF VARCHAR2(4000) definiert ist. Es kann nicht passieren, dass die Logik in der Funktion <strong>dbms_xplan.display<\/strong> diesen Datentyp \u00e4ndert. Der Tabellen-Datentyp ist fix. Und das ist ein Hindernis, um flexible und generische Erweiterungsfunktionen implementieren zu k\u00f6nnen. <\/p>\n<p>Aber auch die Implementierung spezifischer Funktionen kann problematisch sein. Vielleicht haben Sie sich gefargt, wie gehe ich mit parallelen Ausf\u00fchrung um? Wenn Sie sich nach den M\u00f6glichkeiten umgeschaut haben, wie man Strings aggregieren (konkatenieren) kann, bevor die Funktion <strong>LISTAGG<\/strong> in 11g eingef\u00fchrt wurde, haben sie bestimmt schon mal <a href=\"https:\/\/asktom.oracle.com\/pls\/asktom\/f?p=100:11:0::::p11_question_id:2196162600402\" rel=\"noopener\" target=\"_blank\">die Tom Kyte&#8217;s Implementierung von der Funktion <strong>STRAGG<\/strong><\/a> gesehen. Nicht wirklich einfach, \u00fcber einen Objekttyp mit vier Methoden, unter anderem einer Methode, um die Teilergebnisse nach der parallelen Ausf\u00fchrung zu &#8222;mergen&#8220;.<\/p>\n<p><strong>Polymorphe Tabellenfunktionen (PTF)<\/strong>  adressieren diese Probleme.  Polymorphe Tabellenfunktionen sind Teil von ANSI SQL 2016 Standard und sind eine Evolution der Tabellenfunktionen. Sie k\u00f6nnen in der FROM-Klausel aufgerufen werden, akzeptieren Daten aus beliebiger Tabellen (oder WITH-Unterabfragen). Der R\u00fcckgabe-Datentyp muss nicht bei Entwicklung festgelegt werden. Die Funktion kann die Daten verarbeiten, dabei Datens\u00e4tze hinzuf\u00fcgen oder weggruppieren, Spalten hinzuf\u00fcgen oder herausnehmen, und die verarbeiteten Daten weiter aus der Funktion an die SQL-Abfrage zur\u00fcckgeben. Der Datentyp der zur\u00fcckgegebener Tabelle muss nicht vorher definiert werden.<\/p>\n<p>Ich verzichte auf mein eigenes Beispiel, weil es eine Spekulation w\u00e4re, ohne es selber in einer Datenbank ausprobiert zu haben. Hier ist das Beispiel aus dem Vortrag von Keith Laker: Kredit-Score und Risiko-Level eines Kunden zur\u00fcckgeben:<\/p>\n<p><a href=\"http:\/\/blog.sqlora.com\/en\/wp-content\/uploads\/sites\/2\/2017\/12\/20171123_141434.jpg\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/blog.sqlora.com\/en\/wp-content\/uploads\/sites\/2\/2017\/12\/20171123_141434.jpg\" alt=\"\" width=\"2008\" height=\"1092\" class=\"alignnone size-full wp-image-567\" \/><\/a><\/p>\n<p>Wir \u00fcbergeben die Tabelle <strong>scott.customers<\/strong> in die Funktion CREDIT_RISK, definieren die Spalten, die man f\u00fcr Berechnungen braucht und die Spalten, die zur\u00fcckgegeben werden m\u00fcssen. Eigentlich bin ich der Meinung, dass wir hier noch die Spalte STATE_ID im Parameter OUTS erw\u00e4hnen sollten, damit wir danach gruppieren k\u00f6nnen. Kann aber sein, dass ich etwas missverstanden habe. Das war&#8217;s. Wir k\u00f6nnen aus der Funktion einfach selektieren: die neu berechneten Spalten sind an jedem Datensatz dran und k\u00f6nnen gruppiert werden.<\/p>\n<p>Mann kann &#8222;Leaf&#8220; und &#8222;Non-Leaf&#8220; PTFs unterscheiden und die letzten k\u00f6nnen <strong>row semantic<\/strong> (RS PTF) oder <strong>table semantic<\/strong> (TS PTF) sein. Der Unterschied ist, dass man bei  row semantic nur den aktuellen Datensatz braucht, um alle Outputs zu produzieren. Bei table semantic muss man auf die vorher verarbeiteten Datens\u00e4tze schauen, um neue Spalten oder Datens\u00e4tze zu generieren &#8211; hier geht es eher um eine Art Aggregat-\/analytische Funktionen. Mit TS PTF kann man optional den Input partitionieren und ordnen (wie bei analytischen Funktionen):<\/p>\n<pre class=\"brush: sql; title: ; notranslate\" title=\"\">\r\nSELECT *\r\nFROM   CUSTOM_GRPBY(scott.emp PARTITION BY job ORDER BY empno);\r\n<\/pre>\n<p>Wie sieht es mit parallelen Ausf\u00fchrung aus? Die Aussage ist, der Entwickler kann einfach von einer seriellen Ausf\u00fchrung ausgehen. Alles andere wird durch die Datenbank geregelt. Mit einer Anmerkung: bei Tabellensemantik muss man die Partitionierung mit angeben, wie im Beispiel oben. Alle Datens\u00e4tze einer Partition landen auf demselben Parallel Slave. Gibt man die Partitionierung nicht an, wird die Funktion zu einem Serialisierungspunkt.<\/p>\n<p>Es gibt auch ein paar gute Neuigkeiten f\u00fcr Performance: Bulk-\u00dcbertragung der Daten in die und aus der Funktion, wobei nur die ben\u00f6tigten Spalten \u00fcbertragen werden. Soweit m\u00f6glich, werden die Pr\u00e4dikate, Partitionierung in die darunterliegende Tabelle weitergereicht.<\/p>\n<p>Alle Datentypen und Hilfsroutinen zum Implementieren der PTFs sind im Package <strong>DBMS_TF<\/strong> zu finden. \u00dcbrigens, das Package ist bereits in der Version 12.2.0.1 da, wenn auch (noch) nicht dokumentiert. Sie k\u00f6nnen sogar versuchen, ihre erste PTF anzulegen, es geht aber nicht ganz. Schlie\u00dflich ist es ein 18c Feature!<\/p>\n<p><a href=\"http:\/\/blog.sqlora.com\/en\/wp-content\/uploads\/sites\/2\/2017\/12\/DBMS_TF.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/blog.sqlora.com\/en\/wp-content\/uploads\/sites\/2\/2017\/12\/DBMS_TF.png\" alt=\"\" width=\"243\" height=\"355\" class=\"alignnone size-full wp-image-579\" \/><\/a><\/p>\n<pre class=\"brush: sql; title: ; notranslate\" title=\"\">\r\ncreate or replace package ptf_test as \r\n  procedure describe (new_cols out dbms_tf.columns_new_t,  tab in out dbms_tf.table_t, cols dbms_tf.columns_t) ;\r\n  function my_ptf (tab table, type_name varchar2, flip varchar2 default 'True') \r\n       return table pipelined row polymorphic using ptf_test;\r\nend;\r\n\/\r\n\r\nshow errors\r\n\r\nLINE\/COL ERROR\r\n-------- --------------------------------------------------------\r\n3\/124    PLS-00998: implementation restriction (may be temporary)\r\n\r\n<\/pre>\n<p>Welche interessante Anwendungsf\u00e4lle kann ich mir vorstellen? Im Moment arbeite ich an der Integration der Daten in einem Data Warehouse System, die urspr\u00fcnglich aus einem <a href=\"https:\/\/www.xbrl.org\/\" rel=\"noopener\" target=\"_blank\">XBRL<\/a> Format stammen. In der Quelldatenbank werden diese XBRL-Daten in einer sog. &#8222;Key-Value&#8220; Tabelle persistiert. Nun sind die Anforderungen, wie man mit DAtens\u00e4tzen aus der riesigen Key-Value-Tabelle umgeht, unterschiedlich, meistens abh\u00e4ngig von den Key-Werten und sind SQL-technisch nicht &#8222;kompatibel&#8220;. Mal muss man diese aggregieren, mal pivotieren auch mit Erkennung von sog. &#8222;Tupeln&#8220;, mal noch was anderes. Dies macht mehrfaches Scannen der gro\u00dfen Tabelle n\u00f6tig. Was w\u00e4re, wenn wir die M\u00f6glichkeit h\u00e4tten, die Tabelle nur einmal zu scannen und dann innerhalb einer PTF die Datens\u00e4tze bedingt aggregieren, pivotieren, was auch immer? Man k\u00f6nnte einen ETL-Prozess entwickeln, der aus einer gro\u00dfen Tabelle liest, reicht die Daten in die PTF weiter, die unterschiedliche Datenstr\u00f6me vorbereitet, und leitet die anschlie\u00dfend \u00fcber INSERT ALL SQL Statement in mehrere Zieltabellen weiter. Alles in einem Schritt. Wenn das auch schnell l\u00e4uft, warum nicht? <\/p>\n<p>Apropos Pivot, wird es wohl auch m\u00f6glich sein, eine Funktion f\u00fcr dynamisches PIVOT zu entwickeln, wobei man nicht zur Entwicklungszeit festlegen muss, welche Werte zu Spalten werden, \u00e4hnlich wie die von <a href=\"https:\/\/technology.amis.nl\/2006\/05\/16\/pivot-dynamic-data\/\">Anton Scheffer<\/a>, aber ohne, dass man den Abfragetext \u00fcbergeben muss? Vielleicht habe ich noch was \u00fcbersehen, aber erstmal es klingt gut f\u00fcr mich.<\/p>\n<p>Wenn alles so funktioniert wie versprochen und auch schnell und skalierbar ist, wird es ein sehr interessantes Feature! Abwarten! Bleiben Sie dran, ich werde definitiv im n\u00e4chsten Jahr \u00fcber Polymorphische Funktionen schreiben und ich kann es kaum erwarten, an den Beispielen und Folien zu diesem Thema f\u00fcr unseren Kurs <a href=\"https:\/\/www.trivadis.com\/en\/training\/new-features-12c-developers-o-nf12c-dev\" rel=\"noopener\" target=\"_blank\">New Features Training<\/a> zu arbeiten.<\/p>\n<div class=\"crp-list-container\"><h3 class=\"crp-list-title\">Related Posts<\/h3><ul class=\"crp-list\"><li class=\"crp-list-item crp-list-item-image-none\"><div class=\"crp-list-item-title\"><a href=\"https:\/\/blog.sqlora.com\/de\/polymorphic-table-functions-example-transposing-columns-to-rows\/\" target=\"_blank\">Polymorphic Table Functions Example - Transposing Columns To Rows<\/a><\/div><\/li><li class=\"crp-list-item crp-list-item-image-none\"><div class=\"crp-list-item-title\"><a href=\"https:\/\/blog.sqlora.com\/de\/polymorphic-table-functions-example-not-transposing-rows-to-columns\/\" target=\"_blank\">Polymorphic Table Functions Example \u2013 (NOT) Transposing Rows to Columns<\/a><\/div><\/li><li class=\"crp-list-item crp-list-item-image-none\"><div class=\"crp-list-item-title\"><a href=\"https:\/\/blog.sqlora.com\/de\/polymorphic-table-functions-ptf-part-1-basics\/\" target=\"_blank\">Polymorphic Table Functions (PTF) ,  Part 1 - Basics<\/a><\/div><\/li><\/ul><\/div>\n","protected":false},"excerpt":{"rendered":"<p>Letzten Monat konnte ich an der DOAG-Jahreskonferenz in N\u00fcrnberg teilnehmen. Wie immer ein tolles Event, gro\u00dfartige Community und exzellente Vortr\u00e4ge. Und es scheint so zu sein, als h\u00e4tte ich mein Lieblings-Feature der neuen Datenbank 18c gefunden. Keith Laker (@ASQLBarista), Oracle&#8217;s Produkt Manager f\u00fcr Analytisches SQL, sparch \u00fcber &#8222;Building Agile Self-Describing SQL Functions For Big Data&#8220;. [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":396,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[54,1,39,3,53,4,22],"tags":[56,57,58,55],"class_list":["post-395","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-18c","category-allgemein","category-data-warehouse","category-oracle","category-ptf","category-sql","category-trivadis","tag-polymorphe-tabellenfunktion","tag-18c","tag-pivot","tag-ptf"],"_links":{"self":[{"href":"https:\/\/blog.sqlora.com\/de\/wp-json\/wp\/v2\/posts\/395","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.sqlora.com\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.sqlora.com\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.sqlora.com\/de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.sqlora.com\/de\/wp-json\/wp\/v2\/comments?post=395"}],"version-history":[{"count":16,"href":"https:\/\/blog.sqlora.com\/de\/wp-json\/wp\/v2\/posts\/395\/revisions"}],"predecessor-version":[{"id":433,"href":"https:\/\/blog.sqlora.com\/de\/wp-json\/wp\/v2\/posts\/395\/revisions\/433"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.sqlora.com\/de\/wp-json\/wp\/v2\/media\/396"}],"wp:attachment":[{"href":"https:\/\/blog.sqlora.com\/de\/wp-json\/wp\/v2\/media?parent=395"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.sqlora.com\/de\/wp-json\/wp\/v2\/categories?post=395"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.sqlora.com\/de\/wp-json\/wp\/v2\/tags?post=395"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}