解决方法
我将为您的示例中的表提供更有意义的名称,并添加更多行以突出显示该问题.在现实生活中,这些表将是真正的表,当然,不是变量,但我会坚持使用变量来使这个示例脚本易于运行和尝试.我在这个例子中使用sql Server 2008.
@H_403_2@declare @TMain table (empid int); declare @TPhones table (empid int,phone varchar(50)); declare @TLicenses table (empid int,license varchar(50)); declare @TEmails table (empid int,email varchar(100)); insert into @TMain values (1); insert into @TMain values (2); insert into @TMain values (3); insert into @TMain values (4); insert into @TPhones values (1,'5551234'); insert into @TPhones values (2,'5551111'); insert into @TPhones values (2,'5552222'); insert into @TPhones values (2,'5553333'); insert into @TPhones values (2,'5554444'); insert into @TLicenses values (2,'L4455'); insert into @TLicenses values (3,'L7890'); insert into @TEmails values (2,'xxx@abc'); insert into @TEmails values (2,'yyy@abc'); insert into @TEmails values (2,'zzz@abc');简单的变种
@H_403_2@SELECT Main.empid,Phones.phone,Licenses.license,Emails.email FROM @TMain AS Main LEFT JOIN @TPhones AS Phones ON Phones.empid = Main.empid LEFT JOIN @TLicenses AS Licenses ON Licenses.empid = Main.empid LEFT JOIN @TEmails AS Emails ON Emails.empid = Main.empid ORDER BY Main.empid,email;它生成所有行的笛卡尔积和重复行.这是上面查询的结果集.您可以看到empid = 2返回12行,即4个电话乘以3个电子邮件和1个许可证.我的猜测是你只想看到empid = 2的4行.换句话说,对于每个empid,结果应该有最小可能的行数(我将在最后显示正确的结果集).
@H_403_2@empid phone license email 1 5551234 NULL NULL 2 5551111 L4455 xxx@abc 2 5551111 L4455 yyy@abc 2 5551111 L4455 zzz@abc 2 5552222 L4455 xxx@abc 2 5552222 L4455 yyy@abc 2 5552222 L4455 zzz@abc 2 5553333 L4455 xxx@abc 2 5553333 L4455 yyy@abc 2 5553333 L4455 zzz@abc 2 5554444 L4455 xxx@abc 2 5554444 L4455 yyy@abc 2 5554444 L4455 zzz@abc 3 NULL L7890 NULL 4 NULL NULL NULL长变种
我不确定下面提出的方法是否比你的更有效.您必须同时尝试并比较数据的性能.
我们需要一张数字表.
SQL,Auxiliary table of numbers
http://sqlserver2000.databases.aspfaq.com/why-should-i-consider-using-an-auxiliary-numbers-table.html
http://sqlblog.com/blogs/adam_machanic/archive/2006/07/12/you-require-a-numbers-table.aspx
同样,在现实生活中,你将拥有一个合适的数字表,但是对于这个例子,我将使用以下内容:
@H_403_2@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的电话,许可证和电子邮件的数量开始:
@H_403_2@WITH CTE_Rows AS ( SELECT Phones.empid,COUNT(*) AS EmpRows FROM @TPhones AS Phones GROUP BY Phones.empid UNION ALL SELECT Licenses.empid,COUNT(*) AS EmpRows FROM @TLicenses AS Licenses GROUP BY Licenses.empid UNION ALL SELECT Emails.empid,COUNT(*) AS EmpRows FROM @TEmails AS Emails GROUP BY Emails.empid )然后我们计算每个empid的最大行数:
@H_403_2@,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生成给定的行数.我在这里使用Numbers表:
@H_403_2@,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 )然后我们需要向所有带有数据的表添加行号,我们稍后会用它们加入:
@H_403_2@,CTE_Phones AS ( SELECT Phones.empid,ROW_NUMBER() OVER (PARTITION BY Phones.empid ORDER BY phone) AS rn,Phones.phone FROM @TPhones AS Phones ),CTE_Licenses AS ( SELECT Licenses.empid,ROW_NUMBER() OVER (PARTITION BY Licenses.empid ORDER BY license) AS rn,Licenses.license FROM @TLicenses AS Licenses ),CTE_Emails AS ( SELECT Emails.empid,ROW_NUMBER() OVER (PARTITION BY Emails.empid ORDER BY email) AS rn,Emails.email FROM @TEmails AS Emails )现在我们准备将所有这些加入到一起. CTE_RowNumbers具有我们需要的确切行数,因此这里不需要复杂的FULL JOIN,简单的LEFT JOIN就足够了:
@H_403_2@,CTE_Data AS ( SELECT CTE_RowNumbers.empid,CTE_Phones.phone,CTE_Licenses.license,CTE_Emails.email FROM CTE_RowNumbers LEFT JOIN CTE_Phones ON CTE_Phones.empid = CTE_RowNumbers.empid AND CTE_Phones.rn = CTE_RowNumbers.rn LEFT JOIN CTE_Licenses ON CTE_Licenses.empid = CTE_RowNumbers.empid AND CTE_Licenses.rn = CTE_RowNumbers.rn LEFT JOIN CTE_Emails ON CTE_Emails.empid = CTE_RowNumbers.empid AND CTE_Emails.rn = CTE_RowNumbers.rn )我们差不多完成了.我猜,主表有可能没有任何相关数据(没有电话,没有虱子,没有电子邮件),例如我的样本数据中的empid = 4.为了在结果集中获得这些empid,我将把CTE_Data连接到主表:
@H_403_2@SELECT Main.empid,CTE_Data.phone,CTE_Data.license,CTE_Data.email FROM @TMain AS Main LEFT JOIN CTE_Data ON CTE_Data.empid = Main.empid ORDER BY Main.empid,email;要获取完整脚本,只需将此帖子中的所有代码块按照此处显示的顺序放在一起.
这是结果集:
@H_403_2@empid phone license email 1 5551234 NULL NULL 2 5551111 L4455 xxx@abc 2 5552222 NULL yyy@abc 2 5553333 NULL zzz@abc 2 5554444 NULL NULL 3 NULL L7890 NULL 4 NULL NULL NULL