2015-06-05 2 views
2

Есть две таблицы.Как читать значение внутри курсора?

Одна таблица содержит:

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

Почему вы хотите сделать это с помощью 'CURSOR' вместо подхода на основе набора? –

+0

, поскольку существует огромное количество имен и их соответствующих значений, поэтому я думаю, что это простой в использовании курсор. – SaNa3819

+0

Я думаю, что основанный на наборе подход не будет очень простым здесь – BICube

ответ

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)) + ',', ','[email protected] + ',') > 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

Во-первых, вам нужна функция для разделения значений, разделенных запятыми. Вот DelimitedSplit8K, написанный Джеффом Моденом и улучшенный сообществом. Это считается одним из самых быстрых сплиттеров на основе SQL.

Вы должны также прочитать на FOR XML PATH(''), способ объединения строк. Проверьте это article от Aaron Bertrand за дополнительной информацией.

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. Для других функций сплиттера ознакомьтесь с этим article от Aaron Bertrand.
+0

Хммм .. почему нисходящий? –

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 Я расколоть значения в городе, с рекурсивным КТР. После этого я рассчитываю новое имя для каждого Города. Я не знаю, как быстро на большом столе, но я думаю, что это лучше, чем курсор.

0

с использованием CROSS ОТНОСИТЬСЯ мы изначально будем разграничивать все значения, а затем мы можем acheieve используя путь XML() и CTE в

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 
Смежные вопросы