Sql Select phone numbers from a many to many table with different types (mobile, home)

蹲街弑〆低调 提交于 2019-12-06 13:38:38

You could do this in a more complicated way if you have multiple numbers or might have more than home/mobile, but for now, if you only have two numbers, then this will work:

SELECT Name, HomeNumber.Number AS Home, MobileNumber.Number AS Mobile
FROM Person
    LEFT JOIN PersonPhoneNumber HomeMap
        ON Person.ID = HomeMap.Person_ID
    LEFT JOIN PhoneNumber HomeNumber
        ON HomeMap.PhoneNumber_ID = HomeNumber.ID AND HomeNumber.Type = 'home'
    LEFT JOIN PersonPhoneNumber MobileMap
        ON Person.ID = MobileMap.Person_ID
    LEFT JOIN PhoneNumber MobileNumber
        ON MobileMap.PhoneNumber_ID = MobileNumber.ID AND MobileNumber.Type = 'mobile'

BTW, if you dont want people with any numbers, then you can make the PersonPhoneNumber mapping a JOIN instead of a LEFT JOIN

1) First solution: every person can have zero/one (home/mobile) phone number (demo here).

DECLARE @Person TABLE
(
    ID INT PRIMARY KEY,
    Name NVARCHAR(50) NOT NULL
);

DECLARE @PhoneNumber TABLE
(
    ID INT PRIMARY KEY,
    [Type] VARCHAR(10) NOT NULL CHECK([Type] IN ('mobile', 'home')),
    Number VARCHAR(15) NOT NULL CHECK( PATINDEX('%[^0-9]%', Number) = 0 )
);

DECLARE @PersonPhoneNumber TABLE
(
    ID INT IDENTITY(2,2) PRIMARY KEY,
    Person_ID INT NOT NULL, --FOREIGN KEY
    PhoneNumber_ID INT NOT NULL --FOREIGN KEY
);

INSERT  @Person (ID, Name)
SELECT  1, 'Ed'
UNION ALL
SELECT  2, 'Bob';

INSERT  @PhoneNumber (ID, [Type], Number)
SELECT  1, 'home', '1111'
UNION ALL
SELECT  2, 'mobile', '2222'
UNION ALL
SELECT  3, 'home', '3333'
UNION ALL
SELECT  4, 'mobile', '4444';

INSERT  @PersonPhoneNumber (Person_ID, PhoneNumber_ID)
SELECT  1, 1
UNION ALL
SELECT  1, 2
UNION ALL
SELECT  2, 3
UNION ALL
SELECT  2, 4;

SET ANSI_WARNINGS ON;

DECLARE @x XML;
SET     @x = 
(
        SELECT  p.ID AS Person_ID,
                p.Name AS Person_Name,
                pn.[Type], 
                pn.Number
        FROM @Person p  
        INNER JOIN @PersonPhoneNumber ppn ON p.ID = ppn.Person_ID 
        INNER JOIN @PhoneNumber pn ON ppn.PhoneNumber_ID = pn.ID 
        WHERE p.ID IN (1,2) --Here you could write the filter for persons
        FOR XML AUTO,ROOT
);
SELECT  @x;
SELECT  T.XMLCol.value('(@Person_ID)', 'INT') AS Person_ID,
        T.XMLCol.value('(@Person_Name)', 'NVARCHAR(50)') AS Person_Name,
        T.XMLCol.value('(pn[@Type="home"]/@Number)[1]', 'VARCHAR(15)') AS HomePhoneNumber,
        T.XMLCol.value('(pn[@Type="mobile"]/@Number)[1]', 'VARCHAR(15)') AS MobilePhoneNumber
FROM    @x.nodes('//root/p') T(XMLCol);

Results:

Person_ID Person_Name HomePhoneNumber MobilePhoneNumber
--------- ----------- --------------- -----------------
1         Ed          1111            2222
2         Bob         3333            4444

2) Second solution: if every person can have zero, one, two or more (home/mobile) phone numbers then you could use this solution (XML & FLWOR XQuery based):

DECLARE @Person TABLE
(
    ID INT PRIMARY KEY,
    Name NVARCHAR(50) NOT NULL
);

DECLARE @PhoneNumber TABLE
(
    ID INT PRIMARY KEY,
    [Type] VARCHAR(10) NOT NULL CHECK([Type] IN ('mobile', 'home')),
    Number VARCHAR(15) NOT NULL CHECK( PATINDEX('%[^0-9]%', Number) = 0 )
);

DECLARE @PersonPhoneNumber TABLE
(
    ID INT IDENTITY(2,2) PRIMARY KEY,
    Person_ID INT NOT NULL, --FOREIGN KEY
    PhoneNumber_ID INT NOT NULL --FOREIGN KEY
);

INSERT  @Person (ID, Name)
SELECT  1, 'Ed'
UNION ALL
SELECT  2, 'Bob';

INSERT  @PhoneNumber (ID, [Type], Number)
SELECT  1, 'home', '1111'
UNION ALL
SELECT  2, 'mobile', '2222'
UNION ALL
SELECT  3, 'mobile', '3333'
UNION ALL
SELECT  4, 'mobile', '4444';

INSERT  @PersonPhoneNumber (Person_ID, PhoneNumber_ID)
SELECT  1, 1
UNION ALL
SELECT  1, 2
UNION ALL
SELECT  1, 3
UNION ALL
SELECT  2, 3;

SET ANSI_WARNINGS ON;

SELECT  p.*, 
        CONVERT(XML, ca.XMLPhoneNumbers).query('
        for $r in //root/row
        where $r/@Type = "home"
        order by $r/@Number
            return string($r/@Number)
        ').value('.', 'NVARCHAR(4000)') AS HomePhoneNumbers,
        CONVERT(XML, ca.XMLPhoneNumbers).query('
        for $r in //root/row
        where $r/@Type = "mobile"
        order by $r/@Number
            return string($r/@Number)
        ').value('.', 'NVARCHAR(4000)') AS MobilePhoneNumbers,
        ca.XMLPhoneNumbers
FROM    @Person p
CROSS APPLY
(
        SELECT 
            (SELECT pn.[Type], pn.Number
            FROM    @PersonPhoneNumber ppn
            INNER JOIN @PhoneNumber pn ON ppn.PhoneNumber_ID = pn.ID 
            WHERE   ppn.Person_ID = p.ID 
            FOR XML RAW, ROOT) AS XMLPhoneNumbers --RAW generates <row ... /> elements

) ca;

Results:

ID Name  HomePhoneNumbers MobilePhoneNumbers XMLPhoneNumbers
-- ----- ---------------- ------------------ ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1  Ed    1111             2222 3333          <root><row Type="home" Number="1111"/><row Type="mobile" Number="2222"/><row Type="mobile" Number="3333"/></root>
2  Bob                    3333               <root><row Type="mobile" Number="3333"/></root>
select  p.Name
,       pnh.Number as Home
,       pnm.Number as Mobile
from    Person p
left join 
        PersonPhoneNumber pn
on      pn.Person_ID = p.ID
left join    
        PhoneNumber pnh
on      pnh.ID = pn.PhoneNumber_ID
        and phh.Type = 'home'
left join    
        PhoneNumber pnm
on      pnm.ID = pn.PhoneNumber_ID
        and pnm.Type = 'mobile'

Hope this helps


SELECT DISTINCT P.NAME,
                (SELECT DISTINCT PN1.PH_NUMBER
                   FROM PHONE_NUMBER PN1
                  WHERE PN1.ID = PN.ID
                    AND PN1.TYPE = 'Home') AS HOME,
                (SELECT DISTINCT PN1.PH_NUMBER
                   FROM PHONE_NUMBER PN1
                  WHERE PN1.ID = PN.ID
                    AND PN1.TYPE = 'Mobile') AS MOBILE
  FROM PERSON P, PHONE_NUMBER PN
  ORDER BY P.NAME

This will help you

select * from @Person p
left join @PersonPhoneNumber ppn
on p.ID = ppn.PersonID
right join @PhoneNumber pn
on ppn.PhoneNoID = pn.ID
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!