2017-08-08 112 views
0

SQL很新,但我需要一些幫助,我相信這是一個簡單的修復。SQL Sting拆分爲單列

我在名爲'Produce'的表內有一列數據,其中水果的類型存儲在名爲'Fruit'的列中。此列中的某些值由逗號分隔。

有沒有一種簡單的方法來分割下面的結果,使結果成爲單列唯一條目?

E.g.示例表

Fruit 
----- 
Apple 
Plum 
Pear, Mango 
Pear 

什麼我希望能回報是以下:

Fruit 
----- 
Apple 
Plum 
Pear 
Mango 

我曾嘗試使用字符串分割功能,但我想我有它徹底。任何人都可以幫助提供一些關於如何做到這一點的解釋嗎?如果有幫助,我正在使用T-SQL。

在此先感謝。

+0

T-SQL在SQL SERVER?什麼版本? – xQbert

+1

[將逗號分隔的字符串變成單個行]可能的重複(https://stackoverflow.com/questions/5493510/turning-a-comma-separated-string-into-individual-rows) – xQbert

回答

0

要解決的核心問題是停止將值存儲爲以逗號分隔的列表。保持數據正常化。有了這樣說......每個人都需要一個良好的分離器...

declare @table table (Fruit varchar(64)) 
insert into @table 
values 
('Apple'), 
('Plum'), 
('Pear,Mango'), 
('Pear') 

select distinct 
    Item 
from 
    @table 
cross apply 
    dbo.DelimitedSplit8K(Fruit,',') 

或者,如果你的SQL Server 2016上...

select distinct 
    Item 
from 
    @table 
cross apply 
    string_split(Fruit,',') 

中的作用

SET ANSI_NULLS ON 
GO 

SET QUOTED_IDENTIFIER ON 
GO 

CREATE FUNCTION [dbo].[DelimitedSplit8K] (@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 
; 
GO 

Jeff Moden Article for Function

0

這可以用純SQL來完成,不需要用戶編寫的函數。

SQL服務器

WITH 
    fruittable 
    AS 
     ( SELECT 'Apple' fruit, 1 id 
     UNION ALL 
      SELECT 'Banana,Apple', 2 
     UNION ALL 
      SELECT 'Tomato,Grapefruit,Apple', 3 
     UNION ALL 
      SELECT 'Watermelon,Persimmons', 4 
     ), 
    split (fruit, id, leftover) 
    AS 
     (SELECT case when len(fruit) = 0 or fruit is null then null else left(fruit + ',', charindex(',',fruit + ',') -1) end AS fruit 
       , id 
       , case when len(fruit) = 0 or fruit is null then null else right(fruit + ',', len(fruit) - charindex(',',fruit + ',') + 1) end as leftover 
      FROM fruittable 
     UNION ALL 
     SELECT case when len(leftover) = 0 or leftover is null then null else left(leftover, charindex(',',leftover) - 1) end AS fruit 
       , id 
       , case when len(leftover) = 0 or leftover is null then null else substring(leftover, charindex(',',leftover) + 1, len(leftover)) end as leftover 
      FROM split 
      WHERE fruit IS NOT NULL) 
SELECT fruit, id 
    FROM split where fruit is not null 
    order by fruit, id; 

甲骨文

WITH 
    fruittable 
    AS 
     (SELECT 'Apple' fruit, 1 id 
      FROM DUAL 
     UNION ALL 
     SELECT 'Banana,Apple', 2 
      FROM DUAL 
     UNION ALL 
     SELECT 'Tomato,Grapefruit,Apple', 3 
      FROM DUAL 
     UNION ALL 
     SELECT 'Watermelon,Persimmons', 4 
      FROM DUAL), 
    split (fruit, id, leftover) 
    AS 
     (SELECT SUBSTR (fruit || ',', 1, INSTR (fruit || ',', ',') - 1) AS fruit 
       , id 
       , SUBSTR (fruit || ',', INSTR (fruit || ',', ',') + 1) AS leftover 
      FROM fruittable 
     UNION ALL 
     SELECT SUBSTR (leftover, 1, INSTR (leftover, ',') - 1) AS fruit 
       , id 
       , SUBSTR (leftover, INSTR (leftover, ',') + 1) AS leftover 
      FROM split 
      WHERE fruit IS NOT NULL) 
    SELECT fruit, id 
    FROM split 
    WHERE fruit IS NOT NULL 
ORDER BY fruit, id