如何读取游标内的值?

时间:2015-06-05 04:55:50

标签: sql sql-server sql-server-2008

有两张桌子。

一个表包含:

 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

如何使用光标执行此操作?

4 个答案:

答案 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;

产生你的输出:

enter image description here

请不要 - 但如果您出于某种原因确实需要光标,而不是

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)

SQL Fiddle

注意:

  1. 确保获取DelimitedSplit8K
  2. 的最新版本
  3. 对于其他分离器功能,请查看Aaron Bertrand的article