2013-06-17 3 views
1

У меня есть столбец (full_location_id) в таблице, содержащей строку, которая разделена на «-», которую мне нужно разделить на 4 столбца в представлении (Test_SplitColumn). Не каждая запись в full_location_id содержит ту же длину идентификаторов. У некоторых могут быть такие идентификаторы, как A1-BF-35-B1, в то время как другие могут просто иметь 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 

Любая помощь или руководство будут оценены.

+0

Это было бы гораздо лучше разрешено в обратном направлении. Имейте 4 столбца в таблице и столбец view/compute, чтобы отобразить 4 объединенную в виде строки с разделителями. – GarethD

+0

Мне удалось решить мою проблему, используя функцию, найденную здесь: http://www.itdeveloperzone.com/2012/03/find-nth-occurrence-of-character-sql.html вместе с некоторой логикой случая в коде SQL который создал мое мнение. – dspencer

ответ

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          

МАХ (СЛУЧАЙ ....) Выражения поворотного трюк обратно в SQL SERVER 2000 земли. Я никогда не видела оператора PIVOT.

SQL Скрипки: http://sqlfiddle.com/#!3/80f74/1

12

Вот быстрый и простой способ:

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 ║ 
╚══════════════════╩══════╩══════╩══════╩══════╝ 

И here is an sqlfiddle с демо.

+0

Ницца - думал о злоупотреблении PARSENAME, но это всегда заставляет меня нервничать! –

+0

Но PARSENAME не может обрабатывать более 4 значений. –

+0

@TabAlleman Да, но это требование: «Мне нужно разделить на 4 столбца в представлении» – Lamak

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;