如何将复杂值作为列

时间:2014-02-27 15:23:29

标签: sql sql-server

我有三张桌子:

  • contacts
  • contact_phones
  • phone_types

contacts中的每个联系人在表contact_phones中都有多个电话号码,每个电话号码都有一个type_id来自表phone_types(移动电话,办公室,家庭等) )

我需要一个查询,为每个联系人提供一行,为每种手机类型添加一列。

对于每个电话类型列,我需要一个逗号分隔的字符串,其中包含此联系人从表contact_phones获得的所有电话号码。

例如:

first_name  last_name   Mobile_Phones           Office_Phones   
----------  ---------   ----------------------  -------------
Amancio     Ortega      0501111111,0502222222   031111111,032222222         
Avi         Zohar       0503333333              033333333
Beat        Hirt        0504444444,0505555555   NULL
Ben         Gurion      0501234567,05076545321  034444444,035555555
Dany        Azriel      0506764879,05065587436  034847968

可以吗?

3 个答案:

答案 0 :(得分:0)

您正在寻找 pivot 数据。有几种解决方案,复杂程度越来越高(就移动部件的数量而言):

  • 使用PIVOT次查询。为此,您必须事先知道所有列;
  • PIVOT与动态SQL结合使用,例如参见this question;
  • 保持查询中的数据不变,并使用SSRS和create a matrix;

答案 1 :(得分:0)

这应该让你开始:

DECLARE @CONTACTS TABLE (ID INT IDENTITY(1,1), FNAME VARCHAR(100), LNAME VARCHAR(100))
DECLARE @CONTACTS_PHONE TABLE (ID INT IDENTITY(1,1), CONTACTID INT, TYPEID INT, PHONE VARCHAR(10))
DECLARE @PHONETYPE TABLE (ID INT IDENTITY(1,1), PHONETYPE VARCHAR(100))

INSERT INTO @CONTACTS (FNAME,LNAME) 
SELECT 'JIGGS', 'JEDI'

INSERT INTO @PHONETYPE (PHONETYPE)
SELECT 'MOBILE' UNION
SELECT 'HOME' UNION
SELECT 'OFFICE'

INSERT INTO @CONTACTS_PHONE (CONTACTID, TYPEID, PHONE)
SELECT 1, 1, '5555555' UNION
SELECT 1, 1, '6666666' UNION
SELECT 1, 2, '0000000' UNION
SELECT 1, 2, '1111111' UNION
SELECT 1, 3, '4444444'

;WITH PIVOTSTUFF
AS
(
SELECT *
FROM (SELECT DISTINCT T.CONTACTID, PT.PHONETYPE, 
               PHONES = REPLACE(STUFF( 
                (   SELECT ',' + PHONE AS [data()] 
                    FROM @CONTACTS_PHONE AS X
                    WHERE X.CONTACTID = T.CONTACTID 
                    AND X.TYPEID = T.TYPEID
                    ORDER BY PHONE 
                    FOR XML PATH ('') ), 1, 1, ''), ' ,', ',')
        FROM @CONTACTS_PHONE AS T
        JOIN @PHONETYPE PT
           ON T.TYPEID=PT.ID) DATATABLE
PIVOT(MIN([PHONES])
      FOR PHONETYPE IN ([HOME],[MOBILE],[OFFICE])) PIVOTTABLE
)

SELECT C.ID, C.FNAME, C.LNAME, T.HOME, T.MOBILE, T.OFFICE
FROM PIVOTSTUFF T
JOIN @CONTACTS C
   ON T.CONTACTID=C.ID

答案 2 :(得分:0)

编辑:您的原始帖子听起来好像您试图从逗号分隔列表中检索多个列,而不是相反。下面的代码旨在扭转这一过程。

=============================================== ==================================

我修改了一些类似问题的代码:让我知道这是否有帮助。正如评论中所指出的,这需要一个名为Numbers的表:我将它作为临时表并将其加载到我的下面的脚本中,但是最好将其永久化并加载一次。

-- Test Data
CREATE TABLE Contacts 
  (
    Contacts_ID INT IDENTITY(1,1) NOT NULL,
    First_Name VARCHAR(100) NOT NULL,
    Last_Name VARCHAR(100) NOT NULL,
    Mobile_Phones VARCHAR(100) NULL,
    Office_Phones VARCHAR(100) NULL
  )

INSERT INTO Contacts (First_Name, Last_Name, Mobile_Phones, Office_Phones)
SELECT 'Amancio',     'Ortega',      '0501111111,0502222222',   '031111111,032222222'       UNION
SELECT 'Avi',         'Zohar',       '0503333333',              '033333333'                 UNION
SELECT 'Beat',        'Hirt',        '0504444444,0505555555',   NULL                        UNION
SELECT 'Ben',         'Gurion',      '0501234567,05076545321',  '034444444,035555555'       UNION
SELECT 'Dany',        'Azriel',      '0506764879,05065587436',  '034847968'             


-- Numbers table (for use in the formula): I would recommend creating a permanent table     and populating it once.  Make sure your count is higher than the length of your phone number     columns.
CREATE TABLE #Numbers (Number INT NOT NULL)
INSERT INTO #Numbers (Number) SELECT 1

DECLARE @Limit INT
DECLARE @Counter INT
SET @Limit = 101
SET @Counter = (SELECT MAX(Number) FROM #Numbers)

WHILE @Counter < @Limit
  BEGIN
    INSERT INTO #Numbers (Number) SELECT MAX(Number) + 1 FROM #Numbers
    SET @Counter = (SELECT MAX(Number) FROM #Numbers)
  END

--SELECT * FROM #Numbers

-- Retrieve the information
SELECT 
    Contacts_ID,
    First_Name,
    Last_Name,
    PhoneType,
    CAST(LTRIM(RTRIM(NULLIF(SUBSTRING(',' + PhoneNumber + ',' , Number , CHARINDEX(',' ,     ',' + PhoneNumber + ',' , Number) - Number) , ''))) AS VARCHAR(10)) PhoneNumber
FROM #Numbers,
    (SELECT 
        Contacts.Contacts_ID, 
        Contacts.First_Name,
        Contacts.Last_Name,
        Contacts.Mobile_Phones,
        Contacts.Office_Phones
    FROM Contacts
    ) P
UNPIVOT
    (PhoneNumber FOR PhoneType IN 
    (
        Mobile_Phones,  Office_Phones
    )) AS UP
WHERE 
    Number <= Len(',' + PhoneNumber + ',') AND SubString(',' + PhoneNumber + ',' ,     Number - 1, 1) = ',' AND 
    CharIndex(',' , ',' + PhoneNumber+ ',' , Number) - Number > 0


-- Clean up
/*
DROP TABLE Contacts
DROP TABLE #Numbers
*/