SQL: вложенный SELECT с несколькими значениями в одном поле - PullRequest
4 голосов
/ 02 апреля 2011

В моей БД SQL 2005 у меня есть таблица со значениями, хранящимися в виде идентификаторов, со связями с другими таблицами.Поэтому в моей таблице MyDBO.warranty я сохраняю product_id вместо product_name для экономии места.Имя продукта хранится в MyDBO.products .

Когда отдел маркетинга извлекает демографическую информацию, запрос выбирает соответствующее имя для каждого идентификатора из связанных таблиц (обрезаетсядля краткости):

SELECT w1.warranty_id AS "No.",
       w1.created AS "Register Date" 
       w1.full_name AS "Name", 
       w1.purchase_date AS "Purchased", 
       (
           SELECT p1.product_name
           FROM WarrDBO.products p1 WITH(NOLOCK)
           WHERE p1.product_id = i1.product_id
       ) AS "Product Purchased",
       i1.accessories
FROM WarrDBO.warranty w1
LEFT OUTER JOIN WarrDBO.warranty_info i1
    ON i1.warranty_id = w1.warranty_id
ORDER BY w1.warranty_id ASC

Теперь моя проблема состоит в том, что в столбце «аксессуары» в таблицеtia_info хранятся несколько значений:

No.     Register Date    Name             Purchased       Accessories
---------------------------------------------------------------------
1500    1/1/2008         Smith, John      Some Product    5,7,9
1501    1/1/2008         Hancock, John    Another         2,3
1502    1/1/2008         Brown, James     And Another     2,9

Мне нужно сделать что-то подобное с "Аксессуары », которые я сделал с« Продуктом »и извлеките accessory_name из таблицы MyDBO.accessories , используя accessory_id .Я не уверен, с чего начать, потому что сначала мне нужно извлечь идентификаторы, а затем каким-то образом объединить несколько значений в строку.Таким образом, каждая строка будет иметь «имя-аксессуара1, имя-аксессуара2, имя-аксессуара3»:

No.     Register Date    Name             Purchased       Accessories
---------------------------------------------------------------------
1500    1/1/2008         Smith, John      Some Product    Case,Bag,Padding
1501    1/1/2008         Hancock, John    Another         Wrap,Label
1502    1/1/2008         Brown, James     And Another     Wrap,Padding

Как мне это сделать?

РЕДАКТИРОВАТЬ >> Публикация окончательного кода:

Я создалэта функция:

CREATE FUNCTION SQL_GTOInc.Split
(
  @delimited varchar(50),
  @delimiter varchar(1)
) RETURNS @t TABLE
(
-- Id column can be commented out, not required for sql splitting string
  id INT identity(1,1), -- I use this column for numbering splitted parts
  val INT
)
AS
BEGIN
  declare @xml xml
  set @xml = N'<root><r>' + replace(@delimited,@delimiter,'</r><r>') + '</r></root>'

  insert into @t(val)
  select
    r.value('.','varchar(5)') as item
  from @xml.nodes('//root/r') as records(r)

  RETURN
END

И соответственно обновил мой код:

SELECT w1.warranty_id, 
       i1.accessories,
       (
           CASE
               WHEN i1.accessories <> '' AND i1.accessories <> 'NULL' AND LEN(i1.accessories) > 0 THEN
                   STUFF(
                       (
                           SELECT ', ' + a1.accessory
                           FROM MyDBO.accessories a1
                           INNER JOIN MyDBO.Split(i1.accessories, ',') a2
                           ON a1.accessory_id = a2.val
                           FOR XML PATH('')
                       ), 1, 1, ''
                   )
               ELSE ''
           END
        ) AS "Accessories"
FROM MyDBO.warranty w1
    LEFT OUTER JOIN MyDBO.warranty_info i1
    ON i1.warranty_id = w1.warranty_id

Ответы [ 4 ]

5 голосов
/ 02 апреля 2011

Вы можете написать табличную функцию, которая просто разбивает строку через запятую на XML и превращает узлы XML в строки.

См .: http://www.kodyaz.com/articles//t-sql-convert-split-delimeted-string-as-rows-using-xml.aspx

Присоединиться к аксессуарам через результат вызова функции и вставить результат обратно в список имен, разделенных запятыми.

Не проверенный код:

SELECT w1.warranty_id AS "No.",
       w1.created AS "Register Date" 
       w1.full_name AS "Name", 
       w1.purchase_date AS "Purchased", 
       (
           SELECT p1.product_name
           FROM WarrDBO.products p1 WITH(NOLOCK)
           WHERE p1.product_id = i1.product_id
       ) AS "Product Purchased",
       STUFF(
         (
           SELECT 
             ', ' + a.name
           FROM [table-valued-function](i1.accessories) acc_list 
             INNER JOIN accessories a ON acc_list.id = a.id
           FOR XML PATH('')
         ), 1, 1, ''
       ) AS [accessories]
FROM WarrDBO.warranty w1
  LEFT OUTER JOIN WarrDBO.warranty_info i1
    ON i1.warranty_id = w1.warranty_id
ORDER BY w1.warranty_id ASC  
3 голосов
/ 02 апреля 2011

Ничего общего с вашим вопросом. Просто обратите внимание, что ваш исходный запрос также может быть написан, перемещая подзапрос в объединение, как:

SELECT w1.warranty_id AS "No.",
       w1.created AS "Register Date" 
       w1.full_name AS "Name", 
       w1.purchase_date AS "Purchased", 
       p1.product_name AS "Product Purchased",
       i1.accessories
FROM WarrDBO.warranty w1
INNER JOIN WarrDBO.products p1 
    ON p1.product_id = i1.product_id 
LEFT OUTER JOIN WarrDBO.warranty_info i1
    ON i1.warranty_id = w1.warranty_id
ORDER BY w1.warranty_id ASC
1 голос
/ 02 апреля 2011

Вам просто нужно использовать функцию FOR XML в SQL Server для простого отслеживания строк:

Пример из ссылки в блоге:

SELECT
  STUFF(
    (
    SELECT
      ' ' + Description
    FROM dbo.Brands
    FOR XML PATH('')
    ), 1, 1, ''
  ) As concatenated_string

Для анализаполе, которое уже сохранено как разделитель запятыми, вам нужно написать UDF, который анализирует поле и возвращает таблицу, которую затем можно использовать с предикатом IN в предложении WHERE.Посмотрите здесь для начала , и здесь .

0 голосов
/ 02 апреля 2011

Кажется, это работа для объединенной агрегатной функции. В SQL его можно развернуть с помощью CLR

http://www.mssqltips.com/tip.asp?tip=2022

...