Разобрать поле SQL на несколько строк - PullRequest
0 голосов
/ 22 сентября 2011

Как мне взять таблицу SQL, которая выглядит следующим образом:

MemberNumber    JoinDate    Associate
1234            1/1/2011    A1 free A2 upgrade A31
5678            3/15/2011   A4
9012            5/10/2011   free

И вывод (используя представление или запись в другую таблицу или что-либо простое) это:

MemberNumber    Date
1234-P         1/1/2011
1234-A1        1/1/2011
1234-A2        1/1/2011
1234-A31       1/1/2011
5678-P         3/15/2011
5678-A4        3/15/2011
9012-P         5/10/2011

Где каждая строка приводит к «-P» (основной) выходной строке, а также к любым A # (ассоциированным) строкам. Поле Associate может содержать несколько различных значений, отличных от «A #», но все, что меня интересует, это «A #» (# от 1 до 99). В этом же поле может быть много символов "A #".

Ответы [ 3 ]

3 голосов
/ 22 сентября 2011

Конечно, редизайн таблицы значительно упростит этот запрос, но иногда нам просто нужно выполнить его. Я написал следующий запрос, используя несколько CTE; Я считаю, что легче следовать и точно видеть, что происходит, но вы можете упростить это, когда освоите технику.

Чтобы вставить ваш основной ряд "P", вы увидите, что я просто поместил его в столбец Associate, но его лучше поместить в простой UNION вне CTE.

Кроме того, если вы решите провести рефакторинг своей схемы, можно использовать метод ниже, чтобы «разбить» столбец Associate на строки.

;with 
Split (MemberNumber, JoinDate, AssociateItem)
as  (       select MemberNumber, JoinDate, p.n.value('(./text())[1]','varchar(25)')
            from    (   select MemberNumber, JoinDate, n=cast('<n>'+replace(Associate + ' P',' ','</n><n>')+'</n>' as xml).query('.')
                        from @t
                    ) a
            cross apply n.nodes('n') p(n)
    )
select  MemberNumber + '-' + AssociateItem,
        JoinDate
from    Split
where   left(AssociateItem, 1) in ('A','P')
order
by      MemberNumber;

Метод XML не является хорошим вариантом с точки зрения производительности, так как его скорость уменьшается по мере увеличения количества элементов в «массиве». Если у вас длинные массивы, вам может пригодиться следующий подход:

--* should be physical table, but use this cte if needed
--;with 
--number (n) 
--as    (   select top(50) row_number() over(order by number) as n
--      from    master..spt_values
--  )
    select  MemberNumber + '-' + substring(Associate, n, isnull(nullif(charindex(' ', Associate + ' P', n)-1, -1), len(Associate)) - n+1),
            JoinDate
    from    (   select MemberNumber, JoinDate, Associate + ' P' from @t
            ) t (MemberNumber, JoinDate, Associate)
    cross
    apply   number n
    where   n <= convert(int, len(Associate)) and
            substring(' ' + Associate, n, 1) = ' ' and
            left(substring(Associate, n, isnull(nullif(charindex(' ', Associate, n)-1, -1), len(Associate)) - n+1), 1) in ('A', 'P');
1 голос
/ 22 сентября 2011

Попробуйте эту новую версию

declare @t table (MemberNumber varchar(8), JoinDate date, Associate varchar(50))  

insert into @t values  ('1234', '1/1/2011', 'A1 free A2 upgrade A31'),('5678', '3/15/2011', 'A4'),('9012', '5/10/2011', 'free')  

;with b(f, t, membernumber, joindate, associate)
as 
( 
select 1, 0, membernumber, joindate, Associate
from @t 
union all 
select t+1, charindex(' ',Associate + ' ', t+1), membernumber, joindate, Associate
from b 
where t <  len(Associate)
)
select MemberNumber + case when t = 0 then '-P' else '-'+substring(Associate, f,t-f) end NewMemberNumber, JoinDate
from b  
where t = 0 or substring(Associate, f,1) = 'A' 
--where t = 0 or substring(Associate, f,2) like 'A[1-9]' 
-- order by MemberNumber, t

Результат совпадает с запрошенным выводом.

0 голосов
/ 22 сентября 2011

Я бы порекомендовал изменить структуру вашей базы данных, добавив таблицу ссылок вместо столбца «Связать».Таблица ссылок будет состоять из двух или более столбцов, таких как:

MemberNumber    Associate   Details
-----------------------------------
1234            A1          free
1234            A2          upgrade
1234            A31         
5678            A4          

Тогда желаемый результат может быть получен простым JOIN:

SELECT CONCAT(m.`MemberNumber`, '-', 'P'), m.`JoinDate`
FROM `members` m
UNION
SELECT CONCAT(m.`MemberNumber`, '-', IFNULL(a.`Associate`, 'P')), m.`JoinDate`
FROM `members` m
RIGHT JOIN `members_associates` a ON m.`MemberNumber` = a.`MemberNumber`
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...