【问题标题】:What is the best way to separate hyphen-delimited data into several columns in SQL Server?在 SQL Server 中将连字符分隔的数据分成几列的最佳方法是什么?
【发布时间】:2015-04-13 16:51:25
【问题描述】:

我使用的是 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 语句中的列(最好没有函数等)?此表有数百万行需要分成单独的列。

我昨天通过几个小时的谷歌搜索走了这么远,但还没有真正找到我能够工作的另一个选择。

【问题讨论】:

  • 正如我在问题中所说,我想避免使用函数或存储过程,因为我没有创建这些的权限。如果没有更好的方法,我可能会开始削减繁文缛节以通过它。

标签: sql sql-server tsql


【解决方案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

【讨论】:

  • 不错的解决方案!您可能想在您的recursive cte 中解释double unions
【解决方案2】:

如果你知道你的数据有一个固定的长度,你可以使用类似的东西:

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() 的结果来定义子字符串的起点和终点。 Maciej 的做法与此类似。

【讨论】:

    【解决方案3】:

    如果你总是有 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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-09-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多