SQL查询 - 多个联接作为列返回

时间:2017-03-28 14:46:36

标签: sql sql-server join

首先,我在SQL上很糟糕,这证明了这一点。最终目标是我有一个数据库(What' s Up Gold),我试图将所有设备导出并在此过程中获得一些关键字段。我可以通过下面的查询获得我想要的所有数据。但是,由于连接,它会返回找到匹配项的重复行。理想情况下,我希望将每个唯一项添加为一列,以便每个设备留下一行,并根据需要添加所有属性,即使它是空的。

SELECT Device.sDisplayName, NetworkInterface.sNetworkAddress, NetworkInterface.sNetworkName, DeviceGroup.sGroupName, 
ActiveMonitorType.sMonitorTypeName, ActionPolicy.sPolicyName, Device.sNote , DeviceAttribute.sValue
FROM NetworkInterface  join Device  ON (NetworkInterface.nDeviceID = Device.nDeviceID) 
left outer join dbo.DeviceType   on Device.nDeviceTypeID = DeviceType.nDeviceTypeID 
left outer join dbo.PivotDeviceToGroup  on PivotDeviceToGroup.nDeviceID = Device.nDeviceID 
left outer join dbo.DeviceGroup  on  DeviceGroup.nDeviceGroupID = PivotDeviceToGroup.nDeviceGroupID 
left outer join dbo.PivotActiveMonitorTypeToDevice  on PivotActiveMonitorTypeToDevice.nDeviceID = Device.nDeviceID 
left outer join dbo.ActiveMonitorType  on ActiveMonitorType.nActiveMonitorTypeID = PivotActiveMonitorTypeToDevice.nActiveMonitorTypeID 
left outer join ActionPolicy ON Device.nActionPolicyID = ActionPolicy.nActionPolicyID 
left outer JOIN DeviceAttribute ON Device.nDeviceID = DeviceAttribute.nDeviceID 
WHERE Device.bRemoved = 0 AND sMonitorTypename NOT LIKE '%Interface%' ORDER BY sNetworkName

示例返回数据:

    sDisplayName        sNetworkAddress sGroupName      sMonitorTypeName    sPolicyName sNote   sValue
    Portland DCI South  10.0.0.1        Main WAN MAP    Ping                DCI 10 Gig Alert    Added from Discovery on Mon Sep 26 12:46:30 2011    Portland HQ Data Center
    Portland DCI South  10.0.0.1        Main WAN MAP    Ping                DCI 10 Gig Alert    Added from Discovery on Mon Sep 26 12:46:30 2011    PDX Data Center 10 Gig DCI South
    Portland DCI South  10.0.0.1        Main WAN MAP    Ping                DCI 10 Gig Alert    Added from Discovery on Mon Sep 26 12:46:30 2011    ETH10000-XXXXXXX
    Portland DCI South  10.0.0.1        Main WAN MAP    Ping                DCI 10 Gig Alert    Added from Discovery on Mon Sep 26 12:46:30 2011    CenturyLink - 1-888-345-4762 option 2

我希望看到的是sValue列中的每个唯一商品都会以sValue1 sValue2等方式返回。这样我就离开了每个唯一网络设备使用一行,sNetworkAddress字段使其唯一。

所需的查询输出:

    sDisplayName        sNetworkAddress sGroupName      sMonitorTypeName    sPolicyName         sNote                                               sValue1                 sValue2                             sValue3             sValue4
    Portland DCI South  10.0.0.1        Main WAN MAP    Ping                DCI 10 Gig Alert    Added from Discovery on Mon Sep 26 12:46:30 2011    Portland HQ Data Center PDX Data Center 10 Gig DCI South    ETH10000-XXXXXXX    CenturyLink - 1-888-345-4762 option 2

更新了查询以最小化选择

    SELECT d.sDisplayName
        , ni.sNetworkAddress
        , ni.sNetworkName
        , d.sNote 
        , da.sValue
    FROM NetworkInterface ni
    INNER join Device d ON ni.nDeviceID = d.nDeviceID 
        AND d.bRemoved = 0
    left outer join dbo.PivotDeviceToGroup pdg on pdg.nDeviceID = d.nDeviceID 
        left outer join dbo.DeviceGroup dg on dg.nDeviceGroupID = pdg.nDeviceGroupID 
    left outer join dbo.PivotActiveMonitorTypeToDevice pamtd on pamtd.nDeviceID = d.nDeviceID 
        left outer join dbo.ActiveMonitorType amt on amt.nActiveMonitorTypeID = pamtd.nActiveMonitorTypeID 
            AND amt.sMonitorTypename NOT LIKE '%Interface%' 
    left outer join ActionPolicy ap ON ap.nActionPolicyID = d.nActionPolicyID
    left outer JOIN DeviceAttribute da ON da.nDeviceID = d.nDeviceID
    ORDER BY ni.sNetworkName

使用Pivot更新查询

        SELECT d.sDisplayName
            , ni.sNetworkAddress
            , ni.sNetworkName
            , d.sNote 
            , da.sValue
        INTO #TEMP
        FROM NetworkInterface ni
        INNER join Device d ON ni.nDeviceID = d.nDeviceID 
            AND d.bRemoved = 0
        left outer join dbo.PivotDeviceToGroup pdg on pdg.nDeviceID = d.nDeviceID 
            left outer join dbo.DeviceGroup dg on dg.nDeviceGroupID = pdg.nDeviceGroupID 
        left outer join dbo.PivotActiveMonitorTypeToDevice pamtd on pamtd.nDeviceID = d.nDeviceID 
            left outer join dbo.ActiveMonitorType amt on amt.nActiveMonitorTypeID = pamtd.nActiveMonitorTypeID 
                AND amt.sMonitorTypename NOT LIKE '%Interface%' 
        left outer join ActionPolicy ap ON ap.nActionPolicyID = d.nActionPolicyID
        left outer JOIN DeviceAttribute da ON da.nDeviceID = d.nDeviceID


    DECLARE @cols AS NVARCHAR(MAX),
        @query  AS NVARCHAR(MAX);

    SET @cols = STUFF((SELECT distinct ',' + QUOTENAME(c.sValue) 
                FROM #TEMP c
                FOR XML PATH(''), TYPE
                ).value('.', 'NVARCHAR(MAX)') 
            ,1,1,'')

    set @query = 'SELECT sDisplayName, sNetworkAddress, sNote' + @cols + ' from 
                (
                    SELECT 
                        DisplayName
                        ,sNetworkAddress
                        ,sNetworkName
                        ,sNote 
                        ,sValue
                    from temp
               ) x
                pivot 
                (
                     max(sNote)
                    for sValue in (' + @cols + ')
                ) p '


    execute(@query)

    drop table #TEMP

返回错误:

    (2195 row(s) affected)
    Msg 1038, Level 15, State 4, Line 11
    An object or column name is missing or empty. For SELECT INTO statements, verify each column has a name. For other statements, look for empty alias names. Aliases defined as "" or [] are not allowed. Change the alias to a valid name.
    Msg 1038, Level 15, State 4, Line 53
    An object or column name is missing or empty. For SELECT INTO statements, verify each column has a name. For other statements, look for empty alias names. Aliases defined as "" or [] are not allowed. Change the alias to a valid name.

2 个答案:

答案 0 :(得分:0)

这里需要的是一个数据透视表。

如果每个项目在查询中返回的记录数量相同,或者您知道可能的最大值,则可以使用简单的数据透视表。但是,如果您有最大未知记录数,则需要使用动态数据透视表。

这是指向此网站上另一个问题的链接,其中显示了如何创建动态数据透视表:SQL Server dynamic PIVOT query?

SELECT d.sDisplayName
    , ni.sNetworkAddress
    , ni.sNetworkName
    , d.sNote 
    , da.sValue
INTO #TEMP
FROM NetworkInterface ni
INNER join Device d ON ni.nDeviceID = d.nDeviceID 
    AND d.bRemoved = 0
left outer join dbo.PivotDeviceToGroup pdg on pdg.nDeviceID = d.nDeviceID 
    left outer join dbo.DeviceGroup dg on dg.nDeviceGroupID = pdg.nDeviceGroupID 
left outer join dbo.PivotActiveMonitorTypeToDevice pamtd on pamtd.nDeviceID = d.nDeviceID 
    left outer join dbo.ActiveMonitorType amt on amt.nActiveMonitorTypeID = pamtd.nActiveMonitorTypeID 
        AND amt.sMonitorTypename NOT LIKE '%Interface%' 
left outer join ActionPolicy ap ON ap.nActionPolicyID = d.nActionPolicyID
left outer JOIN DeviceAttribute da ON da.nDeviceID = d.nDeviceID


DECLARE @cols AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX);

SET @cols = STUFF((SELECT distinct ',' + QUOTENAME(c.sValue) 
            FROM temp c
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT sDisplayName, sNetworkAddress, sNote' + @cols + ' from 
            (
                SELECT 
                    DisplayName
                    ,sNetworkAddress
                    ,sNetworkName
                    ,sNote 
                    ,sValue
                from temp
           ) x
            pivot 
            (
                 max(sNote)
                for sValue in (' + @cols + ')
            ) p '


execute(@query)

drop table temp

答案 1 :(得分:0)

您的设备似乎有多个DeviceAttribute.sValues。因此,您的重复行是与您的设备关联的其他行。

一些建议/说明:

1)为了便于阅读,请为表格添加别名。

2)在JOIN条件下保持一致。有些是ON x = y,有些是ON y = x。它会更容易看到你实际上正在加入的东西。

3)将您的WHERE条件移动到与其关联的JOIN中。如果您在JOERE中的WHERE vs中进行过滤,则可能会得到不一致的结果。

4)双击PivotDeviceToGroup / DeviceGroup和PivotActiveMonitorTypeToDevice / ActiveMonitorType的JOIN组。他们回来了你期望他们返回的东西吗?由于两者都是双LEFT JOIN,因此您将返回pdtg中与pamtd中的dg和行不匹配的行,这些行与amt LIKE'%Interface%',amt data为NULL。

示例:

SELECT d.sDisplayName
    , ni.sNetworkAddress
    , ni.sNetworkName
    , dg.sGroupName
    , amt.sMonitorTypeName
    , ap.sPolicyName
    , d.sNote 
    , da.sValue
FROM NetworkInterface ni
INNER join Device d ON ni.nDeviceID = d.nDeviceID 
    AND d.bRemoved = 0
-- left outer join dbo.DeviceType dt on dt.nDeviceTypeID = d.nDeviceTypeID  --- <<< You aren't using this table. Can you remove the JOIN?
left outer join dbo.PivotDeviceToGroup pdg on pdg.nDeviceID = d.nDeviceID 
    left outer join dbo.DeviceGroup dg on dg.nDeviceGroupID = pdg.nDeviceGroupID 
left outer join dbo.PivotActiveMonitorTypeToDevice pamtd on pamtd.nDeviceID = d.nDeviceID 
    left outer join dbo.ActiveMonitorType amt on amt.nActiveMonitorTypeID = pamtd.nActiveMonitorTypeID 
        AND amt.sMonitorTypename NOT LIKE '%Interface%' 
left outer join ActionPolicy ap ON ap.nActionPolicyID = d.nActionPolicyID
left outer JOIN DeviceAttribute da ON da.nDeviceID = d.nDeviceID

ORDER BY ni.sNetworkName

编辑:这很快就会成为一个相当复杂的PIVOT。但玩起来很有趣。我没有任何数据可以测试,但看看这样的东西是否有效。

 DECLARE @colNames nvarchar(max)
  , @query nvarchar(max)
 DECLARE @tq1 TABLE ( 
    sDisplayName varchar(max)
    , sNetworkAddress varchar(max)
    , sNetworkName varchar(max)
    , sGroupName varchar(max)
    , sMonitorType varchar(max)
    , sPolicyName varchar(max)
    , sNote varchar(max)
    , sValue varchar(max)
  )

 INSERT INTO @tq1 
 SELECT d.sDisplayName
  , ni.sNetworkAddress
  , ni.sNetworkName
  , dg.sGroupName
  , amt.sMonitorType
  , ap.sPolicyName
  , d.sNote 
  , da.sValue
 FROM dbo.Device d
 INNER JOIN dbo.NetworkInterface ni ON ni.nDeviceID = d.nDeviceID    
 LEFT OUTER JOIN dbo.PivotDeviceToGroup pdg ON pdg.nDeviceID = d.nDeviceID
  INNER JOIN dbo.DeviceGroup dg ON dg.nDeviceGroupID = pdg.nDeviceGroupID 
 LEFT OUTER JOIN dbo.PivotActiveMonitorTypeToDevice pamtd ON  pamtd.nDeviceID = d.nDeviceID 
  INNER join dbo.ActiveMonitorType amt on amt.nActiveMonitorTypeID = pamtd.nActiveMonitorTypeID
   AND amt.sMonitorTypename NOT LIKE '%Interface%'
 LEFT OUTER JOIN dbo.ActionPolicy ap ON ap.nActionPolicyID = d.nActionPolicyID
 LEFT JOIN DeviceAttribute da ON d.nDeviceID = da.nDeviceID 
 WHERE d.bRemoved = 0

 SELECT @colNames = STUFF( (SELECT DISTINCT ',' + QUOTENAME(sValue) FROM @tq1 FOR XML PATH(''), TYPE ).value('.','nvarchar(max)'),1,1,'' )
 SET @query = 'SELECT * FROM 
   (
    SELECT sDisplayName
     , sNetworkAddress
     , sNetworkName
     , sGroupName
     , sMonitorType
     , sPolicyName
     , sNote 
     , sValue
    FROM @tq1
   ) t1
   PIVOT
   (
    max(sValue) FOR sValue IN (' + @colNames + ')
   ) p 
   ORDER BY sNetworkName
   '
 EXECUTE ( @query )