Соедините таблицу с разделенными запятыми значениями varchar со значениями Id другой таблицы в SQL Server - PullRequest
0 голосов
/ 21 января 2019

У меня есть две таблицы, назовем их Users и Fruit.

Пользователи

ID   Name    Fruit
-------------------
1    Bob      1,3
2    Jack     3

Фрукты

ID   Name
-------------
 1   Apple
 2   Orange
 3   Grape

Как можно объединить эти две таблицы, чтобы заполнить таблицу с выбором пользователем названий фруктов?

Нужно ли мне писать хранимую процедуру с циклом?

Я довольно новичок в SQL Server и был бы рад любой помощи или указанию в правильном направлении.

Ответы [ 5 ]

0 голосов
/ 23 января 2019

Создание физических таблиц с образцами данных

CREATE TABLE TempUsers 
( ID INT,
  Name  VARCHAR(100),
  Fruit VARCHAR(100)
 )
INSERT INTO TempUsers 
SELECT 1,'Bob' ,'1,3' UNION ALL
SELECT 2,'Jack','3'

CREATE TABLE TempFruit 
    ( ID INT,
      Name  VARCHAR(100))

INSERT INTO TempFruit
SELECT 1,'Apple' UNION ALL
SELECT 2,'Orange'UNION ALL
SELECT 3,'Grape'

Создание табличной функции для извлечения названий фруктов, разделенных запятыми

CREATE FUNCTION [dbo].[udf_GetFruitNames]
(
@vc_String nvarchar(max)
)
RETURNS  @OutTable TABLE
(
Reqdata nvarchar(max)
)
AS
BEGIN

    DECLARE @Temp AS TABLE
    (
        DATA nvarchar(max)
    )
    INSERT INTO @Temp
    SELECT @vc_String;

    DECLARE @Temp1 AS TABLE
    (
    DATA nvarchar(max)
    )
    INSERT INTO @Temp1
    SELECT 
            STUFF((SELECT DISTINCT ','+ Name FROM
            (
                SELECT  ID,
                        Name    
                FROm TempFruit 
                WHERE ID IN  ( SELECT    
                                    CAST(Split.a.value('.', 'nvarchar(1000)') AS INT) AS FruitId
                                    FROM
                                    (   SELECT    
                                        CAST( '<S>'+ REPLACE(DATA,',','</S><S>')+'</S>' AS XML) AS FruitId             
                                        FROM @Temp f

                                    )AS A
                                    CROSS APPLY FruitId.nodes('S') AS Split(a))
            ) As dt FOR XML PATH ('')),1,1,'') As FruitName


        INSERT INTO @OutTable
        SELECT * FROM @Temp1
RETURN
END

Запрос Sql

SELECT ID
      ,Name
      ,uf.Reqdata AS FruitNames
FROM TempUsers u
CROSS APPLY [dbo].[udf_GetFruitNames](u.Fruit) AS uf

Or

SELECT ID
  ,Name
  ,(SELECT Reqdata FROM [dbo].[udf_GetFruitNames](u.Fruit) ) AS FruitNames
  FROM TempUsers u

Результат

ID  Name    FruitNames
---------------------
1   Bob     Apple,Grape
2   Jack    Grape
0 голосов
/ 21 января 2019

Вы можете достичь желаемого результата, не меняя ничего при помощи этого запроса

SELECT u.Id, u.Name, f.name  
FROM Users u
inner join Fruit f  on f.ID IN (SELECT cast(value as int)FROM STRING_SPLIT(u.fruit, ','));

Поскольку у вас есть sql server 2014, у вас есть различные опции, такие как CLR, XML и числовые функции.Лучший из них - CLR, но он сложный.Таким образом, вы можете использовать этот код XML.

select * from
(SELECT ID, [name],LTRIM(RTRIM(m.n.value('.[1]','varchar(8000)'))) AS fruitid
    FROM
(SELECT ID,[name],CAST('<XMLRoot><RowData>' + REPLACE(fruit,',','</RowData><RowData>') + '</RowData></XMLRoot>' AS XML) AS x FROM  {User table})t
CROSS APPLY x.nodes('/XMLRoot/RowData')m(n)) u 
inner join {fruit table} f on f.id = u.fruitid
0 голосов
/ 21 января 2019

Для SQL Server 2014, где вы не можете использовать STRING_SPLIT, вы можете разделить varchar, используя XML, как показано ниже.

  ;WITH cte 
     AS (SELECT id, 
                name, 
                fruitid 
         FROM   (SELECT *, 
                        Cast('<X>' + Replace(F.fruit, ',', '</X><X>') 
                             + '</X>' AS XML) AS xmlfilter 
                 FROM   users F)F1 
                CROSS apply (SELECT fdata.d.value('.', 'varchar(50)') AS FruitId 
                             FROM   f1.xmlfilter.nodes('X') AS fdata(d)) O) 
SELECT * 
FROM   cte C 
       INNER JOIN fruit F 
               ON F.id = Cast(C.fruitid AS INT) 

DEMO

0 голосов
/ 21 января 2019

Пример данных

DECLARE @Users AS TABLE(ID INt,  Name  VARCHAR(100),fruit VARCHAR(100))
INSERT INTO @Users
SELECT 1,'Bob' ,'1,3' UNION ALL
SELECT 2,'Jack','3'

DECLARE  @Fruit AS TABLE(ID INt,  Name  VARCHAR(100))
INSERT INTO @Fruit
SELECT 1,'Apple' UNION ALL
SELECT 2,'Orange'UNION ALL
SELECT 3,'Grape'

Sql Script

;WITH CTE
AS
(
SELECT  UserId,
        UserName ,      
        CAST(Split.a.value('.', 'nvarchar(1000)') AS INT) AS FruitId
FROM
(       SELECT  u.ID AS UserId,
                u.Name AS UserName ,      
                CAST( '<S>'+ REPLACE(fruit,',','</S><S>')+'</S>' AS XML) AS FruitId             
        FROM @Fruit f
        INNER JOIN  @Users u
        ON u.ID=f.ID 
)AS A
CROSS APPLY FruitId.nodes('S') AS Split(a)
)
SELECT Userid,
       UserName,
       FruitId,
       ft.name AS FruitName
FROM CTE c
LEFT JOIN (SELECT * FROM @Fruit) AS Ft
ON ft.ID=c.FruitId

Результат

Userid  UserName    FruitId   FruitName
------------------------------------------
1        Bob            1       Apple
1        Bob            3       Grape
2        Jack           3       Grape
0 голосов
/ 21 января 2019

Прежде всего, вам нужно переделать ваши столы.Существует необходимость в соединительной таблице, которая будет содержать информацию о том, какой пользователь подключен к какому фрукту.Это N:N raletionship.

Итак, вы должны создать такую ​​таблицу:

FruitUser

UserId  FruitId
1       1
1       3
2       3

UserId от FK до Users таблицы, FruitId - это таблица от FK до Fruits, и оба этих столбца образуют первичный ключ композита.Это стандартный подход.

Тогда вы можете использовать простое объединение для получения результатов:

select * from users u
join FruitUser fu on u.id = fu.userid
join Fruit f on f.id = fu.fruitId
...