4

我在一个表中有一个列 ( ),其中包含一个由“-”分隔的字符串,我需要在视图 ( )full_location_id中将其拆分为 4 列。Test_SplitColumn并非每条记录都full_location_id包含相同长度的 id。有些可能有诸如 A1-BF-35-B1 之类的 id,而另一些可能只有 AR-B3。我不确定这样做的最佳方式。我能够检索第一列,但到目前为止不是其他列。

CREATE VIEW [dbo].[Test_SplitColumn]
AS
select p.name, location.aisle_id, location.full_location_id, SUBSTRING(location.full_location_id, 0,charindex('-', location.full_location_id )) as Aisle,
SUBSTRING(location.full_location_id, charindex('-', location.full_location_id ) + 1, charindex('-', location.full_location_id, LEN(SUBSTRING(location.full_location_id, 0,charindex('-', location.full_location_id ))) )) as ShelvingUnit
from location 
inner join product p on p.id = location.product_id
GO

任何帮助或指导将不胜感激。

4

3 回答 3

14

这是一个快速、简单的方法:

DECLARE @T TABLE(full_location_id varchar(100));

INSERT INTO @T 
VALUES  ('A1-BF-35-B1'),
        ('AR-B3');

WITH CTE AS
(
    SELECT  full_location_id,
            LEN(full_location_id)-LEN(REPLACE(full_location_id,'-','')) N
    FROM @T
)
SELECT  full_location_id,
        PARSENAME(REPLACE(full_location_id,'-','.'),N+1),
        PARSENAME(REPLACE(full_location_id,'-','.'),N),
        PARSENAME(REPLACE(full_location_id,'-','.'),N-1),
        PARSENAME(REPLACE(full_location_id,'-','.'),N-2)
FROM CTE

结果:

╔══════════════════╦══════╦══════╦══════╦══════╗
║ full_location_id ║ Col1 ║ Col2 ║ Col3 ║ Col4 ║
╠══════════════════╬══════╬══════╬══════╬══════╣
║ A1-BF-35-B1      ║ A1   ║ BF   ║ 35   ║ B1   ║
║ AR-B3            ║ AR   ║ B3   ║ NULL ║ NULL ║
╚══════════════════╩══════╩══════╩══════╩══════╝

这是一个带有演示的sqlfiddle 。

于 2013-06-17T15:20:54.663 回答
3

这是您的模型的失败。与其将位置存储为分隔字符串,不如创建一个 1-n 表来存储位置。事实上,您问题的正确“答案”可能是“重新设计这部分数据库!”

但是,要做你想做的事,你可以做这样的事情:

USE tempdb
GO

/* udfSplit (A Fast String Splitter) **************************************************************
 *
 * Uses a number table to *very* quickly split the text (@text). Splits on the delimiter (@d)
 * Returns Table of ( [RowID], [SplitText] ). Inlineable for CROSS APPLY etc.
 *
 * Charlie
 *
 *************************************************************************************************/
CREATE FUNCTION [dbo].[udfSplit] (@text NVARCHAR(4000), @d NVARCHAR(50))
RETURNS TABLE AS RETURN (
WITH numbers(n) AS (
SELECT ROW_NUMBER() OVER (ORDER BY a.[n])
  FROM
             ( VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9) ) AS  a ([n])
  CROSS JOIN ( VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9) ) AS  b ([n])
  CROSS JOIN ( VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9) ) AS  c ([n])
  CROSS JOIN ( VALUES (0), (1), (2), (3), (4)) AS  d ([n])
  )
SELECT
      [RowID] = ROW_NUMBER() OVER ( ORDER BY [n] ASC )
    , [SplitText] = SUBSTRING(
        @d + @text + @d
        , [n] + LEN(@d)
        , CHARINDEX(@d, @d + @text + @d, [n] + LEN(@d)) - [n] - LEN(@d)
        )
FROM numbers AS n
WHERE [n] <= LEN(@d + @text + @d) - LEN(@d)
  AND SUBSTRING(@d + @text + @d, [n], LEN(@d)) = @d
)
GO

IF OBJECT_ID('tempdb..#sample') IS NOT NULL DROP TABLE #sample
GO

CREATE TABLE #sample (
      name VARCHAR(255)
    , locations VARCHAR(MAX)
    )

INSERT #sample (name, locations)    
VALUES ('a', 'ab-cd')
     , ('b', 'ab-cd-ef')
     , ('c', 'gh')
     , ('d', NULL)

; WITH SPLIT AS (
    SELECT [name], l.*
    FROM #sample AS s
    OUTER APPLY dbo.[udfSplit](s.locations,'-') AS l
    )
SELECT
      s.name
    , MAX(CASE WHEN s.rowId = 1 THEN s.SplitText ELSE '' END) AS a
    , MAX(CASE WHEN s.rowId = 2 THEN s.SplitText ELSE '' END) AS b
    , MAX(CASE WHEN s.rowId = 3 THEN s.SplitText ELSE '' END) AS c
    , MAX(CASE WHEN s.rowId = 4 THEN s.SplitText ELSE '' END) AS d
FROM
    SPLIT AS s
GROUP BY
    s.name

这可能看起来超级复杂。udfSplit 函数是一个非常快速的字符串拆分器——它将您的分隔字符串转换为一个返回位置 (1-4) 和拆分字符串的表。除非你真的想进入它,否则不要担心它是如何工作的。如果您确实想了解如何在数据库中拆分字符串(以及为什么这是一个糟糕的计划)——请阅读此处:

http://www.sqlservercentral.com/articles/Tally+Table/72993/

其余代码组成一个示例表,然后对其进行选择以获得您想要的输出:

(4 row(s) affected)
name                 a     b     c     d
-------------------- ----- ----- ----- -----
a                    ab    cd          
b                    ab    cd    ef    
c                    gh                
d                                      

MAX(CASE....) 表达式是 sql server 2000 领域的一个重要技巧。我从来没有掌握过 PIVOT 操作员的窍门。

SQL 小提琴: http ://sqlfiddle.com/#!3/80f74/1

于 2013-06-17T15:17:50.190 回答
0

这是一种更通用的方法。这假设您知道您将拥有的最大列数。

CREATE TABLE #tmp (
    full_location_id varchar(255) );

INSERT INTO dbo.#tmp VALUES ( 'A1-BF-35-B1' );
INSERT INTO dbo.#tmp VALUES ( 'AR-B3' );
INSERT INTO dbo.#tmp VALUES ( 'A1-BF-35' );
INSERT INTO dbo.#tmp VALUES ( 'A1' );


with tmp( full_location_id, c, position, single ) as (
select #tmp.full_location_id
     , STUFF( #tmp.full_location_id, 1, CHARINDEX('-', #tmp.full_location_id + ' -'), '') AS c
     , 1 AS position
     , convert(nvarchar(max),left(#tmp.full_location_id, CHARINDEX('-', #tmp.full_location_id + ' -') -1)) AS single
  from #tmp
 union all
select full_location_id
     , STUFF(c, 1, CHARINDEX('-', c + ' -'), '')
     , position + 1
     , convert(nvarchar(max),left(c, CHARINDEX('-', c + ' -') -1))
  from tmp
 where c > ''
)

SELECT pvt.full_location_id
     , [1]
     , [2]
     , [3]
     , [4]
 FROM 
( SELECT full_location_id
       , single
       , position
    FROM tmp ) AS src
PIVOT
(
    MAX( single )
    FOR position IN ( [1], [2], [3], [4] )

) AS pvt;
于 2013-06-17T16:19:07.857 回答