我有下表:
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
我怎样才能达到预期的效果?
答案 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
才能删除第一个前导逗号。