2017-04-07 102 views
0

我无法回避以下问题的解决方案我的头: 我有三个表(MS SQL):透视查询

+-----------+-------------+ 
| MachineID | MachineName | 
+-----------+-------------+ 
|   1 | Press 1  | 
|   2 | Press 2  | 
|   3 | Press 3  | 
+-----------+-------------+ 

零件

+-----------+-------------+ 
| PartID | PartName | 
+-----------+-------------+ 
|   1 | Part 1  | 
|   2 | Part 2  | 
|   3 | Part 3  | 
+-----------+-------------+ 

MachinePartAssign

+----+-----------+--------+--+ 
| ID | MachineId | PartID | | 
+----+-----------+--------+--+ 
| 1 |   1 |  1 | | 
| 2 |   1 |  2 | | 
| 3 |   1 |  3 | | 
| 4 |   2 |  2 | | 
| 5 |   3 |  2 | | 
| 6 |   3 |  3 | | 
+----+-----------+--------+--+ 

而且多数民众赞成我想我的查询结果: (如果机器和部分被分配,这是这​​样,当存在匹配行MachinePartAssign(或),否则其应该是(或)。我还可以为每个零件/机器组合插入一行到MachinePartAssign并包含一个额外的布尔(位)列(如果这使得它更容易)。那么我仍然需要一个类似的枢轴查询。 (?))

期望结果(真或假,可以用1和0交换如果,可以更容易)

+-----------+--------+--------+--------+ 
|   | Part 1 | Part 2 | Part 3 | 
| Press 1 | true | true | true | 
| Press 2 | false | true | false | 
| Press 3 | false | true | true | 
+-----------+--------+--------+--------+ 

目前IM与在c#环路这样做:第一选择每台机器,然后选择所有部件,然后为该特定机器/部件组合选择MachinePartAssign。如果我得到> 1行回它的真实。这意味着每一台机器/部件都有一个查询。

我敢肯定,那里有应该是一个更优雅的方式。我知道MSSQL提供PIVOT功能,但我不知道如何在我的情况下使用它。

非常感谢!

+0

请格式化你的代码。 什么是您的查询?它存储什么数据库? – BugFinder

+0

完成 - 对不起。 – RambaZamba

+0

以什么为依据是真假错误还是为新闻而来1,2,3,3 @ RambaZamba – Chanukya

回答

0

这里是PIVOT函数的动态版本,如果你将拥有的不仅仅是3部分更在你的零件表:既然你需要在列的每个部分有

declare @cols_part as nvarchar(max), @query as nvarchar(max) 

select @cols_part = stuff((select distinct ',' + quotename(partname) from 
parts for xml path(''), type).value('.', 'nvarchar(max)') ,1,1,'') 

set @query = 'select machinename, ' + @cols_part + ' 
       from 
       (
        select mp.machinename, mp.partname, iif(mpa.ID IS NULL, 
        0, 1) as machinepart 
        from (select m.machineid, m.machinename, p.partid, 
            p.partname from machines m cross join 
          parts p) mp 
        left join machinepartassign mpa on mp.machineid = 
        mpa.machineid and mp.partid = mpa.partid 

       ) x 
       pivot (max(machinepart) for partname in (' + @cols_part + ') 
      )p ' 
select @query -- to check the generated query 
execute sp_executesql @query; 

,即使有没有针对特定机器的零件,我使用机器和零件之间的交叉连接,然后左连接到machinepartassign表,以查找是否有特定车辆的任何零件,如果是,则显示1.

Here你可以看到这个查询的结果。

+0

非常感谢! SQL Server 2008 R2(应该把它放在最初的问题..)不支持IIF,所以我用:“CASE WHEN(mpa.ID IS NULL)然后0 ELSE 1 END AS MACHINEPART”。这应该是一样的。真棒解决方案! – RambaZamba

+0

是的,它应该。那很好!快乐的编码! :) –

1

也许PIVOT这样

DECLARE @Parts AS TABLE (PartID int, PartName varchar(30)) 
INSERT INTO @Parts VALUES 
(1 ,'Part 1'), 
(2 ,'Part 2'), 
(3 ,'Part 3') 

DECLARE @Machine AS TABLE(MachineID int, MachineName varchar(30)) 
INSERT INTO @Machine VALUES (1, 'Press 1'), (2, 'Press 2'), (3, 'Press 3') 

DECLARE @MachinePartAssign AS TABLE(ID int, MachineId int, PartID int ) 
INSERT INTO @MachinePartAssign VALUES (1,1,1),(2,1,2),(3,1,3),(4,2,2),(5,3,2),(6,3,3) 

SELECT MachineName, Isnull([Part 1],0) AS [Part 1], Isnull([Part 2],0) AS [Part 2], isnull([Part 3],0) AS [Part 3] 
FROM 
(
    SELECT m.MachineName, p.PartName, IIF(mpa.ID IS NULL, 0, 1) AS MachinePart FROM @Machine m 
    LEFT JOIN @MachinePartAssign mpa ON m.MachineID = mpa.MachineId 
    LEFT JOIN @Parts p ON p.PartID = mpa.PartID 
) src 
PIVOT 
(
    max(MachinePart) FOR PartName IN ([Part 1], [Part 2], [Part 3]) 
) pvt 

此查询的结果:In rextester

如果表部分可以添加其他物品,你可以使用动态SQL这样的查询

CREATE TABLE #Parts (PartID int, PartName varchar(30)) 
INSERT INTO #Parts VALUES 
(1 ,'Part 1'), 
(2 ,'Part 2'), 
(3 ,'Part 3'), 
(4 ,'Part 4') 

CREATE TABLE #Machine (MachineID int, MachineName varchar(30)) 
INSERT INTO #Machine VALUES (1, 'Press 1'), (2, 'Press 2'), (3, 'Press 3') 

CREATE TABLE #MachinePartAssign (ID int, MachineId int, PartID int ) 
INSERT INTO #MachinePartAssign VALUES (1,1,1),(2,1,2),(3,1,3),(4,2,2),(5,3,2),(6,3,3) 

DECLARE @PivotColumns nvarchar(max) 
SELECT @PivotColumns = STUFF((SELECT concat(',[',p.PartName,']') FROM #Parts p FOR XML PATH ('')),1,1,'') 

DECLARE @HeaderColumns nvarchar(max) 
SELECT @HeaderColumns = STUFF((SELECT concat(', ISNULL([',p.PartName,'],0) AS [', p.PartName,']') FROM #Parts p FOR XML PATH ('')),1,1,'') 

--SELECT @HeaderColumns, @PivotColumns 

DECLARE @sql nvarchar(max) = CONCAT(' 
SELECT MachineName,', @HeaderColumns , 
' FROM 
(
    SELECT m.MachineName, p.PartName, IIF(mpa.ID IS NULL, 0, 1) AS MachinePart FROM #Machine m 
    LEFT JOIN #MachinePartAssign mpa ON m.MachineID = mpa.MachineId 
    LEFT JOIN #Parts p ON p.PartID = mpa.PartID 
) src 
PIVOT 
(
    max(MachinePart) FOR PartName IN (',@PivotColumns,') 
) pvt' 
) 

-- PRINT @sql 

EXEC(@sql) 

DROP TABLE #Machine 
DROP TABLE #Parts 
DROP TABLE #MachinePartAssign 

链接演示:Dynamic pivot

+0

是的,但如果你添加行的部分,你需要改变你的SQL? – bbsimonbb

+0

在这种情况下,您可以使用动态sql查询来计算数据透视表的列数 – TriV

+0

非常感谢! SQL Server 2008 R2(应该在最初的问题中提到这一点)不支持CONCAT或IIF,但我设法结合你和rigertas来满足我的需求。真棒解决方案! – RambaZamba

0

这是一个投影,并且你总是在SQL中进行投影。因为你已经得到了机器和部分的每一个组合单元,这是当你需要一个交叉连接罕见的场合之一,所以......

SELECT m.MachineName, p.PartName, IIF(mpa.id is null, 0, 1) 
FROM Machines m CROSS JOIN Parts p 
LEFT JOIN MachinePartAssign mpa 
ON m.MachineId = mpa.MachineId and p.PartId = mpa.PartId 
ORDER BY m.MachineId, p.PartId 

我想你最好还是构建表在C#中,因为动态sql选项对我来说很难理解和维护。回到您的应用程序中,您只需循环访问结果集,并在每次MachineId更改时吐出一条新行。

这是Rextester的full working example,这是一个很酷的工具!

0

它也可以如果转角柱和VALU科拉姆是一样的:

SELECT MachineName, [Part 1], [Part 2], [Part 3] 
    FROM 
    (
     SELECT m.MachineName, p.PartName 
     FROM dbo.MachinePartAssign a 
     INNER JOIN dbo.Parts p ON a.PartID = p.PartID 
     INNER JOIN dbo.Machines m ON a.MachineID = m.MachineID 
    ) x 
    PIVOT 
    (
     COUNT(PartName) FOR PartName IN ([Part 1], [Part 2], [Part 3]) 
    ) p