标准化sql字符串组合

时间:2019-01-31 17:54:56

标签: sql sql-server tsql

我有下表:

CREATE TABLE #Fruits
( 

    Fruits VARCHAR(100)
) 

INSERT INTO #Fruits (Fruits)
VALUES ( 'banana,apple'),
       ('apple,banana'),
       ('kiwi,jackfruit'),
       ('jackfruit, kiwi')


banana,apple
apple,banana
kiwi,jackfruit
jackfruit, kiwi

我想再增加一列,以每行中用逗号分隔的值并将它们按字母顺序重新排列。我试图对值进行标准化,因为出于我的目的,香蕉香蕉香蕉香蕉是相同的东西。而猕猴桃,菠萝蜜菠萝蜜,猕猴桃是一样的东西。输出应如下所示:

Fruits                Normalized_Fruits
banana,apple          apple,banana
apple,banana          apple,banana
kiwi,jackfruit        jackfruit, kiwi
jackfruit, kiwi       jackfruit, kiwi

我怎样才能达到预期的效果?

5 个答案:

答案 0 :(得分:5)

我对string_split的最大抱怨之一是它缺少每个值的序数位置。这使得这种情况变得更容易处理。这是另一种解决方法。我正在使用Jeff Moden的分离器,该分离器可以在here中找到。确实不需要在这里放置光标。

我还随意添加了GroupID列,因此一旦解析出它们,您就知道每个值属于哪一行。如果“水果”列是唯一的,则可以跳过该列,但很难确定。

CREATE TABLE #Fruits
( 
    GroupID int identity
    , Fruits VARCHAR(100)
) 

INSERT INTO #Fruits (Fruits)
VALUES ( 'banana,apple'),
       ('apple,banana'),
       ('kiwi,jackfruit'),
       ('jackfruit, kiwi')

;
with SortedResults as
(
    select f.GroupID
        , Item = ltrim(x.Item)
        , x.ItemNumber
        , RowNum = ROW_NUMBER() over(partition by GroupID order by ltrim(x.Item))
    from #Fruits f
    cross apply dbo.DelimitedSplit8K(f.Fruits, ',') x
)

select Max(case when RowNum = 1 then Item end) + ', ' + max(case when RowNum = 2 then Item end)
from SortedResults
group by GroupID

drop table #Fruits

答案 1 :(得分:2)

试一下...我可能会被非光标族钉住,但这就是我想出的。

    CREATE TABLE #Fruits
( 

    Fruits VARCHAR(100)
) 

INSERT INTO #Fruits (Fruits)
VALUES ( 'banana,apple'),
       ('apple,banana'),
       ('kiwi,jackfruit'),
       ('jackfruit, kiwi')


Declare @tblFruit Table (Fruit1 varchar(100))
Declare @tblFruitSorted Table (FruitSorted varchar(100))

Declare fCursor Cursor For
Select Fruits From #Fruits

Declare @Fruitunsorted varchar(100), @FruitSorted Varchar(100) = ''

Open fCursor

Fetch Next From fCursor Into @Fruitunsorted

While @@FETCH_STATUS = 0
BEGIN

Set @FruitSorted = ''

Insert Into @tblFruit
Select * From string_split(@Fruitunsorted,',')

Update @tblFruit Set Fruit1 = Ltrim(Rtrim(Fruit1))

Select @FruitSorted = @FruitSorted + ',' + Ltrim(Rtrim(Fruit1)) From @tblFruit Order by Fruit1

Delete From @tblFruit

Insert Into @tblFruitSorted
Select Substring(@FruitSorted,2, LEN(@FruitSorted)-1)

Fetch Next From fCursor into @Fruitunsorted
END

Close fCursor
Deallocate fCursor

Select * From @tblFruitSorted

Drop Table #Fruits

答案 2 :(得分:1)

如果您使用的是SQL 2017或更高版本:

SELECT f.Fruits
    ,STRING_AGG(RTRIM(LTRIM(s.[value])),',') WITHIN GROUP (ORDER BY RTRIM(LTRIM(s.[value])))
FROM #Fruits f CROSS APPLY STRING_SPLIT(f.Fruits,',') s
GROUP BY f.Fruits
;

如果您使用的是旧版SQL(例如2008):

IF OBJECT_ID('tempdb..#Fruits') IS NOT NULL DROP TABLE #Fruits;
CREATE TABLE #Fruits(Fruits VARCHAR(100));
INSERT INTO #Fruits (Fruits) VALUES 
    ('banana,apple'),
    ('apple,banana'),
    ('kiwi,jackfruit'),
    ('jackfruit, kiwi')
;

;WITH Split AS (
    SELECT DISTINCT a.Fruits,RTRIM(LTRIM(tbl.col.value ('@Value', 'nvarchar(max)'))) AS [Fruit]
    FROM (SELECT f.Fruits,CONVERT(XML,'<N Value="' + REPLACE(f.Fruits,',','"></N><N Value="') + '"></N>') AS [x] FROM #Fruits f) a
    CROSS APPLY a.x.nodes('//N') AS tbl (col)
)
SELECT r.Fruits,STUFF((SELECT ',' + s.Fruit FROM Split s WHERE s.Fruits = r.Fruits ORDER BY s.Fruit FOR XML PATH(''),TYPE).value('(./text())[1]','VARCHAR(MAX)'),1,1,'') AS [NormalizedFruits]
FROM #Fruits r
;

IF OBJECT_ID('tempdb..#Fruits') IS NOT NULL DROP TABLE #Fruits;

答案 3 :(得分:1)

由于我们只在谈论一个用逗号分隔的两个纯字母字符串列表,因此我将PARSENAME再次抛出以求得简洁明了。由于源数据中空格的使用不一致,因此存在修剪,ELSE可能会更短,但我希望结果保持一致。

SELECT 
    Fruits
    ,CASE 
        WHEN LTRIM(RTRIM(PARSENAME(REPLACE(Fruits,',','.'),2))) > LTRIM(RTRIM(PARSENAME(REPLACE(Fruits,',','.'),1)))
        THEN LTRIM(RTRIM(PARSENAME(REPLACE(Fruits,',','.'),1))) + ', ' + LTRIM(RTRIM(PARSENAME(REPLACE(Fruits,',','.'),2)))
        ELSE LTRIM(RTRIM(PARSENAME(REPLACE(Fruits,',','.'),2))) + ', ' + LTRIM(RTRIM(PARSENAME(REPLACE(Fruits,',','.'),1)))
    END AS Normalized_Fruits
FROM #Fruits

答案 4 :(得分:1)

而且-只是为了好玩-还有另外一个解决方案,叫XQuery进行营救。

DECLARE @Fruits TABLE(Fruits VARCHAR(100));
INSERT INTO @Fruits (Fruits) VALUES 
    ('banana,apple'),
    ('apple,banana'),
    ('kiwi,jackfruit'),
    ('jackfruit, kiwi');

-这是查询

SELECT f.*
      ,CAST('<x>' + REPLACE(REPLACE(f.Fruits,' ',''),',','</x><x>') + '</x>' AS XML)
           .query('
                   for $f in /x/text()
                   order by $f
                   return <y>{concat(",",$f)}</y>
                  ')
           .value('substring(.,2,1000)','nvarchar(max)')
FROM @Fruits f;

通过使用for $f in distinct-values(/x/text())而不是for $f in /x/text(),我们将禁止重复单词。

简而言之:
您的字符串将转换为XML。这样就可以处理.query()的{​​{1}}。对于相当普通的问题,这非常强大。单词被排序并以逗号开头。需要最后的XQuery才能删除第一个前导逗号。