在 SQL Server 中水平连接两个以上的表

     2023-02-24     270

关键词:

【中文标题】在 SQL Server 中水平连接两个以上的表【英文标题】:Concatenate more than two tables horizontally in SQL Server 【发布时间】:2015-02-24 08:03:47 【问题描述】:

以下是架构

+---------+---------+
| Employee Table    |
+---------+---------+
| EmpId   | Name    | 
+---------+---------+
| 1       | John    |
| 2       | Lisa    |
| 3       | Mike    |
|         |         |
+---------+---------+

+---------+-----------------+
| Family   Table            |
+---------+-----------------+
| EmpId   | Relationship    | 
+---------+-----------------+
| 1       | Father          |
| 1       | Mother          |
| 1       | Wife            |
| 2       | Husband         |
| 2       | Child           |
+---------+-----------------+

+---------+---------+
| Loan  Table       |
+---------+--------+
| LoanId  | EmpId  | 
+---------+--------+
| L1      | 1      |
| L2      | 1      |
| L3      | 2      |
| L4      | 2      |
| L5      | 3      |
+---------+--------+
Employee Table 和 Family Table 是一对多的关系 Employee Table 和 Loan Table 有一个多关系

我尝试过加入,但它给出了多余的行。

现在所需的输出将是

+---------+---------+--------------+---------+
| EmpId   | Name    | RelationShip | Loan    | 
+---------+---------+--------------+---------+
| 1       | John    | Father       | L1      |
| -       | -       | Mother       | L2      |
| -       | -       | Wife         | -       |
| 2       | Lisa    | Husband      | L3      |
| -       | -       | Child        | L4      |
| 3       | Mike    | -            | L5      |
|         |         |              |         |
+---------+---------+--------------+---------+    

【问题讨论】:

贷款属于员工(1:N),家庭关系也属于员工(1:N)。您如何将 L2 和 L4 贷款归于非员工 - 这些信息不包含在数据中? 一个选择没有设置顺序,也没有排序来产生这个。现有列无法做到这一点。 【参考方案1】:

您似乎正试图将贷款“按顺序”分配给家庭表中的行。解决这个问题的方法是首先获取正确的行,然后将贷款分配给行。

正确的行(和前三列)是:

select f.EmpId, e.Name, f.Relationship
from family f join
     Employee e
     on f.empid = e.empid;

请注意,这不会将连字符放在重复值的列中,而是放入实际值。尽管您可以在 SQL 中安排连字符,但这是一个坏主意。 SQL 结果采用表格的形式,表格是无序集,每列和每行都有值。当您开始输入连字符时,您取决于顺序。

现在的问题是加入贷款。这实际上很简单,通过使用row_number() 添加一个join 键:

select f.EmpId, e.Name, f.Relationship, l.LoanId
from Employee e left join
     (select f.*, row_number() over (partition by f.EmpId order by (select NULL)) as seqnum
      from family f
     ) f 
     on f.empid = e.empid left join
     (select l.*, row_number() over (partition by l.EmpId order by (select NULL)) as seqnum
      from Loan l
     ) l
     on f.EmpId = l.EmpId and f.seqnum = l.seqnum;

请注意,这并不能保证给定员工的贷款分配顺序。您的数据似乎没有足够的信息来处理更一致的分配。

【讨论】:

@VladimirBaranov 。 . .现在应该。我用错误的表开始left joins 链。【参考方案2】:

下面概述的方法允许轻松地将更多表“连接”到结果集。不限于两张表。

我将使用表变量来说明解决方案。在现实生活中,这些表当然是真实的表,而不是变量,但我会坚持使用变量,以使这个示例脚本易于运行和尝试。

declare @TEmployee table (EmpId int, Name varchar(50));
declare @TFamily table (EmpId int, Relationship varchar(50));
declare @TLoan table (EmpId int, LoanId varchar(50));

insert into @TEmployee values (1, 'John');
insert into @TEmployee values (2, 'Lisa');
insert into @TEmployee values (3, 'Mike');

insert into @TFamily values (1, 'Father');
insert into @TFamily values (1, 'Mother');
insert into @TFamily values (1, 'Wife');
insert into @TFamily values (2, 'Husband');
insert into @TFamily values (2, 'Child');

insert into @TLoan values (1, 'L1');
insert into @TLoan values (1, 'L2');
insert into @TLoan values (2, 'L3');
insert into @TLoan values (2, 'L4');
insert into @TLoan values (3, 'L5');

我们需要一个数字表。

SQL, Auxiliary table of numbers

http://web.archive.org/web/20150411042510/http://sqlserver2000.databases.aspfaq.com/why-should-i-consider-using-an-auxiliary-numbers-table.html

http://dataeducation.com/you-require-a-numbers-table/

同样,在现实生活中,您将拥有一个适当的数字表,但对于本示例,我将使用以下内容:

declare @TNumbers table (Number int);
insert into @TNumbers values (1);
insert into @TNumbers values (2);
insert into @TNumbers values (3);
insert into @TNumbers values (4);
insert into @TNumbers values (5);

我的方法背后的主要思想是首先创建一个帮助表,该表将包含每个 EmpId 的正确行数,然后使用该表有效地获得结果。

我们将从计算每个EmpId 的关系和贷款数量开始:

WITH
CTE_Rows
AS
(
    SELECT Relationships.EmpId, COUNT(*) AS EmpRows
    FROM @TFamily AS Relationships
    GROUP BY Relationships.EmpId

    UNION ALL

    SELECT Loans.EmpId, COUNT(*) AS EmpRows
    FROM @TLoan AS Loans
    GROUP BY Loans.EmpId
)

然后我们计算每个EmpId的最大行数:

,CTE_MaxRows
AS
(
    SELECT
        CTE_Rows.empid
        ,MAX(CTE_Rows.EmpRows) AS MaxEmpRows
    FROM CTE_Rows
    GROUP BY CTE_Rows.empid
)

上面的 CTE 对于每个 EmpId: EmpId 本身都有一行,并且此 EmpId 的最大关系或贷款数。现在我们需要扩展这个表并为每个EmpId 生成给定的行数。这里我使用Numbers 表:

,CTE_RowNumbers
AS
(
SELECT
    CTE_MaxRows.empid
    ,Numbers.Number AS rn
FROM
    CTE_MaxRows
    CROSS JOIN @TNumbers AS Numbers
WHERE
    Numbers.Number <= CTE_MaxRows.MaxEmpRows
)

然后我们需要将行号添加到所有包含数据的表中,我们稍后将用于连接。您可以使用表格中的其他列对行号进行排序。对于这个例子,没有太多选择。

,CTE_Relationships
AS
(
    SELECT
        Relationships.EmpId
        ,ROW_NUMBER() OVER (PARTITION BY Relationships.EmpId ORDER BY Relationships.Relationship) AS rn
        ,Relationships.Relationship
    FROM @TFamily AS Relationships
)
,CTE_Loans
AS
(
    SELECT
        Loans.EmpId
        ,ROW_NUMBER() OVER (PARTITION BY Loans.EmpId ORDER BY Loans.LoanId) AS rn
        ,Loans.LoanId
    FROM @TLoan AS Loans
)

现在我们准备好将这一切结合在一起。 CTE_RowNumbers 有我们需要的确切行数,所以简单的LEFT JOIN 就足够了:

,CTE_Data
AS
(
    SELECT
        CTE_RowNumbers.empid
        ,CTE_Relationships.Relationship
        ,CTE_Loans.LoanId
    FROM
        CTE_RowNumbers
        LEFT JOIN CTE_Relationships ON CTE_Relationships.EmpId = CTE_RowNumbers.EmpId AND CTE_Relationships.rn = CTE_RowNumbers.rn
        LEFT JOIN CTE_Loans ON CTE_Loans.EmpId = CTE_RowNumbers.EmpId AND CTE_Loans.rn = CTE_RowNumbers.rn
)

我们差不多完成了。主Employee 表可能有一些EmpIds 没有任何相关数据,例如您的示例数据中的EmpId = 3。为了在结果集中获得这些EmpIds,我将把CTE_Data 加入到主表中,并将NULLs 替换为破折号:

SELECT
    Employees.EmpId
    ,Employees.Name
    ,ISNULL(CTE_Data.Relationship, '-') AS Relationship
    ,ISNULL(CTE_Data.LoanId, '-') AS LoanId
FROM
    @TEmployee AS Employees
    LEFT JOIN CTE_Data ON CTE_Data.EmpId = Employees.EmpId
ORDER BY Employees.EmpId, Relationship, LoanId;

要获得完整的脚本,只需将这篇文章中的所有代码块按照它们在此处显示的顺序放在一起。

这是结果集:

EmpId   Name   Relationship   LoanId
1       John   Father         L1
1       John   Mother         L2
1       John   Wife           -
2       Lisa   Child          L3
2       Lisa   Husband        L4
3       Mike   -              L5

【讨论】:

【参考方案3】:

Vladimir Baranov 已经写了一个很好的解决方案,但它相当长(并且有一个小问题:您想要 Husband-L3 和 Child-L4,但此解决方案返回 Child-L3 和 Husband-L4)。

Gordon Linoff 编写了一个较短的解决方案,但它无法正常工作。

我可以修复 Gordon 的解决方案,如下所示:

SELECT e.EmpId, e.Name, f.Relationship, l.LoanId
FROM @TEmployee e
LEFT JOIN (
    SELECT f.*, ROW_NUMBER() OVER (PARTITION BY f.EmpId ORDER BY (SELECT NULL)) AS seqnum
    FROM @TFamily f
) f ON f.empid = e.empid 
LEFT JOIN (
    SELECT l.*, ROW_NUMBER() OVER (PARTITION BY l.EmpId ORDER BY (SELECT NULL)) AS seqnum
    FROM @TLoan l
) l ON l.EmpId = e.EmpId AND (f.seqnum = l.seqnum OR f.seqnum IS NULL)

但是,我宁愿说这个问题是不正确的,因为它要求我们将家庭成员任意匹配特定的贷款(当不存在真正的关系时)。

我宁愿说正确的问题是具有以下答案的问题:

SELECT e.EmpId, e.Name,
    SUBSTRING((
        SELECT ', '+f.Relationship AS '*'
        FROM @TFamily f
        WHERE f.EmpId=e.EmpId
        FOR XML PATH(''), TYPE
    ).value('.','nvarchar(4000)'),3,4000) AS FamilyMembers,
    SUBSTRING((
        SELECT ', '+l.LoanId AS '*'
        FROM @TLoan l
        WHERE l.EmpId=e.EmpId
        FOR XML PATH(''), TYPE
    ).value('.','nvarchar(4000)'),3,4000) AS Loans
FROM @TEmployee e

【讨论】:

sql连接两个以上的表

】sql连接两个以上的表【英文标题】:Sqljoinmorethantwotables【发布时间】:2021-12-2614:22:39【问题描述】:我有三个表transaction、transaction-docs和generic-transaction-task。transaction-docs和generic-transaction-task表是在参数transactionId(外键关系)... 查看详情

如何使用大小类和自动布局在 Xib 中水平对齐两个 UI 按钮

】如何使用大小类和自动布局在Xib中水平对齐两个UI按钮【英文标题】:HowtoAlignTwoUIButtonHorizontallyAlignedinAXibUsingSizeClassandAutoLayout【发布时间】:2015-09-1506:50:17【问题描述】:如何使用SizeClass和AutoLayout在Xib中水平对齐两个UI按钮。... 查看详情

连接两个以上的表时,Oracle 连接消除无法按预期工作

】连接两个以上的表时,Oracle连接消除无法按预期工作【英文标题】:Oraclejoineliminationnotworkingasexpectedwhenjoiningmorethantwotables【发布时间】:2017-11-0616:11:16【问题描述】:连接两个表时,连接消除工作正常:SQL>setlines200;SQL>SQL&... 查看详情

在材料设计中水平对齐元素

】在材料设计中水平对齐元素【英文标题】:horizontallyalignelementsinmaterialdesign【发布时间】:2016-03-0106:48:26【问题描述】:我有一个包含两个组件的简单工具栏:一个h1和一个菜单按钮。我希望h1居中在工具栏和左侧的菜单按钮。... 查看详情

停止视图在相对布局中水平重叠

】停止视图在相对布局中水平重叠【英文标题】:Stopviewsoverlappinghorizontallyinarelativelayout【发布时间】:2016-04-2815:57:40【问题描述】:作为日历的一部分,我有两个视图,一个日视图和一个事件视图。我希望我的事件以宽度填充... 查看详情

Sencha Touch - 在面板中水平放置而不是垂直放置的标签

】SenchaTouch-在面板中水平放置而不是垂直放置的标签【英文标题】:SenchaTouch-Labelshorizontallyplacedinsteadofverticallyinapanel【发布时间】:2011-12-0515:31:47【问题描述】:我想在垂直布局中放置两个标签、一个文本字段和一个按钮。但是... 查看详情

VBA Excel在多行中水平对齐图片

】VBAExcel在多行中水平对齐图片【英文标题】:VBAExcelAlignPicturesHorizontallyinMultipleRows【发布时间】:2020-11-2713:44:47【问题描述】:从我尝试修改的一些示例VBA代码中,我的目标是使用VBA学习Excel,并希望获得指导以水平对齐图片,... 查看详情

在 Spring Data JPA 中使用注释连接两个以上的表

】在SpringDataJPA中使用注释连接两个以上的表【英文标题】:JoinmorethantwotablesusingAnnotationsinSpringDataJPA【发布时间】:2018-10-0104:24:03【问题描述】:我有三个实体:A、B和C,它们之间的关系如下:classA@ManyToManyList<B>bs;//otherattrib... 查看详情

在 postgresql 中水平分片的好方法是啥

】在postgresql中水平分片的好方法是啥【英文标题】:whatisagoodwaytohorizontalshardinpostgresql在postgresql中水平分片的好方法是什么【发布时间】:2010-11-0222:51:46【问题描述】:什么是postgresql中水平分片的好方法1.pgpool22.gridsql这是使用... 查看详情

在 iOS 中水平读取 UIWebView 中的 PDF

】在iOS中水平读取UIWebView中的PDF【英文标题】:PDFReadinginUIWebViewwithhorinzontallyiniOS【发布时间】:2014-05-2304:36:34【问题描述】:用于在iOS中水平读取UIWebView中的PDF文件。所以我写了以下内容以添加UIScrollView以水平滚动。UIScrollView*s... 查看详情

java示例代码_在javafx中水平增长进度条

java示例代码_在javafx中水平增长进度条 查看详情

Jquery在行路径中水平动画六个图像

】Jquery在行路径中水平动画六个图像【英文标题】:JqueryAnimatesiximagehorizontallyinlinepath【发布时间】:2016-02-2212:33:03【问题描述】:我已经创建了带有3个图像的图像动画,其中最初只显示1个图像,然后在第一个图像的动画完成后... 查看详情

如何在 UINavigationBar 中水平定位 backIndicatorImage

】如何在UINavigationBar中水平定位backIndicatorImage【英文标题】:HowtohorizontallypositionthebackIndicatorImageinUINavigationBar【发布时间】:2016-01-0416:26:44【问题描述】:我使用此代码将插图添加到导航栏的backIndicator图像。但是,这仅适用于... 查看详情

SQL Server UNION ALL 合并连接(连接)太慢

】SQLServerUNIONALL合并连接(连接)太慢【英文标题】:SQLServerUNIONALLMergeJoin(Concatenation)tooslow【发布时间】:2017-08-1715:56:58【问题描述】:我有一个选择查询,它在两个具有相同结构的表(列和主键,它们具有不同的非聚集索引)... 查看详情

如何在 Android 中水平滚动 ImageView?

】如何在Android中水平滚动ImageView?【英文标题】:HowtoscrollImageViewhorizontalinAndroid?【发布时间】:2019-01-2503:59:42【问题描述】:在我的应用程序中,我想将图像从服务器显示到ImageView。我的imageView填满整个屏幕,为此我使用android... 查看详情

在 matplotlib 中水平显示单选按钮

】在matplotlib中水平显示单选按钮【英文标题】:DisplayingRadiobuttonshorizontallyinmatplotlib【发布时间】:2019-08-0108:51:14【问题描述】:我正在使用matplotlib.widgets在我的小部件中创建单选按钮,即将出现的按钮垂直堆叠,我希望它们水... 查看详情

如何在 JavaScript 中水平翻转图像

】如何在JavaScript中水平翻转图像【英文标题】:HowtohorizontiallyflipaimageinJavaScript【发布时间】:2022-01-2211:30:48【问题描述】:我正在尝试翻转从网络摄像头流中捕获的图像。CSS中的网络摄像头view被翻转,但是当我拍摄快照时,它... 查看详情

如何在jquery中水平滚动textarea?

】如何在jquery中水平滚动textarea?【英文标题】:howtoscrolltextareahorizontallyinjquery?【发布时间】:2013-10-2122:00:50【问题描述】:我想滚动输入字段。所以我用谷歌搜索我发现我们不能滚动输入字段,但是我们可以水平滚动文本区域... 查看详情