将连字符分隔的数据分成SQL Server中的多个列的最佳方法是什么?

时间:2015-02-12 19:30:52

标签: sql sql-server tsql

我使用的是SQL Server 2008 R2,我将数据格式AA-BB-CCCCCCCC-DDDDDDDD-EEEE存储在一列中。我需要用T-SQL内联将它分成5个单独的列(我不想为此目的创建一个函数,但是如果有严重的性能提升我会调查它,这里有权限问题我必须处理)。我正在创建一个视图来模仿另一台服务器上类似表的布局。

所以,我希望我的输出看起来像这样:

+------+------+----------+----------+------+
| Col1 | Col2 | Col3     | Col4     | Col5 |
+------+------+----------+----------+------+
| AA   | BB   | CCCCCCCC | DDDDDDDD | EEEE |
+------+------+----------+----------+------+

现在,我有一些有用的东西,但对我来说似乎完全没效率,在我的测试中对此视图执行查询非常耗时。我使用CTE和XML来打破列,但这需要诸如转义&符号之类的东西等。

所以,我现在拥有的是:

WITH cte (ColA, ColB, Colc, etc.)
AS
(
        SELECT 
            CONVERT(XML,'<Account><Attribute>' 
                + REPLACE(REPLACE(MY_COLUMN,'&','&amp;'),'-', '</Attribute><Attribute>') 
                + '</Attribute></Account>') as ACCOUNT_VALUE
)

SELECT
    ACCOUNT_VALUE.value('/Account[1]/Attribute[1]','varchar(2)') as Col1,
    ACCOUNT_VALUE.value('/Account[1]/Attribute[2]','varchar(2)') as Col2,
    ACCOUNT_VALUE.value('/Account[1]/Attribute[3]','varchar(8)') as Col3,
    ACCOUNT_VALUE.value('/Account[1]/Attribute[4]','varchar(8)') as Col4,
    ACCOUNT_VALUE.value('/Account[1]/Attribute[5]','varchar(4)') as Col5
FROM cte

这样可以很好地返回数据,但需要特别长的时间。那么,是否有更好的方法将带连字符的数据分成T-SQL语句中的列(最好没有函数等)?该表有数百万行需要分成不同的列。

我昨天通过几个小时的谷歌搜索得到了这一点,并没有真正找到我能够工作的另一种选择。

3 个答案:

答案 0 :(得分:1)

试试这个:

CREATE TABLE Test
(
  LongText VARCHAR(400)
)

INSERT INTO Test (LongText)
VALUES('AA-BB-CCCCCCCC-DDDDDDDD-EEEE'),
('BB-CC-DDDDDDDD-EEEEEEEE-FFFF')

;WITH CTE AS
(
    --initial part
    SELECT LongText, 1 AS ColNo, LEFT(LongText, CHARINDEX('-', LongText)-1) AS Part,
      RIGHT(LongText, LEN(LongText) - CHARINDEX('-', LongText)) AS Remainder
    FROM Test
    WHERE CHARINDEX('-', LongText)>0
    --recursive part, gets 'Part' till the last '-'
    UNION ALL
    SELECT LongText, ColNo + 1 AS ColNo,LEFT(Remainder, CHARINDEX('-', Remainder)-1) AS Part,
      RIGHT(Remainder, LEN(Remainder) - CHARINDEX('-', Remainder)) AS Remainder
    FROM CTE
    WHERE CHARINDEX('-', Remainder)>0
    --recursive part, gets the last 'Part' (there is no '-')
    UNION ALL
    SELECT LongText, ColNo + 1 AS ColNo,Remainder AS Part,NULL AS Remainder
    FROM CTE
    WHERE CHARINDEX('-', Remainder)=0
)
SELECT [1],[2],[3],[4],[5]
FROM (
    SELECT LongText, ColNo, Part
    FROM CTE
  ) AS DT
PIVOT(MAX(Part) FOR ColNo IN ([1],[2],[3],[4],[5])) AS PT

SQL Fiddle

答案 1 :(得分:0)

如果您知道您的数据有设定的长度,您可以使用以下内容:

Declare @value as Varchar(50);
Set @value = 'AA-BB-CCCCCCCC-DDDDDDDD-EEEE'
Select left(@value, 2) as col1, SUBSTRING(@value, 4, 2) as col2, 
       SUBSTRING (@value, 7, 8) as col3

等。另外,你可以使用类似的模式,使用charindex()对&#39; - &#39;用于定义子字符串的起点和终点的字符。 Maciej的方法与此类似。

答案 2 :(得分:0)

如果你总是有5个部分,这种方法可能比XML处理更快:

select 
  left(MY_COLUMN, P1.P1-1) as PART1,
  substring(MY_COLUMN, P1.P1+1,P2.P2-P1.P1-1) as PART2,
  substring(MY_COLUMN, P2.P2+1,P3.P3-P2.P2-1) as PART3,
  substring(MY_COLUMN, P3.P3+1,P4.P4-P3.P3-1) as PART4,
  substring(MY_COLUMN, P4.P4+1,8000) as PART5
from 
  MY_TABLE
  cross apply (select charindex('-', MY_COLUMN) as P1) P1
  cross apply (select charindex('-', MY_COLUMN, P1.P1+1) as P2) P2
  cross apply (select charindex('-', MY_COLUMN, P2.P2+1) as P3) P3
  cross apply (select charindex('-', MY_COLUMN, P3.P3+1) as P4) P4
  cross apply (select charindex('-', MY_COLUMN, P4.P4+1) as P5) P5