如何在sql中使用窗口函数来带来不同的值?
Posted
技术标签:
【中文标题】如何在sql中使用窗口函数来带来不同的值?【英文标题】:How to use window functions in sql to bring distinct values? 【发布时间】:2014-04-06 07:55:28 【问题描述】:我有这个查询要带上公司名称及其前 5 个联系人姓名和前 5 个电话号码。
当我只带联系人或只带电话时,它工作正常,但当我尝试带两个时,返回的所有值都不是不同的(例如,每家公司有不止一行)。 我认为它与分区有关,但我不知道它是什么。
有人可以帮我吗:
-
修复此查询。
了解修复的含义。
查询:
select
p.company_name,
p.Contact_1, p.Contact_2, p.Contact_3, p.Contact_4, p.Contact_5,
p.Phone_1, p.Phone_2, p.Phone_3, p.Phone_4, p.Phone_5
from
(
select contact.first_name + ' ' + contact.last_name as contact_name,
phone.display_phone,
company.company_name,
'Contact_'+
cast(row_number() over(partition by relation.company_id
order by contact.first_name, contact.last_name) as varchar(50)) row,
'Phone_'+
cast(row_number() over(partition by phone.contact_id
order by phone.display_phone) as varchar(50)) row2
from contacts company
left join contact_company_relation_additional_information relation
on company.id = relation.company_id and relation.ContactCompanyRelation_IsActive = 1
left join contacts contact
on relation.contact_id = contact.id and contact.is_company = 0 and contact.is_active = 1
left join contact_phones phone on company.id = phone.contact_id and phone.is_active = 1
where company.is_company = 1 and company.is_active = 1
) d
pivot
(
max(contact_name)
for row in (Contact_1, Contact_2, Contact_3, Contact_4, Contact_5)
) x
pivot
(
max(display_phone)
for row2 in (Phone_1, Phone_2, Phone_3, Phone_4, Phone_5)
) p
这里是重复行的 sql fiddle 链接:Contacts and Phones
以下是仅包含联系人或仅电话的查询链接,每个公司都有一行:
Contacts only Phones only【问题讨论】:
【参考方案1】:为了获得您想要的结果,我建议采用稍微不同的方法。由于您想在两列数据Contacts
和Phone
上进行旋转,因此我首先将这些列取消旋转为多行,然后应用 PIVOT - 我认为这样做更容易,然后尝试应用 PIVOT 两次。
我看到了一些我会在您当前的查询中解决的问题。连接到所有表的查询的主要部分有几处需要更改。首先,我只会创建一个Row
列:
row_number() over(partition by relation.company_id
order by contact.first_name, contact.last_name) row
此列将由contact_company_relation
表中的company_Id
分区。这个新的行号将用于Contact
和Phone
数字列。
其次,您当前返回Phone
号码的加入似乎不正确。您当前的代码使用主要的company
id,但您想加入每个联系人。更改您的代码:
left join contact_phones phone
on company.id = phone.contact_id
到:
left join contact_phones phone
on contact.id = phone.contact_id
这将使您的子查询:
select
contact.first_name + ' ' + contact.last_name as contact_name,
phone.display_phone,
company.company_name,
row_number() over(partition by relation.company_id
order by contact.first_name, contact.last_name) row
from contacts company
left join contact_company_relation relation
on company.id = relation.company_id
left join contacts contact
on relation.contact_id = contact.id
and contact.is_company = 0
left join contact_phones phone
on contact.id = phone.contact_id -- change to join on contact
where company.is_company = 1;
见SQL Fiddle with Demo。数据现在看起来像:
| CONTACT_NAME | DISPLAY_PHONE | COMPANY_NAME | ROW |
|--------------|---------------|--------------|-----|
| Ben Gurion | 2222222 | Analist | 1 |
| Ofer Jerus | 3333333 | Analist | 2 |
| Ori Reshef | 1111111 | Analist | 3 |
一旦您获得了带有行号的数据,您就可以将display_phone
和company_name
还原为多行而不是列。您没有指定您使用的 SQL Server 版本,但您可以使用 UNPIVOT 或 CROSS APPLY 来执行此操作。当您取消透视数据时,您将使用 Row
值来关联每个 contact
和 phone
对 - 这确保每个联系人仍与正确的电话号码相关联。代码类似于:
;with cte as
(
-- query from above here
)
select compnay_name, col, value
from
(
select company_name,
col = col+'_'+cast(row as varchar(50)),
value
from cte
cross apply
(
select 'Contact', Contact_name union all
select 'Phone', display_phone
) c (col, value)
) src;
见SQL Fiddle with Demo。数据现在将采用每个公司名称、联系人和电话具有多行的格式:
| COMPANY_NAME | COL | VALUE |
|--------------|-----------|------------|
| Analist | Contact_1 | Ben Gurion |
| Analist | Phone_1 | 2222222 |
| Analist | Contact_2 | Ofer Jerus |
| Analist | Phone_2 | 3333333 |
| Analist | Contact_3 | Ori Reshef |
| Analist | Phone_3 | 1111111 |
最后一步是添加 PIVOT 函数来制作最终代码:
;with cte as
(
select
contact.first_name + ' ' + contact.last_name as contact_name,
phone.display_phone,
company.company_name,
row_number() over(partition by relation.company_id
order by contact.first_name, contact.last_name) row
from contacts company
left join contact_company_relation relation
on company.id = relation.company_id
left join contacts contact
on relation.contact_id = contact.id
and contact.is_company = 0
left join contact_phones phone
on contact.id = phone.contact_id -- change to join on contact
where company.is_company = 1
)
select company_name,
contact_1, contact_2, contact_3, contact_4, contact_5,
phone_1, phone_2, phone_3, phone_4, phone_5
from
(
select company_name,
col = col+'_'+cast(row as varchar(50)),
value
from cte
cross apply
(
select 'Contact', Contact_name union all
select 'Phone', display_phone
) c (col, value)
) src
pivot
(
max(value)
for col in (contact_1, contact_2, contact_3, contact_4, contact_5,
phone_1, phone_2, phone_3, phone_4, phone_5)
) p;
见SQL Fiddle with Demo。最终结果如下:
| COMPANY_NAME | CONTACT_1 | CONTACT_2 | CONTACT_3 | CONTACT_4 | CONTACT_5 | PHONE_1 | PHONE_2 | PHONE_3 | PHONE_4 | PHONE_5 |
|--------------|------------|------------|------------|-----------|-----------|---------|---------|---------|---------|---------|
| Analist | Ben Gurion | Ofer Jerus | Ori Reshef | (null) | (null) | 2222222 | 3333333 | 1111111 | (null) | (null) |
| Bar Net | Dima Brods | Maya Leshe | Yossi Farc | (null) | (null) | 7777777 | 4444444 | 6666666 | (null) | (null) |
【讨论】:
以上是关于如何在sql中使用窗口函数来带来不同的值?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 SQL Server 中使用带有框架的窗口函数执行 COUNT(DISTINCT)