SQL JOIN возвращает несколько элементов в 1 строку - PullRequest
0 голосов
/ 02 февраля 2019

У меня есть 2 таблицы SQL.

IF OBJECT_ID ('A', 'U') IS NOT NULL
    BEGIN
        DROP TABLE A;
    END
GO
CREATE TABLE A
(
    [ID] [INT] IDENTITY(1,1) Primary Key NOT NULL,
    [Name] [NVARCHAR](80) DEFAULT('Guest'),
    [Acct_Num] [NVARCHAR](255)
)
GO

IF OBJECT_ID ('B', 'U') IS NOT NULL
    BEGIN
        DROP TABLE B;
    END
GO
CREATE TABLE B
(
    [ID] [INT] IDENTITY(1,1) Primary Key NOT NULL,
    [Name] [NVARCHAR](80) DEFAULT('Unk'),
    [Acct_Num] [NVARCHAR](255)
)
GO

Добавление в тестовые данные

INSERT INTO [A] ([Name], [Acct_Num]) VALUES ('Test1', '5006347')
INSERT INTO [A] ([Name], [Acct_Num]) VALUES ('Test2', '5006348')
INSERT INTO [A] ([Name], [Acct_Num]) VALUES ('Test3', '5006349')
GO

INSERT INTO [B] ([Name], [Acct_Num]) VALUES ('Attach1', '5006347')
INSERT INTO [B] ([Name], [Acct_Num]) VALUES ('Attach2', '5006347')
GO

Я ищу запрос на возврат A [Name], A [Acct_Num] и B [Имя], где A [Acct_Num] соответствует B [Acct_Num].B [Имя] необходимо добавить к результату в виде прикрепленного столбца. (пример выходных данных приведен ниже)

Теперь моя непосредственная потребность - это всего лишь 2 элемента в таблице B, но что, если это будет 5?

Я попробовал что-то подобноено он не возвращает значения в AS 'имя столбца'.

DECLARE @Row_Cnt Integer = (SELECT COUNT(*) FROM [B] WHERE B.Acct_Num = '5006347')
SELECT A.[Name], A.[Acct_Num],
CASE WHEN @Row_Cnt = '1' THEN
    (SELECT MIN(B.[Name]) FROM B WHERE B.Acct_Num = A.Acct_Num)
    END  AS 'Attach 1', 
CASE WHEN @Row_Cnt = '2' THEN
    (SELECT MAX(B.[Name]) FROM B WHERE B.Acct_Num = A.Acct_Num)
    END AS 'Attach 2'
FROM A WHERE A.Acct_Num = '5006347'

Теперь это работает с использованием MIN & MAX, но при этом было бы невозможно получить все совпадения, если было найдено более 2 элементов.

SELECT A.[Name], A.[Acct_Num], MIN(B.[Name]) AS 'Attach 1', MAX(B.[Name]) AS 'Attach 2'
FROM A
JOIN B ON B.Acct_Num = A.Acct_Num
WHERE A.Acct_Num = '5006347'
GROUP BY A.Name, A.Acct_Num

РЕЗУЛЬТАТ:

Name  | Acct_Num | Attach 1 | Attach 2
Test1 | 5006347  | Attach1  | Attach2

И это не очень эффектное (хотя и функциональное) решение для моего квеста с двумя столбцами, но я чувствую, что грядет необходимость в большем количестве возвращаемых столбцов.

Так что же является более эффективным методом, который бы учитывал неизвестное количество возвращаемых предметов.

Спасибо!

Ответы [ 2 ]

0 голосов
/ 03 февраля 2019

В качестве альтернативы динамическому SQL можно, если вы работаете с версией 2017, получить динамически скомпонованный вывод для одного столбца, который создается как список всех значений благодаря новой функции агрегирования.Затем ваш код должен разбивать данные таким образом, чтобы их можно было увидеть в виде нескольких столбцов.

SELECT A.[Name], A.[Acct_Num], STRING_AGG(B.[Name], ',') AS Attaches
FROM A
JOIN B ON B.Acct_Num = A.Acct_Num
WHERE A.Acct_Num = '5006347'
GROUP BY A.Name, A.Acct_Num

Имя Acct_Num Attach Test1 5006347 Attach1, Attach2

На случай, если вы захотитечтобы увидеть больше примеров этого, вы можете зайти в Google для group_concat, который является эквивалентной функцией MySQL, которая существовала довольно давно.

0 голосов
/ 03 февраля 2019

Динамический SQL выглядит так, как будто вы объединяете код SQL на «языке программирования», но все же вы можете использовать только SQL, например, так:

DECLARE @NameA NVARCHAR(80), @NameB NVARCHAR(80), @Acct_Num NVARCHAR(255),
   @DynamicSQL NVARCHAR(MAX), @i INT

SET @Acct_Num = '5006347'
SET @i = 1

SELECT @NameA  = [Name] FROM [A] WHERE Acct_Num = @Acct_Num

-- Start of Dynamic SQL (or dynamic SELECT) - I include columns
-- A.[Name] and Acct_Num, which I've know already
SELECT @DynamicSQL = N'SELECT N''' + @NameA + N''' AS NameA, N''' + 
   @Acct_Num + ''' AS Acct_Num,'

DECLARE cur CURSOR FAST_FORWARD LOCAL FOR
   SELECT B.[Name]
   FROM B
   JOIN A ON A.Acct_Num = B.Acct_Num
   WHERE A.[Name] = @NameA

OPEN cur

WHILE(1=1)
BEGIN
   FETCH NEXT FROM cur INTO @NameB

   IF @@FETCH_STATUS <> 0
      BREAK;

   -- Add each row from cursor to the dynamic sql string as a column. 
   -- Added columns are named 'Col' and order number (eg. Col1, Col2 etc.)
   SELECT @DynamicSQL = @DynamicSQL + N'N''' + @NameB + N''' AS Col' +
      CAST(@i AS NVARCHAR) + ','
   SELECT @i = @i+1
END

SELECT @DynamicSQL = LEFT(@DynamicSQL, LEN(@DynamicSQL) - 1)  -- remove last comma

EXEC sp_executesql @DynamicSQL

Это не оптимизированный код, но я надеюсь надемонстрация это нормально.Если у вас есть несколько имен в таблице A для одного Acct_Num, код действительно нуждается в некоторой модификации.

...