{"id":872,"date":"2011-09-09T20:20:04","date_gmt":"2011-09-10T01:20:04","guid":{"rendered":"http:\/\/anvil-of-time.com\/wordpress\/?p=872"},"modified":"2021-03-10T20:53:42","modified_gmt":"2021-03-11T01:53:42","slug":"joining-to-a-table-function-in-sql-server","status":"publish","type":"post","link":"https:\/\/anvil-of-time.com\/powerbuilder\/joining-to-a-table-function-in-sql-server\/","title":{"rendered":"Joining to a Table Function in SQL Server"},"content":{"rendered":"<p>Following up on my <a title=\"Splitting up a String or Text in SQL\" href=\"http:\/\/anvil-of-time.com\/wordpress\/powerbuilder\/splitting-up-a-string-or-text-in-sql\/\" target=\"_blank\" rel=\"noopener\">previous post<\/a>, SQL Server (since version 2005 while in SQL 90 compatibility mode) provides the ability to join to the table created by a parameterized table function. In earlier versions you were not able to use a table function with a dynamic parameter or even join to it. If you needed this functionality your best bet was a stored procedure.<\/p>\n<p>So in my case I was refactoring some code to correct for a PowerBuilder datawindow issue and needed to split up a block of text from a table into individual rows based on the carriage return \/ linefeed codes contained within it. Since I needed additional data from another table to properly group the data I came up with the following as a datasource for the datawindow.<\/p>\n<pre name=\"code\" class=\"sql\">SELECT\tsn.Item\nFROM \t\tdbo.Customer\nLEFT JOIN\tdbo.CustomerNotes ON dbo.Customer.cust_id = dbo.CustomerNotes.cust_id\nCROSS APPLY dbo.uft_splitnotes(dbo.CustomerNotes.note_txt, &#039;[CRLF]&#039;) sn \nWHERE dbo.Customer.cust_id = 1<\/pre>\n<p>Note the use of CROSS APPLY which functions like an INNER JOIN. To simulate a LEFT JOIN use OUTER APPLY.<\/p>\n<p>The following script snippits can be run against your database to demonstrate the use of this technique. I am duplicating the table function script from my previous post for the sake of completeness.<\/p>\n<pre name=\"code\" class=\"sql\">CREATE TABLE Customer (\ncust_id Int\n,cust_name nvarchar(100)\n)\n\nCREATE TABLE CustomerNotes (\nnote_id int\n,cust_id INT\n,note_dt datetime\n,note_txt nvarchar(max)\n)\n\nINSERT INTO Customer (\ncust_id, cust_name)\nSELECT 1, &#039;Koala Quarries&#039;\n\nINSERT INTO Customer (\ncust_id, cust_name)\nSELECT 2, &#039;Ironclad Aluminum&#039;\n\n\nINSERT INTO CustomerNotes (\nnote_id, cust_id, note_dt, note_txt)\nSELECT 1, 1, Getdate(), &#039;I spoke with&#039; + CHAR(13) + CHAR(10) + &#039;Jim to discuss the June shipment.&#039; + CHAR(13) + CHAR(10) + &#039;Will follow up on Monday.&#039;\n\n\nCREATE FUNCTION [dbo].[uft_splitnotes] (@StringArray NVARCHAR(MAX),\n                                        @Delimiter   NVARCHAR(30))\nRETURNS @Results TABLE \n(\n  SeqNo INT IDENTITY(1, 1),\n  Item  NVARCHAR(MAX)\n)\nAS\n  BEGIN\n      DECLARE @Next INT\n      DECLARE @lenStringArray INT\n      DECLARE @lenDelimiter INT\n      DECLARE @ii INT\n      --initialise everything\n      IF UPPER(@Delimiter) = &#039;SPACE&#039;\n        BEGIN\n            SET @Delimiter = &#039; &#039;\n        END\n      IF UPPER(@Delimiter) = &#039;[CR]&#039;\n        BEGIN\n            SET @Delimiter = CHAR(13)\n        END\n      IF UPPER(@Delimiter) = &#039;[LF]&#039;\n        BEGIN\n            SET @Delimiter = CHAR(10)\n        END\n      IF UPPER(@Delimiter) = &#039;[CRLF]&#039;\n        BEGIN\n            SET @Delimiter = CHAR(13) + CHAR(10)\n        END\n      IF UPPER(@Delimiter) = &#039;CHAR(13)&#039;\n        BEGIN\n            SET @Delimiter = CHAR(13)\n        END\n      IF UPPER(@Delimiter) = &#039;CHAR(10)&#039;\n        BEGIN\n            SET @Delimiter = CHAR(10)\n        END\n      IF @Delimiter = &#039;CHAR(13) + CHAR(10)&#039;\n        BEGIN\n            SET @Delimiter = CHAR(13) + CHAR(10)\n        END\n\n      SELECT @ii = 1,\n             @lenStringArray = LEN(REPLACE(@StringArray, &#039; &#039;, &#039;|&#039;)),\n             @lenDelimiter = LEN(REPLACE(@Delimiter, &#039; &#039;, &#039;|&#039;))\n      --notice we have to be cautious about LEN with trailing spaces!\n      --while there is more of the string\u2026\n      WHILE @ii &lt;= @lenStringArray\n        BEGIN--find the next occurrence of the delimiter in the stringarray\n            SELECT @Next = CHARINDEX(@Delimiter, @StringArray + @Delimiter, @ii)\n\n            INSERT INTO @Results\n                        (Item)\n            SELECT SUBSTRING(@StringArray, @ii, @Next - @ii)\n            --note that we can get all the items from the list by appeending a\n            --delimiter to the final string\n            SELECT @ii = @Next + @lenDelimiter\n        END\n      RETURN\n  END \n  \n---- selects to show the data from above\nselect * from CustomerNotes\n\nSELECT\tdbo.Customer.cust_id\n\t\t, dbo.Customer.cust_name\n\t\t, dbo.CustomerNotes.note_dt\n\t\t, sn.Item\nFROM \t\tdbo.Customer\nLEFT JOIN\tdbo.CustomerNotes ON dbo.Customer.cust_id = dbo.CustomerNotes.cust_id\nOUTER APPLY dbo.uft_splitnotes(dbo.CustomerNotes.note_txt, &#039;[CRLF]&#039;) sn \n\nSELECT\tdbo.Customer.cust_id\n\t\t, dbo.Customer.cust_name\n\t\t, dbo.CustomerNotes.note_dt\n\t\t, sn.Item\nFROM \t\tdbo.Customer\nLEFT JOIN\tdbo.CustomerNotes ON dbo.Customer.cust_id = dbo.CustomerNotes.cust_id\nCROSS APPLY dbo.uft_splitnotes(dbo.CustomerNotes.note_txt, &#039;[CRLF]&#039;) sn<\/pre>\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Following up on my previous post, SQL Server (since version 2005 while in SQL 90 compatibility mode) provides the ability to join to the table created by a parameterized table function. In earlier versions you were not able to use a table function with a dynamic parameter or even join to it. If you needed&hellip;<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[8,3,10],"tags":[12,16,15],"_links":{"self":[{"href":"https:\/\/anvil-of-time.com\/wp-json\/wp\/v2\/posts\/872"}],"collection":[{"href":"https:\/\/anvil-of-time.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/anvil-of-time.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/anvil-of-time.com\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/anvil-of-time.com\/wp-json\/wp\/v2\/comments?post=872"}],"version-history":[{"count":6,"href":"https:\/\/anvil-of-time.com\/wp-json\/wp\/v2\/posts\/872\/revisions"}],"predecessor-version":[{"id":1979,"href":"https:\/\/anvil-of-time.com\/wp-json\/wp\/v2\/posts\/872\/revisions\/1979"}],"wp:attachment":[{"href":"https:\/\/anvil-of-time.com\/wp-json\/wp\/v2\/media?parent=872"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/anvil-of-time.com\/wp-json\/wp\/v2\/categories?post=872"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/anvil-of-time.com\/wp-json\/wp\/v2\/tags?post=872"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}