简体   繁体   中英

Populate Column Data based on source table in T-SQL

I'm not sure my question is even worded correctly but here goes.

I have a table called Contacts that has FK references to tables Address, Email, Phone ( these have 1 to many with Contacts ). I need to create a query that will pull all the data and has a column called Contact Method that shows which sub table that row came from.

Contact: ID, AddressID, EmailID, PhoneID
Address: ID, Line1, City, State
Email :  ID, EAddress
Phone :  ID, Number, Extension

I need the resulting table to look like this:

ContactMethod | ID | [Value1] | [Value2] | [Value3]

Address         2      N5980    Onalaska     WI
Email           8     myEmail@
Phone           5     555-5555    1234

Alternatively it could list all the combined columns in a row if that's simpler, I can work with that as well. ie

ContactMethad | ID | Line1 | City | State | ID | EAddress | ID | Number | Extension

I looked at PIVOT , which is neat but doesn't seem to solve my problem by itself. Do I need to combine it with COALESCE ?

Thanks for any help.


EDIT

My data, on table Contact would look like this:

ID | AddressID | PhoneID | EmailID

1      3           null      null
2     null         null      7
3     null          5        null
4     4            null      null
5     null         6         null

The proposed solution works except that I get 3 rows per ID. Make sense?

You can unpivot the data using a CROSS APPLY and VALUES clause to get the result:

select d.ContactMethod, d.id, d.Value1, d.Value2, d.Value3
from contacts c
left join address a
  on c.addressid = a.id
left join email e
  on c.emailid = e.id
left join phone p
  on c.phoneid = p.id
cross apply
(
  values
    ('Address', c.addressid, a.Line1, a.City, a.State),
    ('Email', c.emailid, e.eAddress, '', ''),
    ('Phone', c.phoneid, p.number, cast(p.extension as varchar(10)), '')
) d (ContactMethod, id, Value1, Value2, Value3)

See SQL Fiddle with Demo .

This gives the result:

| CONTACTMETHOD | ID |   VALUE1 |   VALUE2 | VALUE3 |
-----------------------------------------------------
|       Address |  2 |    N5980 | Onalaska |     WI |
|         Email |  8 | myEmail@ |          |        |
|         Phone |  5 | 555-5555 |     1234 |        |

If you want your second result, then you can use multiple joins to get it:

select cm.ContactMethod,
  a.id addressid,
  a.line1,
  a.city,
  a.state,
  e.id emailid,  
  e.eaddress,
  p.id phoneid,
  p.number,
  p.extension
from contacts c
cross join
(
  VALUES ('Address'),('Email'),('Phone')
) cm (ContactMethod)
left join address a
  on c.addressid = a.id
  and cm.ContactMethod = 'Address'
left join email e
  on c.emailid = e.id
  and cm.ContactMethod = 'Email'
left join phone p
  on c.phoneid = p.id
  and cm.ContactMethod = 'Phone';

See SQL Fiddle with Demo . The result is:

| CONTACTMETHOD | ADDRESSID |  LINE1 |     CITY |  STATE | EMAILID | EADDRESS | PHONEID |   NUMBER | EXTENSION |
----------------------------------------------------------------------------------------------------------------
|       Address |         2 |  N5980 | Onalaska |     WI |  (null) |   (null) |  (null) |   (null) |    (null) |
|         Email |    (null) | (null) |   (null) | (null) |       8 | myEmail@ |  (null) |   (null) |    (null) |
|         Phone |    (null) | (null) |   (null) | (null) |  (null) |   (null) |       5 | 555-5555 |      1234 |

Edit #1, based on your changes you can alter the queries to the the following.

The first one with the three value columns, then you can just add a WHERE clause to filter out any null values:

select c.ID, ContactMethod, Value1, Value2, Value3
from contacts c
left join address a
  on c.addressid = a.id
left join email e
  on c.emailid = e.id
left join phone p
  on c.phoneid = p.id
cross apply
(
  values
    ('Address', c.addressid, a.Line1, a.City, a.State),
    ('Email', c.emailid, e.eAddress, null, null),
    ('Phone', c.phoneid, p.number, cast(p.extension as varchar(10)), null)
) d (ContactMethod, id, Value1, Value2, Value3)
where value1 is not null
  or value2 is not null
  or value3 is not null

See SQL Fiddle with Demo . The result is:

 ID | CONTACTMETHOD |            VALUE1 |    VALUE2 | VALUE3 |
---------------------------------------------------------------
|  1 |       Address |             N5980 |  Onalaska |     WI |
|  2 |         Email |          myEmail@ |    (null) | (null) |
|  3 |         Phone |          555-5555 |      1234 | (null) |
|  4 |       Address | 1417 Saint Andrew | La Crosse |     WI |

If you want the results in a single row, then you will want to use the UNPIVOT function:

select *
from
(
  select id,
    case col 
      when 'addressid' then 'address'
      when 'emailid' then 'email'
      when 'phoneid' then 'phone' end ContactMethod,
    contact_id
  from contacts
  unpivot
  (
    contact_id
    for col in (addressid, emailid, phoneid)
  ) unpiv
) c
left join address a
  on c.contact_id = a.id
  and c.ContactMethod = 'Address'
left join email e
  on c.contact_id = e.id
  and c.ContactMethod = 'Email'
left join phone p
  on c.contact_id = p.id
  and c.ContactMethod = 'Phone';

See SQL Fiddle with Demo . The result of this query is:

| ID | CONTACTMETHOD | CONTACT_ID |             LINE1 |      CITY |  STATE | EADDRESS |   NUMBER | EXTENSION |
--------------------------------------------------------------------------------------------------------------
|  1 |       address |          2 |             N5980 |  Onalaska |     WI |   (null) |   (null) |    (null) |
|  2 |         email |          8 |            (null) |    (null) | (null) | myEmail@ |   (null) |    (null) |
|  3 |         phone |          5 |            (null) |    (null) | (null) |   (null) | 555-5555 |      1234 |
|  4 |       address |          3 | 1417 Saint Andrew | La Crosse |     WI |   (null) |   (null) |    (null) |

It is a lot easier to get to the second column layout. For that you just need to join:

SELECT *
FROM dbo.Contact c
JOIN dbo.Address a
ON c.AddressID = a.ID
JOIN dbo. Email e
ON c. EmailID = e.ID
JOIN dbo. Phone p
ON c. PhoneID = p.ID

I just used SELECT * , but you will have to actually list all the columns as you do not want all of them. If you do not necessarily have a row in each child table you need to use LEFT OUTER JOIN instead of just JOIN .

For more details about JOINs checkout this series: http://sqlity.net/en/1146/a-join-a-day-introduction/


If you need multiple rows you can use this:

SELECT *
FROM dbo.Contact c
CROSS JOIN (VALUES('Address','Email','Phone'))X(ContactMethod)
LEFT JOIN dbo.Address a
ON c.AddressID = a.ID
AND X.ContactMethod = 'Address'
LEFT JOIN dbo. Email e
ON c. EmailID = e.ID
AND X.ContactMethod = 'Email'
LEFT JOIN dbo. Phone p
ON c. PhoneID = p.ID
AND X.ContactMethod = 'Phone'

The advantage of going with the "spread out" version is that you do not have to deal with data type incompatibilities.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM