Domanda

I have this query to bring a company name and its top 5 contact names and top 5 phone numbers.

It works fine when I bring only contacts or only phones but when I try to bring both all the values returned are not distinct (e.g. there is more then one row for each company). I think it has something to do with the partitions, but I have not idea what it is.

Can someone please help me to:

  1. Fix this query.
  2. Understand what the fix means.

query:

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

Here is a link to sql fiddle with the duplicated rows: Contacts and Phones

Here are links to the queries with only contacts or only phones that bring one row for each company:

È stato utile?

Soluzione

In order to get the result that you want, I would suggest a slightly different approach to this. Since you want to pivot on two columns of data Contacts and Phone, I would first unpivot these columns into multiple rows, then apply the PIVOT - I think it is easier to to that then trying to apply the PIVOT twice.

I see a few things that I would fix in your current query. The main part of you query that is joining to all of the tables has a couple of things to change. First, I would only create one Row column:

row_number() over(partition by relation.company_id 
                 order by contact.first_name, contact.last_name) row

This column would be partitioned by the company_Id in the in the contact_company_relation table. This new row number will be used for both the Contact and the Phone number columns.

Second, your current join to return the Phone number appears to be incorrect. Your current code is using the main company id but you want to join on each contact. Change your code from:

left join contact_phones phone 
  on company.id = phone.contact_id

to:

left join contact_phones phone 
  on contact.id = phone.contact_id  

This will make your subquery:

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;

See SQL Fiddle with Demo. The data will now look like:

| CONTACT_NAME | DISPLAY_PHONE | COMPANY_NAME | ROW |
|--------------|---------------|--------------|-----|
|   Ben Gurion |       2222222 |      Analist |   1 |
|   Ofer Jerus |       3333333 |      Analist |   2 |
|   Ori Reshef |       1111111 |      Analist |   3 |

Once you have the data with the row number, you can unpivot the display_phone and company_name into multiple rows instead of columns. You didn't specify what version of SQL Server you are using but you can use either UNPIVOT or CROSS APPLY to do this. When you unpivot the data you will then use the Row value to associate each contact and phone pair - this makes sure that each contact is still associated with the correct phone number. The code would be similar to:

;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;

See SQL Fiddle with Demo. The data will now be in the format which has multiple rows for each company_name, contact and phone:

| 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 |

The final step would be to add the PIVOT function making the final code:

;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;

See SQL Fiddle with Demo. The final result looks like:

| 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) |
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top