如果你使用 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 |
+ ------ + ------------- + ----------- + ------------- + -------------- + --------------- +
添加一个简单的 group by 给了我们想要的东西
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 个电子邮件地址怎么办?这就是动态 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,哈哈。