有两张桌子。
一个表包含:
Name value
A 1
B 2
C 3
D 4
另一个表包含
City value
aa 1
bb 2,3
cc 3
dd 1,2,4
我想要一个包含以下内容的输出:
City value Name
aa 1 A
bb 2,3 B,C
cc 3 C
dd 1,2,4 A,B,D
如何使用光标执行此操作?
答案 0 :(得分:1)
感谢。你的问题让我很欣赏正常形式。
无论如何,我会想出一个基于游标的解决方案,因为你假设无法处理非规范化数据。
一旦有了将行具体化为值列表的功能,就可以通过简单的查询来解决这个问题。
假设:
CREATE TABLE dbo.NV (Name CHAR(1), Value INT)
CREATE TABLE dbo.CV (City varchar(88), ValueList VARCHAR(88))
加载了您指定的数据。
这个SQL脚本:
GO
CREATE FUNCTION dbo.f_NVList(@VList VARCHAR(MAX)) RETURNS VARCHAR(MAX)
AS
BEGIN
DECLARE @VAL VARCHAR(928)='',
@FIDescr VARCHAR(55)
SELECT @VAL = COALESCE(@VAL + LTRIM(map.name),'') + ','
FROM dbo.nv Map
WHERE CHARINDEX(','+LTRIM(STR(map.value)) + ',', ','+@VList + ',' ) > 0
SET @VAL = SUBSTRING(@VAL,1,len(@VAL)-1)
RETURN(@VAL)
END
GO -- end of function
-- this generates the output, using the function to materialize the name-values
SELECT cv.* , dbo.f_NVList(cv.ValueList ) as NameList FROM dbo.CV cv;
产生你的输出:
请不要 - 但如果您出于某种原因确实需要光标,而不是
SELECT cv.* , dbo.f_NVList(cv.ValueList ) as NameList FROM dbo.CV cv;
使用此
OPEN BadIdea;
FETCH NEXT FROM BadIdea INTO @C, @VList
WHILE @@FETCH_STATUS = 0
BEGIN
SET @NameList = dbo.f_NVList(@Vlist)
INSERT INTO @OUT VALUES( @C, @VLIST , @NameList )
FETCH NEXT FROM BadIdea INTO @C, @VList
END
CLOSE BadIdea
DEALLOCATE BadIdea
select * from @OUT ;
答案 1 :(得分:0)
请试试这个:
;with nv as (
select *
from (values ('A', '1'), ('B', '2'), ('C', '3'), ('D', '4')) a (Name, value))
, cv as (
select *
from (values ('aa', '1'), ('bb', '2,3'), ('cc', '3'), ('dd', '1,2,4')) a(City, value)
)
, cv2 as (
select cv.City
, case when charindex(',',cv.value)>0 then LEFT(cv.value, charindex(',',cv.value)-1) else cv.value end value
, case when charindex(',',cv.value)>0 then right(cv.value, LEN(cv.value)-len(LEFT(cv.value, charindex(',',cv.value)-1)+',')) end leftover
from cv
union all
select cv.City
, case when charindex(',',cv.leftover)>0 then LEFT(cv.leftover, charindex(',',cv.leftover)-1) else cv.leftover end value
, case when charindex(',',cv.leftover)>0 then right(cv.leftover, LEN(cv.leftover)-len(LEFT(cv.leftover, charindex(',',cv.leftover)-1)+',')) end leftover
from cv2 cv
where cv.leftover is not null
)
select *
, stuff((
select ','+nv.Name
from cv2
join nv on nv.value=cv2.value
where cv2.City=cv.City
for xml path('')
), 1, 1, '') Name
from cv
使用cv2我将值拆分为City,并使用递归CTE。之后,我计算每个城市的新名称。 我不知道在一张大桌子上有多快,但我认为它比光标更好。
答案 2 :(得分:0)
使用CROSS APPLY我们将首先划分所有值,然后我们可以使用XML path()和CTE&#39>来实现
DECLARE @Name table (name varchar(5),value int)
INSERT INTO @Name (name,value)values ('A',1),('B',2),('C',3),('D',4)
DECLARE @City table (city varchar(10),value varchar(10))
INSERT INTO @City (city,value)values ('aa','1'),('bb','2,3'),('cc','3'),('dd','1,2,4')
代码:
;with CTE AS (
SELECT A.city,
Split.a.value('.', 'VARCHAR(100)') AS Data
FROM
(
SELECT city,
CAST ('<M>' + REPLACE(value, ',', '</M><M>') + '</M>' AS XML) AS Data
FROM @City
) AS A CROSS APPLY Data.nodes ('/M') AS Split(a)
),CTE2 AS (
Select c.city,t.value,STUFF((SELECT ', ' + CAST(name AS VARCHAR(10)) [text()]
FROM @Name
WHERE value = c.Data
FOR XML PATH(''), TYPE)
.value('.','NVARCHAR(MAX)'),1,2,' ') List_Output
from CTE C
INNER JOIN @Name t
ON c.Data = t.value
)
select DISTINCT c.city,STUFF((SELECT ', ' + CAST(value AS VARCHAR(10)) [text()]
FROM CTE2
WHERE city = C.city
FOR XML PATH(''), TYPE)
.value('.','NVARCHAR(MAX)'),1,2,' ') As Value ,STUFF((SELECT ', ' + CAST(List_Output AS VARCHAR(10)) [text()]
FROM CTE2
WHERE city = C.city
FOR XML PATH(''), TYPE)
.value('.','NVARCHAR(MAX)'),1,2,' ')As Name from CTE2 C
答案 3 :(得分:-1)
首先,您需要一个函数来分割逗号分隔的值。以下是Jeff Moden撰写的DelimitedSplit8K并由社区改进。这被认为是最快的基于SQL的字符串分割器之一。
您还应该阅读FOR XML PATH('')
,这是一种连接字符串的方法。请查看Aaron Bertrand的article以获取更多信息。
SELECT
*
FROM Table2 t2
CROSS APPLY(
SELECT STUFF((
SELECT ',' + Name
FROM Table1
WHERE Value IN(
SELECT CAST(s.Item AS INT) FROM dbo.DelimitedSplit8K(t2.Value, ',') s
)
FOR XML PATH(''), type).value('.', 'VARCHAR(MAX)'
), 1, 1, '')
)x(Name)
注意:
DelimitedSplit8K
。