{"id":457,"date":"2008-02-19T13:44:06","date_gmt":"2008-02-19T12:44:06","guid":{"rendered":"http:\/\/soci.hu\/blog\/index.php\/2008\/02\/19\/sql-server-2008-ujdonsagok-23-ordered-sqlclr-table-valued-functions\/"},"modified":"2008-02-19T13:48:05","modified_gmt":"2008-02-19T12:48:05","slug":"sql-server-2008-ujdonsagok-23-ordered-sqlclr-table-valued-functions","status":"publish","type":"post","link":"https:\/\/soci.hu\/blog\/index.php\/2008\/02\/19\/sql-server-2008-ujdonsagok-23-ordered-sqlclr-table-valued-functions\/","title":{"rendered":"SQL Server 2008 \u00fajdons\u00e1gok 23. &#8211; Ordered SQLCLR table-valued functions"},"content":{"rendered":"<p>A Table Valued Function-\u00f6kn\u00e9l eddig nem lehetett megadni inform\u00e1ci\u00f3t a szervernek a f\u00fcggv\u00e9nyb\u0151l j\u00f6v\u0151 adatok sorrendj\u00e9re, \u00edgy hi\u00e1ba j\u00f6ttek sorban az adatok, ha a TVF-b\u0151l j\u00f6v\u0151 lek\u00e9rdez\u00e9s eredm\u00e9ny\u00e9t sorrendeztett\u00fck order by-jal, akkor az SQL Server \u00fajrarendezte az adatokat, ami meglehet\u0151sen nagy k\u00f6lts\u00e9ggel j\u00e1rhat. 2008-ban m\u00e1r megjel\u00f6lhet\u0151 az adatok el\u0151rendezetts\u00e9ge, ami jelent\u0151s k\u00f6lts\u00e9gmegtakar\u00edt\u00e1st eredm\u00e9nyezhet.<br \/>\nN\u00e9zz\u00fck p\u00e1r p\u00e9ld\u00e1t!<\/p>\n<p>Windows Event Log publik\u00e1l\u00e1s:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\nusing System.Collections;\r\nusing System.Data.SqlTypes;\r\nusing System.Diagnostics;\r\nusing Microsoft.SqlServer.Server;\r\n\r\npublic class TVF\r\n{\r\n    &#x5B;SqlFunction(FillRowMethodName = &quot;FillEventLogRow&quot;)]\r\n    public static IEnumerable EventLog(SqlString logName)\r\n    {\r\n        if (logName.IsNull)\r\n        {\r\n            return new int&#x5B;0];\r\n        }\r\n        else\r\n        {\r\n            return new EventLog(logName.Value).Entries;\r\n        }\r\n    }\r\n\r\n    public static void FillEventLogRow(object obj,\r\n        out SqlDateTime timeWritten,\r\n        out SqlString message,\r\n        out SqlString category,\r\n        out SqlString source,\r\n        out SqlInt64 eventId)\r\n    {\r\n        EventLogEntry e = (EventLogEntry)obj;\r\n        message = new SqlString(e.Message);\r\n        timeWritten = new SqlDateTime(e.TimeWritten);\r\n        category = new SqlString(e.Category);\r\n        source = new SqlString(e.Source);\r\n        eventId = new SqlInt64(e.InstanceId);\r\n    }\r\n}\r\n<\/pre>\n<pre class=\"brush: sql; title: ; notranslate\" title=\"\">\r\nuse AdventureWorks;\r\ngo\r\n--Kell az external_access-hez\r\nalter database AdventureWorks set trustworthy on\r\ngo\r\n\r\nexec sp_configure &#039;clr enabled&#039;, &#039;1&#039;;\r\nreconfigure with override;\r\ngo\r\n\r\nif object_id(&#039;EventLog&#039;) is not null drop function EventLog\r\nif exists(select * from sys.assemblies where name = &#039;SqlClrTest&#039;) drop assembly SqlClrTest\r\n\r\ncreate assembly SqlClrTest from &#039;C:\\sql2008\\SqlClrTest\\bin\\SqlClrTest.dll&#039;\r\nwith permission_set = external_access\r\ngo\r\n\r\n--L\u00e9trehozzuk a t\u00e1bla kimenet\u0171 f\u00fcggv\u00e9nyt,\r\n--ami az eventlogot publik\u00e1lja ki\r\ncreate function EventLog(@logname nvarchar(100))\r\nreturns table \r\n(\r\n  Time datetime,\r\n  Message nvarchar(max),\r\n  Category nvarchar(max),\r\n  Source nvarchar(max),\r\n  EventId bigint\r\n)\r\nas external name SqlClrTest.TVF.EventLog\r\ngo\r\n\r\ncreate function EventLogWithOrder(@logname nvarchar(100))\r\nreturns table \r\n(\r\n  Time datetime,\r\n  Message nvarchar(max),\r\n  Category nvarchar(max),\r\n  Source nvarchar(max),\r\n  EventId bigint\r\n)\r\norder (Time asc)  --itt az \u00fajdons\u00e1g\r\nas external name SqlClrTest.TVF.EventLog\r\ngo\r\n<\/pre>\n<p>L\u00e1that\u00f3, hogy az f\u00fcggv\u00e9ny l\u00e9trehoz\u00e1sakor meg lehet adni, az adatok milyen sorrendben fognak j\u00f6nni.<br \/>\nHa hazudunk a sorrendet illet\u0151en, akkor lek\u00e9rdez\u00e9skor \u00edgy j\u00e1runk:<\/p>\n<p>Msg 5332, Level 16, State 1, Line 2<br \/>\nThe order of the data in the stream does not conform to the ORDER hint specified for the CLR TVF &#8216;dbo.EventLog&#8217;. The order of the  data must match the order specified in the ORDER hint for a CLR TVF. Update the ORDER hint to reflect the order in which the input data is ordered, or update the CLR TVF to match the order specified by the ORDER hint.<\/p>\n<p>\u00c9n az event loggal j\u00e1rtam \u00edgy. Elvileg az event log id\u0151 szerint sorba van rendezve. Gyakorlatilag azonban amikor az os-t telep\u00edtettem, akkor az alap\u00e9rtelmezett -8 \u00f3r\u00e1s id\u0151z\u00f3na volt megadva, amit telep\u00edt\u00e9s ut\u00e1n \u00e1ll\u00edtottam \u00e1t. Ett\u0151l az id\u0151 is vissza\u00e1llt 9 \u00f3r\u00e1val, \u00edgy az event log elej\u00e9n voltak nem id\u0151rendi bejegyz\u00e9sek.<\/p>\n<p>Mi\u00e9rt j\u00f3 megadni a sorrendet? Vess\u00fck \u00f6ssze ezt a k\u00e9t lek\u00e9rdez\u00e9st \u00e9s a v\u00e9grehajt\u00e1si terv\u00fcket:<\/p>\n<pre class=\"brush: sql; title: ; notranslate\" title=\"\">\r\nselect *\r\nfrom dbo.EventLog(N&#039;Application&#039;)\r\norder by Time\r\n\r\nselect *\r\nfrom dbo.EventLogWithOrder(N&#039;Application&#039;)\r\norder by Time\r\n<\/pre>\n<p><a href='http:\/\/soci.hu\/blog\/wp-content\/uploads\/2008\/02\/tfvorder.png' title='Table Valued Function order-rel vs. order n\u00e9lk\u00fcl' target='_blank'><img src='http:\/\/soci.hu\/blog\/wp-content\/uploads\/2008\/02\/tfvorder.png' alt='Table Valued Function order-rel vs. order n\u00e9lk\u00fcl' \/><\/a><\/p>\n<p>A val\u00f3di k\u00f6lts\u00e9g persze nem felt\u00e9tlen \u00edgy oszlik meg, de biztos j\u00f3l m\u00e9rhet\u0151 a hat\u00e1s.<br \/>\n\u00c9szrevette mindenki az Assert oper\u00e1tort? \u0150 a hazugs\u00e1gvizsg\u00e1l\u00f3, \u0151 veszi \u00e9szre, ha m\u00e9gse j\u00f3 a be\u00edg\u00e9rt sorrend.<\/p>\n<p>A k\u00f6vetkez\u0151 p\u00e9ld\u00e1ban az eredm\u00e9nyek nagyon gyorsan gener\u00e1l\u00f3dnak, \u00edgy jobban l\u00e1tszik a rendez\u00e9s k\u00f6lts\u00e9ge. Egy egyszer\u0171 hatv\u00e1nyoz\u00f3, kamatos-kamat sz\u00e1m\u00edt\u00f3 f\u00fcggv\u00e9nyt mutatok:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\n&#x5B;SqlFunction(FillRowMethodName = &quot;FillAustinPowersRow&quot;)]\r\npublic static IEnumerable AustinPowers(SqlDouble baseNumber, SqlDouble power, SqlInt32 first, SqlInt32 last)\r\n{\r\n    if (baseNumber.IsNull || power.IsNull || first.IsNull || last.IsNull)\r\n    {\r\n        return null;\r\n    }\r\n    if (first &lt; 0)\r\n    {\r\n        throw new ArgumentOutOfRangeException(&quot;first&quot;,\r\n            &quot;A sorozat els\u0151 eleme min. 0 kell legyen.&quot;);\r\n    }\r\n    if (first &gt; last)\r\n    {\r\n        throw new ArgumentOutOfRangeException(&quot;first, last&quot;,\r\n            &quot;A sorozat els\u0151 elem\u00e9nek kisebb vagy egyenl\u0151 sorsz\u00e1m\u00fanak kell lenni a utols\u00f3 elemn\u00e9l.&quot;);\r\n    }\r\n    return AustinPowersWorker(baseNumber.Value, power.Value, first.Value, last.Value);\r\n}\r\n\r\nstatic IEnumerable AustinPowersWorker(double baseNumber, double power, int first, int last)\r\n{\r\n    double curr = baseNumber * Math.Pow(power, first);\r\n    yield return curr;\r\n\r\n    for (int i = first; i &lt; last; i++)\r\n    {\r\n        curr *= power;\r\n        yield return curr;\r\n    }\r\n}\r\n\r\npublic static void FillAustinPowersRow(object obj, out SqlDouble num)\r\n{\r\n    num = new SqlDouble((double)obj);\r\n}\r\n&amp;#91;\/source&amp;#93;\r\n\r\nH\u00e1t nem im\u00e1dni val\u00f3 ez a yield? \r\n\r\n&amp;#91;source=&#039;sql&#039;&amp;#93;\r\ncreate function Austin(@base float, @power float, @first int, @last int)\r\nreturns table \r\n(\r\n  power float\r\n)\r\nas external name SqlClrTest.TVF.AustinPowers\r\ngo\r\n\r\ncreate function Austin2(@base float, @power float, @first int, @last int)\r\nreturns table \r\n(\r\n  power float\r\n)\r\norder (power asc)\r\nas external name SqlClrTest.TVF.AustinPowers\r\ngo\r\n&amp;#91;\/source&amp;#93;\r\n\r\nTesztek:\r\n&amp;#91;source=&#039;sql&#039;&amp;#93;\r\nselect power from dbo.Austin2(4000000, 1.001, 0, 100000)\r\norder by power\r\ngo\r\nselect power from dbo.Austin(4000000, 1.001, 0, 100000)\r\norder by power\r\n&amp;#91;\/source&amp;#93;\r\n\r\nV\u00e9grehajt\u00e1si id\u0151k: 26000ms vs. 200ms. Ez m\u00e1r jelent\u0151s. R\u00e1ad\u00e1sul, ha nem sz\u00e1mok j\u00f6nnek ki a TVF-b\u0151l, hanem pl. stringek, akkor a rendez\u00e9s m\u00e9g sokkal k\u00f6lts\u00e9gesebb. (A m\u00e9r\u00e9sekn\u00e9l kikapcsoltam az eredm\u00e9nyek megjelen\u00edt\u00e9s\u00e9t a Management Studioban, hogy annak az ideje lehet\u0151leg ne l\u00e1tsszon a kimenetben.)\r\n\r\nAmi viszont furcsa, hogy kb. 10 futtat\u00e1sb\u00f3l egyszer lefut p\u00e1r 100 ms alatt az els\u0151 f\u00fcggv\u00e9ny is. Nem tudom, mi lehet az oka.\r\n\r\nA k\u00f6vetkez\u0151 p\u00e9lda f\u00e1jlokat list\u00e1z egy k\u00f6nyvt\u00e1rban, amely list\u00e1t elvileg az NTFS rendezetten ad vissza:\r\n\r\n&amp;#91;source=&#039;csharp&#039;&amp;#93;\r\n&amp;#91;SqlFunction(FillRowMethodName = &quot;FillDirListRow&quot;)&amp;#93;\r\npublic static IEnumerable DirList(SqlString startDir)\r\n{\r\n    if (startDir.IsNull)\r\n    {\r\n        return null;\r\n    }\r\n    return Directory.GetFiles(startDir.Value);\r\n}\r\n\r\npublic static void FillDirListRow(object obj, out SqlString fileName)\r\n{\r\n    fileName = new SqlString((string)obj);\r\n}\r\n&amp;#91;\/source&amp;#93;\r\n\r\n&amp;#91;source=&#039;sql&#039;&amp;#93;\r\ncreate function DirList(@base nvarchar(300))\r\nreturns table \r\n(\r\n  FileName nvarchar(300)\r\n)\r\norder (FileName asc)\r\nas external name SqlClrTest.TVF.DirList\r\ngo\r\nselect * from dbo.DirList(&#039;c:\\windows\\system32&#039;)\r\n&amp;#91;\/source&amp;#93;\r\n\r\nNos, elk\u00fcldenek a n\u00e1th\u00e1s fr\u00e1szba, hogy m\u00e1r megint be\u00edg\u00e9rtem egy sorrendet, de nem tartottam be. No, mondom, ez \u00e9rdekes lesz, ezek szerint m\u00e9gse rendezetten adja vissza a neveket az NTFS? N\u00e9zz\u00fck a bibis v\u00e1lt\u00e1st:\r\n\r\n&amp;#91;source=&#039;c&#039;&amp;#93;\r\n11\/02\/2006  01:34 PM             6,656 blbres.dll\r\n11\/02\/2006  01:34 PM            17,408 blb_ps.dll\r\n&amp;#91;\/source&amp;#93;\r\n\r\nHopp\u00e1, igaza van, ez t\u00e9nyleg nem stimmel. WTF? N\u00e9mi gugliz\u00e1s ut\u00e1n f\u00e9ny der\u00fclt a titokra.\r\n&lt;a href=&quot;http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2005\/06\/17\/430194.aspx&quot;&gt;Why do NTFS and Explorer disagree on filename sorting?&lt;\/a&gt;\r\n&lt;a href=&quot;http:\/\/blogs.msdn.com\/michkap\/archive\/2005\/01\/16\/353873.aspx&quot;&gt;How &#x5B;case-]insensitive (apologies to Frank Sinatra)&lt;\/a&gt;\r\n\r\nAz els\u0151 linken Raymond Chen bar\u00e1tunk elmondja, hogy az NTFS nem a mi \u00f6r\u00f6m\u00fcnkre rendez, hanem a saj\u00e1t hat\u00e9konys\u00e1ga \u00e9rdek\u00e9ben, \u00edgy ne is v\u00e1rjuk t\u0151le, hogy majd mindenf\u00e9le nyelvi szab\u00e1lyok szerint rendezze a kimenetet, rendezi, ahogy neki j\u00f3. Amikor leform\u00e1zzuk a k\u00f6tetet, akkor kialakul egy sorrend, amit azt\u00e1n semmif\u00e9le locale v\u00e1lt\u00e1s m\u00e1r nem befoly\u00e1sol. Sz\u00f3val eset\u00fcnkben m\u00e1s az SQL Server \u00e1ltal elv\u00e1rt sorrend, mint amelyet az NTFS haszn\u00e1l rendez\u00e9shez. Ok, az NTFS fekete doboz, de milyen sorrendben gondolkodik az SQL Server ebben az esetben? M\u00e1sk\u00e9ppen fogalmazva, mi a TFV-\u00fcnk egyetlen oszlop\u00e1nak a COLLATION-je?\r\nA &lt;a href=&quot;http:\/\/msdn2.microsoft.com\/en-us\/library\/ms179886.aspx&quot;&gt;doksi&lt;\/a&gt; alapj\u00e1n Coercible-default, azaz &quot;... If the object is declared in a batch, the object is assigned the default collation of the current database for the connection.&quot;. Ez vonatkozik r\u00e1nk. Az adatb\u00e1zis SQL_Latin1_General_CP1_CI_AS collation-\u0171, ez az AdventureWorks alap\u00e9rtelmezett elmebeteg collation-je.\r\nHogyan lehetne m\u00e1s collation-t r\u00e1mondani a TFV kimenet\u00e9re? Mi sem egyszer\u0171bb:\r\n\r\n&#x5B;source=&#039;sql&#039;]\r\ncreate function DirList(@base nvarchar(300))\r\nreturns table \r\n(\r\n  FileName nvarchar(300) collate Latin1_General_CI_AS\r\n)\r\norder (FileName asc)\r\nas external name SqlClrTest.TVF.DirList\r\n<\/pre>\n<p>Ett\u0151l nem lesz j\u00f3 a f\u00fcggv\u00e9ny\u00fcnk, de l\u00e1tjuk, hogyan lehet collation-t megadni az oszlop defin\u00edci\u00f3ban. :)<br \/>\nA kis-nagybet\u0171 mappel\u00e9s az NTFS-ben a $UpCase nev\u0171 system metadata file-ban t\u00e1rol\u00f3dik, ha valaki megmutatja, hogyan kell ezt olvasni, megpr\u00f3b\u00e1lom kital\u00e1lni, van-e hozz\u00e1 ill\u0151 collation a szerverben.<\/p>\n<p>\u00d6sszegezve: ha a TVF \u00e1ltal publik\u00e1lt adatok elve valamilyen sorrend szerint vannak rendezve, akkor ezt a rendezetts\u00e9get kommunik\u00e1lhatjuk az SQL Server 2008 fel\u00e9, \u00edgy a rendez\u00e9seket, csoportos\u00edt\u00e1sokat vagy a distinct m\u0171veleteket jelent\u0151sen gyors\u00edthatjuk.<br \/>\nEmellett (nem trvi\u00e1lis \u00e9s kev\u00e9sb\u00e9 ismert inf\u00f3), ha egy t\u00e1bl\u00e1ba sz\u00farjuk be a TVF-b\u0151l kiszelekt\u00e1lt adatokat, \u00e9s a a c\u00e9lt\u00e1bla clu indexet tartalmaz, ami pont a TVF kimenet\u00e9nek megfelel\u0151 sorrendet \u00edr el\u0151, akkor sokkal gyorsabb lesz az insert. <a href=\"http:\/\/soci.hu\/blog\/index.php\/2007\/07\/04\/erdekes-insert-optimalizalas\/\">Err\u0151l m\u00e1r \u00edrtam egyszer<\/a>.<\/p>\n<p>De, hogy itt is l\u00e1ssuk a hat\u00e1st:<\/p>\n<pre class=\"brush: sql; title: ; notranslate\" title=\"\">\r\nif object_id(&#039;tempdb..#Powers&#039;) is not null drop table #Powers\r\n\r\ncreate table #Powers\r\n(\r\n  id int not null identity,\r\n  power float not null\r\n)\r\n\r\ncreate clustered index idx1 on #Powers(power);\r\ngo\r\n\r\ninsert #Powers\r\nselect power from dbo.Austin2(4000000, 1.001, 0, 100000)\r\noption (recompile)\r\n\r\ninsert #Powers\r\nselect power from dbo.Austin(4000000, 1.001, 0, 100000)\r\noption (recompile)\r\n<\/pre>\n<p>A t\u00e1bla pont \u00fagy van rendezve a clu index miatt, mint a TVF kimente. Emiatt a k\u00e9t insert v\u00e9grehajt\u00e1si terve is m\u00e1s lesz:<\/p>\n<p><a href='http:\/\/soci.hu\/blog\/wp-content\/uploads\/2008\/02\/insertfromtvforderedvsunordereddata.png' title='Besz\u00far\u00e1s rendezett \u00e9s rendezetlen alapadatokb\u00f3l' target='_blank'><img src='http:\/\/soci.hu\/blog\/wp-content\/uploads\/2008\/02\/insertfromtvforderedvsunordereddata.png' alt='Besz\u00far\u00e1s rendezett \u00e9s rendezetlen alapadatokb\u00f3l' \/><\/a><\/p>\n<p>Durva, mi? A Sort visz el igen jelent\u0151s k\u00f6lts\u00e9geket. Konkr\u00e9tan, a p\u00e9ldabeli sz\u00e1zezer sor eset\u00e9n 12.1 vs. 16.7 mp. Az els\u0151n\u00e9l a CPU 5.4mp, a m\u00e1sodikn\u00e1l 8mp volt. De ami nagyon fontos, \u00e9s ez nem l\u00e1tszik a profiler kimenetben, kutya sok mem\u00f3ria kell a sorbarendez\u00e9shez, ez\u00e9rt t\u00f6bbsz\u00f6r el is sz\u00e1llt hib\u00e1val a m\u00e1sodik insert, hogy adott id\u0151n bel\u00fcl nem tudott mem\u00f3ri\u00e1t kapni.<\/p>\n<p>Msg 8645, Level 17, State 1, Line 1<br \/>\nA timeout occurred while waiting for memory resources to execute the query in resource pool &#8216;default&#8217; (2). Rerun the query.<\/p>\n<p>Egy sz\u00f3 mint sz\u00e1z, becs\u00fclj\u00e9tek meg az eleve rendezett adatokat. :)<\/p>\n<p>Ps. <a href=\"http:\/\/blogs.msdn.com\/sqlqueryprocessing\/archive\/2006\/11\/14\/query-execution-timeouts-in-sql-server-part-1-of-2.aspx\">Az el\u0151bbi timeoutr\u00f3l inf\u00f3<\/a>, akit \u00e9rdekel.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A Table Valued Function-\u00f6kn\u00e9l eddig nem lehetett megadni inform\u00e1ci\u00f3t a szervernek a f\u00fcggv\u00e9nyb\u0151l j\u00f6v\u0151 adatok sorrendj\u00e9re, \u00edgy hi\u00e1ba j\u00f6ttek sorban az adatok,&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6,4,58],"tags":[],"class_list":["post-457","post","type-post","status-publish","format-standard","hentry","category-adatbazisok","category-szakmai-elet","category-sql-server-2008"],"_links":{"self":[{"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/posts\/457","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/comments?post=457"}],"version-history":[{"count":0,"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/posts\/457\/revisions"}],"wp:attachment":[{"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/media?parent=457"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/categories?post=457"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/tags?post=457"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}