zoukankan      html  css  js  c++  java
  • 字符串分割

    CREATE FUNCTION [dbo].[DelimitedSplit8K]
    --===== Define I/O parameters
            (@pString VARCHAR(8000), @pDelimiter CHAR(1))
    --WARNING!!! DO NOT USE MAX DATA-TYPES HERE!  IT WILL KILL PERFORMANCE!
    RETURNS TABLE WITH SCHEMABINDING AS
     RETURN
    --===== "Inline" CTE Driven "Tally Table" produces values from 1 up to 10,000...
         -- enough to cover VARCHAR(8000)
      WITH E1(N) AS (
                     SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
                     SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
                     SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
                    ),                          --10E+1 or 10 rows
           E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
           E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
     cteTally(N) AS (--==== This provides the "base" CTE and limits the number of rows right up front
                         -- for both a performance gain and prevention of accidental "overruns"
                     SELECT TOP (ISNULL(DATALENGTH(@pString),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
                    ),
    cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter)
                     SELECT 1 UNION ALL
                     SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(@pString,t.N,1) = @pDelimiter
                    ),
    cteLen(N1,L1) AS(--==== Return start and length (for use in substring)
                     SELECT s.N1,
                            ISNULL(NULLIF(CHARINDEX(@pDelimiter,@pString,s.N1),0)-s.N1,8000)
                       FROM cteStart s
                    )
    --===== Do the actual split. The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found.
     SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1),
            Item       = SUBSTRING(@pString, l.N1, l.L1)
       FROM cteLen l

     test

    --=====================================================================================================================
    -- TEST 1:
    -- This tests for various possible conditions in a string using a comma as the delimiter.  The expected results are
    -- laid out in the comments
    --=====================================================================================================================
    --===== Conditionally drop the test tables to make reruns easier for testing.
         -- (this is NOT a part of the solution)
         IF OBJECT_ID('tempdb..#JBMTest') IS NOT NULL DROP TABLE #JBMTest
    ;
    --===== Create and populate a test table on the fly (this is NOT a part of the solution).
         -- In the following comments, "b" is a blank and "E" is an element in the left to right order.
         -- Double Quotes are used to encapsulate the output of "Item" so that you can see that all blanks
         -- are preserved no matter where they may appear.
     SELECT *
       INTO #JBMTest
       FROM (                                               --# & type of Return Row(s)
             SELECT  0, NULL                      UNION ALL --1 NULL
             SELECT  1, SPACE(0)                  UNION ALL --1 b (Empty String)
             SELECT  2, SPACE(1)                  UNION ALL --1 b (1 space)
             SELECT  3, SPACE(5)                  UNION ALL --1 b (5 spaces)
             SELECT  4, ','                       UNION ALL --2 b b (both are empty strings)
             SELECT  5, '55555'                   UNION ALL --1 E
             SELECT  6, ',55555'                  UNION ALL --2 b E
             SELECT  7, ',55555,'                 UNION ALL --3 b E b
             SELECT  8, '55555,'                  UNION ALL --2 b B
             SELECT  9, '55555,1'                 UNION ALL --2 E E
             SELECT 10, '1,55555'                 UNION ALL --2 E E
             SELECT 11, '55555,4444,333,22,1'     UNION ALL --5 E E E E E 
             SELECT 12, '55555,4444,,333,22,1'    UNION ALL --6 E E b E E E
             SELECT 13, ',55555,4444,,333,22,1,'  UNION ALL --8 b E E b E E E b
             SELECT 14, ',55555,4444,,,333,22,1,' UNION ALL --9 b E E b b E E E b
             SELECT 15, ' 4444,55555 '            UNION ALL --2 E (w/Leading Space) E (w/Trailing Space)
             SELECT 16, 'This,is,a,test.'                   --E E E E
            ) d (SomeID, SomeValue)
    ;
    --===== Split the CSV column for the whole table using CROSS APPLY (this is the solution)
     SELECT test.SomeID, test.SomeValue, split.ItemNumber, Item = QUOTENAME(split.Item,'"')
       FROM #JBMTest test
      CROSS APPLY dbo.DelimitedSplit8k(test.SomeValue,',') split
    ;

    Epilogue

    Make no doubt about it. The current best way to split delimited strings in SQL Server is to use a CLR splitter. However, if, for whatever reason, you cannot use a CLR splitter, the new DelimitedSplit8K function provides a close second with both linear and stable performance across a wide range of string and individual element size. And, I promise, I didn't include any dust bunnies in the code. ;-)

    Thanks for listening folks.

    --Jeff Moden

    另一种方法:

    CREATE FUNCTION dbo.Split (@sep char(1), @s varchar(max))
    RETURNS table
    AS
    RETURN (
        WITH Pieces(pn, start, stop) AS (
          SELECT 1, 1, CHARINDEX(@sep, @s)
          UNION ALL
          SELECT pn + 1, stop + 1, CHARINDEX(@sep, @s, stop + 1)
          FROM Pieces
          WHERE stop > 0
        )
        SELECT pn,
          SUBSTRING(@s, start, CASE WHEN stop > 0 THEN stop-start ELSE len(@s) END) AS s
        FROM Pieces
      )
  • 相关阅读:
    java new 关键字到底做了什么?
    (转载)Eclipse中使用SVN
    图标常用网站
    正则表达式之RegExp对象
    表单验证之日期大小验证
    表单验证之正则表达式
    表单验证之JQuery Validate控件
    (转载)SVN使用说明
    oracle中group by 和order by同时存在时
    Oracle用户密码过期问题解决
  • 原文地址:https://www.cnblogs.com/luhe/p/7991469.html
Copyright © 2011-2022 走看看