Соединить таблицу с другим типом данных

У меня есть следующие таблицы:

Orders
  id int
  orderName varchar(5000)

Communication
  body varchar(5000)
  attachment varchar(5000)

Пример данных заказов:

id   name
132  ordGD
589  ordPG
6321 ordMF

Пример данных связи:

body    attachment
body1   132,589,6321

Я хочу создать хранимую процедуру, которая получает 2 столбца {body,Attachment} из Communication

В SP вход @attachment varchar(5000) содержит несколько идентификаторов заказов, разделенных запятыми, которые ссылаются на таблицу заказов.

Проблема: я хочу, чтобы OrderName в соответствии с Orderid из Orders соответствовал @Attachment


person Ravi Makadia    schedule 04.10.2012    source источник
comment
Ваш вопрос действительно трудно понять. Не могли бы вы предоставить схему (имена и типы столбцов в соответствующих таблицах), а также некоторые примеры входных данных и ожидаемых выходных данных?   -  person PHeiberg    schedule 04.10.2012
comment
Невозможно сделать как соединение. Разделите данные и сохраните их атомарно, а не объединяйте их вместе. В противном случае прочитайте данные с помощью курсора, проанализируйте их и сохраните соответствующие записи порядка во временной таблице или табличной переменной. Но не ждите хорошей производительности.   -  person Jeff Siver    schedule 04.10.2012
comment
Таблица: столбец заказов: столбец идентификатора заказа: имя заказа __таблица: столбец связи: тело столбца: вложение   -  person Ravi Makadia    schedule 04.10.2012
comment
в приложении есть идентификаторы заказов, например › 132 589 6321, и я хочу преобразовать идентификаторы заказов в соответствующие имена заказов, такие как › ordGD, ordPG, ordMF   -  person Ravi Makadia    schedule 04.10.2012


Ответы (2)


Как прокомментировали другие, правильное решение состоит в том, чтобы изменить схему базы данных, чтобы иметь нормализованную схему с атомарными полями.

Решение для текущей схемы состоит в том, чтобы сначала разделить поле вложения на список номеров заказов, используя любой из доступно несколько способов сделать это. Следующим шагом является объединение результата с таблицей Orders по идентификатору, чтобы получить результаты. Третий шаг — объединить имена обратно в список. См. приведенные ссылки для пояснений разделения и объединения.

Вот фрагмент, выполняющий 3 шага:

WITH
-- Numbers table for split logic
L0 AS(SELECT 1 AS c UNION ALL SELECT 1),
L1 AS(SELECT 1 AS c FROM L0 AS A, L0 AS B),
L2 AS(SELECT 1 AS c FROM L1 AS A, L1 AS B),
L3 AS(SELECT 1 AS c FROM L2 AS A, L2 AS B),
Numbers AS(SELECT ROW_NUMBER() OVER(ORDER BY c) AS n FROM L3),

-- The join query for step 2
bodyOrders AS
(SELECT body,        
       o1.name orderName
FROM   Numbers AS nums 
INNER JOIN Communication AS valueTable 
ON nums.n <= CONVERT(int, LEN(valueTable.attachment)) 
AND SUBSTRING(N',' + valueTable.attachment, n, 1) = N','
INNER JOIN Orders o1
ON LTRIM(RTRIM(SUBSTRING(valueTable.attachment, nums.n, 
   charindex(N',', valueTable.attachment + N',', nums.n) - nums.n))) = o1.id
)

-- Concatenation logic for step 3
SELECT  body,
stuff( (SELECT ','+ orderName
               FROM bodyOrders b2
               WHERE b2.body = b1.body 
               ORDER BY orderName
               FOR XML PATH(''), TYPE).value('.', 'varchar(5000)')
            ,1,1,'')
       AS orderNumbers
      FROM bodyOrders b1
      GROUP BY body;

Этот фрагмент не предлагает оптимального или эффективного способа сделать это для больших наборов данных. Это просто пример того, как это можно сделать, если вы должны пойти по этому пути.

Пример скрипта SQL.

person PHeiberg    schedule 04.10.2012

Для этого типа процесса вам необходимо split данные, которые находятся в столбце attachment. Я использую что-то похожее на это для строк Split (есть много способов разделить строки, вы можете поискать в Интернете другие функции):

CREATE FUNCTION [dbo].[Split](@String varchar(MAX), @Delimiter char(1))       
returns @temptable TABLE (items varchar(MAX))       
as       
begin     
    declare @idx int       
    declare @slice varchar(8000)       

    select @idx = 1       
        if len(@String)<1 or @String is null  return       

    while @idx!= 0       
    begin       
        set @idx = charindex(@Delimiter,@String)       
        if @idx!=0       
            set @slice = left(@String,@idx - 1)       
        else       
            set @slice = @String       

        if(len(@slice)>0)  
            insert into @temptable(Items) values(@slice)       

        set @String = right(@String,len(@String) - @idx)       
        if len(@String) = 0 break       
    end   
return      

end

Поскольку это возвращает таблицу, вы можете присоединиться к данным. Таким образом, ваш запрос будет выглядеть так:

select o.id,
  o.name,
  c.body
from orders o
left join 
(
  select c.body, s.items as o_id
  from communications c
  cross apply dbo.split(c.attachment, ',') s
) c
  on o.id = c.o_id

См. скрипт SQL с демонстрацией

Если вы хотите просто заменить значения из поля attachment правильными именами, вы можете использовать функцию Split и CTE за один шаг:

;with cte as (
  select o.id,
    o.name,
    c.body
  from orders o
  left join 
  (
    select c.body, s.items as o_id
    from communications c
    cross apply dbo.split(c.attachment, ',') s
  ) c
    on o.id = c.o_id
)
select distinct c2.body,
  stuff((select distinct ', ' + c1.name
         from cte c1
         where c2.body = c1.body
         for XML path('')),1,1,'') attachment
from cte c2

См. SQL Fiddle с демонстрацией.

person Taryn    schedule 04.10.2012