SQL JOIN将多个项目返回到1行

时间:2019-02-02 19:42:40

标签: sql sql-server join multiple-columns

我有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 [名称],A [Acct_Num]和B [Name]的查询,其中A [Acct_Num]匹配B [Acct_Num]。 B [名称]必须作为附加列添加到结果中。 (样品输出下面给出)

现在我急需的是表B中唯一可能的2项,但如果它是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列任务来说,这不是一个非常出色的解决方案(尽管是功能性的),但我感觉需要更多的返回列。

因此,什么是将容纳返回的项目的一个未知量的更有效的方法。

谢谢!

2 个答案:

答案 0 :(得分:0)

动态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有多个名称,则确实需要对代码进行一些修改。

答案 1 :(得分:0)

或者,对于Dynamic 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附件 Test1 5006347 Attach1,Attach2

如果您想查看更多示例,可以在Google上搜索group_concat,它是MySQL等效的功能,已经存在很长时间了。