这可能很难理解我所要求的内容所以我提前道歉。
我尝试连接具有不同主键的数据库中的两个表,employee表具有EmployeeID的主键,并且联系表具有ContactID的主键。我尝试在EmployeeID上加入它们,但是我得到了同一个员工的太多重复项,只是与员工关联的不同电子邮件地址。我希望忽略contactID并查询表以创建显示名称,ID的输出,并列出电子邮件地址。
这可能吗?
以下是一个例子:
员工表:
EmployeeID | Employee Name | Address |
____________ ______________ __________
0001 John 123 Ave.
联系表:
ContactID | Email | EmployeeID|
__________ ______________ ______________
0001 abc@email.com 001
0002 bcd@email.com 001
我想要的输出是:
EmployeeID| Name | Email1 | Email2 |
__________ ______________ ______________ _______________
001 | John |abc@email.com| bcd@email.com
答案 0 :(得分:4)
如果您正在使用SQL Server,则可以使用一些动态SQL来完成您想要的任务。
tl; dr :最后的代码块包含您想要的SQL。
首先,我们将硬编码我们的查询以提供我们想要的东西。接下来,我们将介绍如何使用动态SQL让我们的生活更轻松。我将使用以下示例表:
#Employee
+ ------ + ------------- + ----------- +
| EMP_Id | EMP_Name | EMP_Address |
+ ------ + ------------- + ----------- +
| 1 | John Jacob | 123 Ave. |
| 2 | Ermit Schmidt | 101 St. |
+ ------ + ------------- + ----------- +
#Contact
+ ------ + --------------- + ------ +
| CNT_Id | CNT_Email | EMP_Id |
+ ------ + --------------- + ------ +
| 1 | abc@email.com | 1 |
| 2 | bcd@email.com | 1 |
| 3 | es@email.com | 2 |
| 4 | es12@email.com | 2 |
| 5 | es_01@email.com | 2 |
+ ------ + --------------- + ------ +
1)硬代码
首先,我们必须对#Contact
表进行排名,以便我们从每位员工中选择每个电子邮件地址。以下查询就是这样做的
select *
from #Employee emp
left join (
select *, ROW_NUMBER() over (partition by emp_id order by cnt_id) as Ranking
from #Contact
) cnt on cnt.EMP_Id = emp.EMP_Id
并生成一个类似
的表格+ ------ + ------------- + ----------- + ------ + --------------- + ------ + ------- +
| EMP_Id | EMP_Name | EMP_Address | CNT_Id | CNT_Email | EMP_Id | Ranking |
+ ------ + ------------- + ----------- + ------ + --------------- + ------ + ------- +
| 1 | John Jacob | 123 Ave. | 1 | abc@email.com | 1 | 1 |
| 1 | John Jacob | 123 Ave. | 2 | bcd@email.com | 1 | 2 |
| 2 | Ermit Schmidt | 101 St. | 3 | es@email.com | 2 | 1 |
| 2 | Ermit Schmidt | 101 St. | 4 | es12@email.com | 2 | 2 |
| 2 | Ermit Schmidt | 101 St. | 5 | es_01@email.com | 2 | 3 |
+ ------ + ------------- + ----------- + ------ + --------------- + ------ + ------- +
现在我们可以使用Ranking
字段来选择电子邮件,例如
select emp.*
, case cnt.Ranking when 1 then cnt.CNT_Email end as Email_1
, case cnt.Ranking when 2 then cnt.CNT_Email end as Email_2
, case cnt.Ranking when 3 then cnt.CNT_Email end as Email_3
from #Employee emp
left join (
select *, ROW_NUMBER() over (partition by emp_id order by cnt_id) as Ranking
from #Contact
) cnt on cnt.EMP_Id = emp.EMP_Id
这会产生几乎完整的表格:
+ ------ + ------------- + ----------- + ------------- + -------------- + --------------- +
| EMP_Id | EMP_Name | EMP_Address | Email_1 | Email_2 | Email_3 |
+ ------ + ------------- + ----------- + ------------- + -------------- + --------------- +
| 1 | John Jacob | 123 Ave. | abc@email.com | NULL | NULL |
| 1 | John Jacob | 123 Ave. | NULL | bcd@email.com | NULL |
| 2 | Ermit Schmidt | 101 St. | es@email.com | NULL | NULL |
| 2 | Ermit Schmidt | 101 St. | NULL | es12@email.com | NULL |
| 2 | Ermit Schmidt | 101 St. | NULL | NULL | es_01@email.com |
+ ------ + ------------- + ----------- + ------------- + -------------- + --------------- +
添加一个简单的组给我们想要的东西
select emp.*
, max(case cnt.Ranking when 1 then cnt.CNT_Email end) as Email_1
, max(case cnt.Ranking when 2 then cnt.CNT_Email end) as Email_2
, max(case cnt.Ranking when 3 then cnt.CNT_Email end) as Email_3
from #Employee emp
left join (
select *, ROW_NUMBER() over (partition by emp_id order by cnt_id) as Ranking
from #Contact
) cnt on cnt.EMP_Id = emp.EMP_Id
group by emp.EMP_Id
, emp.EMP_Name
, emp.EMP_Address
+ ------ + ------------- + ----------- + ------------- + -------------- + --------------- +
| EMP_Id | EMP_Name | EMP_Address | Email_1 | Email_2 | Email_3 |
+ ------ + ------------- + ----------- + ------------- + -------------- + --------------- +
| 1 | John Jacob | 123 Ave. | abc@email.com | bcd@email.com | NULL |
| 2 | Ermit Schmidt | 101 St. | es@email.com | es12@email.com | es_01@email.com |
+ ------ + ------------- + ----------- + ------------- + -------------- + --------------- +
2)动态SQL
无论员工拥有多少个电子邮件地址,上述大多数查询都保持不变。唯一的部分是在选择部分找到的;我们必须问自己:"我们如何知道要选择多少电子邮件"?在我们的示例中,我们知道我们需要3,但如果有一个员工有4个或8个或100个电子邮件地址怎么办?这就是Dynamic SQL的用武之地。
我们的想法是构建一个带有循环的字符串,该循环构造一个SQL语句,然后我们将执行该语句。首先,我们需要知道应循环多少次迭代,因此我们在#Contact
表中提取最大等级,如此
if OBJECT_ID('tempdb..#max_rank') is not null drop table #max_rank
select top 1 COUNT(*) as Max_Rank
into #max_rank
from #Contact
group by EMP_Id
order by count(*) desc
declare @max_rank int
select @max_rank = Max_Rank from #max_rank
print @max_rank
接下来,我们编写一个循环来构建查询的max(case ...)
部分
declare @sql varchar(max) = ''
declare @iter int = 1
while @iter <= @max_rank
begin
set @sql = @sql + '
, max(case cnt.Ranking when ' + cast(@iter as varchar(max)) + ' then cnt.CNT_Email end) as Email_' + cast(@iter as varchar(max))
set @iter = @iter+1
end
print @sql
然后我们追加不会改变的其余查询
set @sql =
'select emp.*'
+ @sql
+ '
from #Employee emp
left join (
select *, ROW_NUMBER() over (partition by emp_id order by cnt_id) as Ranking
from #Contact
) cnt on cnt.EMP_Id = emp.EMP_Id
group by emp.EMP_Id
, emp.EMP_Name
, emp.EMP_Address'
print @sql
将所有这些放在一起,我们得到完整的代码
if OBJECT_ID('tempdb..#max_rank') is not null drop table #max_rank
select top 1 COUNT(*) as Max_Rank
into #max_rank
from #Contact
group by EMP_Id
order by count(*) desc
declare @max_rank int
select @max_rank = Max_Rank from #max_rank
declare @sql varchar(max) = ''
declare @iter int = 1
while @iter <= @max_rank
begin
set @sql = @sql + '
, max(case cnt.Ranking when ' + cast(@iter as varchar(max)) + ' then cnt.CNT_Email end) as Email_' + cast(@iter as varchar(max))
set @iter = @iter+1
end
set @sql =
'select emp.*'
+ @sql
+ '
from #Employee emp
left join (
select *, ROW_NUMBER() over (partition by emp_id order by cnt_id) as Ranking
from #Contact
) cnt on cnt.EMP_Id = emp.EMP_Id
group by emp.EMP_Id
, emp.EMP_Name
, emp.EMP_Address'
print @sql
exec(@sql)
希望这有帮助!我也希望你能使用SQL Server,哈哈。