如何为列添加具有相同值的多个行

时间:2017-04-27 16:41:12

标签: database oracle

这可能很难理解我所要求的内容所以我提前道歉。

我尝试连接具有不同主键的数据库中的两个表,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

1 个答案:

答案 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,哈哈。