Skip to content

Instantly share code, notes, and snippets.

@dhmacher
Last active September 13, 2022 07:05
    Show Gist options
    • Save dhmacher/42d4e44bf14a0b2f52fcb511b5657de8 to your computer and use it in GitHub Desktop.
    Save dhmacher/42d4e44bf14a0b2f52fcb511b5657de8 to your computer and use it in GitHub Desktop.
    /*
    Copyright Daniel Hutmacher under Creative Commons 4.0 license with attribution.
    http://creativecommons.org/licenses/by/4.0/
    Source: https://github.com/sqlsunday/sp_ctrl3
    DISCLAIMER: This script may not be suitable to run in a production
    environment. I cannot assume any responsibility regarding
    the accuracy of the output information, performance
    impacts on your server, or any other consequence. If
    your jurisdiction does not allow for this kind of
    waiver/disclaimer, or if you do not accept these terms,
    you are NOT allowed to store, distribute or use this
    code in any way.
    USAGE: SELECT * FROM dbo.Template_Split('Wingtip Toys (Bethel Acres, OK)', '% (%, %)%')
    RETURNS: Similar to STRING_SPLIT(), this inline table-value function
    returns the components in a string, as defined by the "%" wildcard
    in the second parameter of the function.
    VERSION: 2022-07-26
    */
    CREATE OR ALTER FUNCTION dbo.Template_Split(
    @string nvarchar(max),
    @pattern nvarchar(100)
    )
    RETURNS TABLE
    AS
    RETURN (
    --- First, let's turn this "%abc[def]ghi%" into "%abc_ghi%", so that we can
    --- compute the offsets generated by wildcard expressions like "[0-9]".
    WITH pat AS (
    SELECT @pattern AS _pattern
    UNION ALL
    SELECT CAST(STUFF(pat._pattern, x1._start_offset, x2._width, '_') AS nvarchar(100)) AS _pattern
    FROM pat
    OUTER APPLY (
    VALUES (NULLIF(CHARINDEX('[', _pattern), 0))
    ) AS x1(_start_offset)
    OUTER APPLY (
    VALUES (CHARINDEX(']', SUBSTRING(pat._pattern, x1._start_offset, LEN(pat._pattern))))
    ) AS x2(_width)
    WHERE pat._pattern LIKE '%[[]%]%'),
    --- Loop through each "%" in the pattern and its corresponding item in the string:
    cte AS (
    SELECT 0 AS position,
    CAST(NULL AS nvarchar(max)) AS [value],
    @string AS _string,
    @pattern AS _pattern,
    (SELECT _pattern FROM pat WHERE _pattern NOT LIKE '%[[]%]%') AS _plain_pattern
    UNION ALL
    SELECT position+1 AS position,
    (CASE WHEN x1._next_pattern='' THEN ISNULL(LEFT(cte._string, NULLIF(PATINDEX(cte._pattern, cte._string), 0)-1), cte._string)
    ELSE LEFT(cte._string, x1._offset-1) END) AS [value],
    SUBSTRING(cte._string, x1._offset+x1._delimiter_width-1, LEN(cte._string)) AS _string,
    x1._next_pattern AS _pattern,
    x1._next_plain_pattern AS _plain_pattern
    FROM cte
    CROSS APPLY (
    VALUES (--- Offset in the pattern string to the next "%":
    NULLIF(PATINDEX(LEFT(cte._pattern, CHARINDEX('%', SUBSTRING(cte._pattern, 2, 100))+1), cte._string), 0),
    --- Same offset, but in the plaintext string:
    CHARINDEX('%', SUBSTRING(cte._plain_pattern, 2, 100)+'%'),
    --- What the pattern will look for the next iteration:
    SUBSTRING(cte._pattern, CHARINDEX('%', SUBSTRING(cte._pattern, 2, 100)+'%')+1, 100),
    --- Same, but for the "plaintext pattern":
    SUBSTRING(cte._plain_pattern, CHARINDEX('%', SUBSTRING(cte._plain_pattern, 2, 100)+'%')+1, 100))
    ) AS x1(_offset, _delimiter_width, _next_pattern, _next_plain_pattern)
    WHERE cte._pattern IS NOT NULL
    AND x1._next_pattern!=''
    OR cte._string LIKE cte._pattern AND cte._string!='' AND cte._pattern!='')
    SELECT position, [value]
    FROM cte
    WHERE position>0
    );
    GO
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment