C# SQL Server中跨3个表的透视查询

C# SQL Server中跨3个表的透视查询,c#,sql,sql-server,join,C#,Sql,Sql Server,Join,我无法找到以下问题的解决方案: 我有三个表(MS SQL): 机器 +-----------+-------------+ | MachineID | MachineName | +-----------+-------------+ | 1 | Press 1 | | 2 | Press 2 | | 3 | Press 3 | +-----------+-------------+ +-----------+------

我无法找到以下问题的解决方案: 我有三个表(MS SQL):

机器

+-----------+-------------+
| MachineID | MachineName |
+-----------+-------------+
|         1 | Press 1     |
|         2 | Press 2     |
|         3 | Press 3     |
+-----------+-------------+
+-----------+-------------+
| PartID    | PartName    |
+-----------+-------------+
|         1 | Part 1      |
|         2 | Part 2      |
|         3 | Part 3      |
+-----------+-------------+
零件

+-----------+-------------+
| MachineID | MachineName |
+-----------+-------------+
|         1 | Press 1     |
|         2 | Press 2     |
|         3 | Press 3     |
+-----------+-------------+
+-----------+-------------+
| PartID    | PartName    |
+-----------+-------------+
|         1 | Part 1      |
|         2 | Part 2      |
|         3 | Part 3      |
+-----------+-------------+
机器零件分配

+----+-----------+--------+--+
| ID | MachineId | PartID |  |
+----+-----------+--------+--+
|  1 |         1 |      1 |  |
|  2 |         1 |      2 |  |
|  3 |         1 |      3 |  |
|  4 |         2 |      2 |  |
|  5 |         3 |      2 |  |
|  6 |         3 |      3 |  |
+----+-----------+--------+--+
这就是我想要得到的查询结果:
(如果指定了机器和零件,则在machinePartnerAssign中存在匹配行时,其值为true(或1),否则其值应为false(或0)。我还可以为每个零件/机器组合在machinePartnerAssign中插入一行,并包含额外的布尔值(位)列。我仍然需要一个类似的透视查询。(?)

所需结果(如果方便的话,可以用1和0交换true或false)

目前,我正在使用c#中的循环进行此操作:首先选择每台机器,然后选择所有零件,然后选择机器为特定机器/零件组合指定零件。如果返回>1行,则为真。这意味着对每台机器/部件进行一次查询

我相信有一种更优雅的方式。我知道MSSQL提供了PIVOT功能,但我不知道如何在我的案例中使用它


非常感谢

可能是这样的
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
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
此查询的结果:

若表部分可以添加其他项,那个么可以像这样使用动态sql查询

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
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

链接演示:

这是一个投影,在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上的工具,多么酷的工具

这里是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


您可以看到此查询的结果。

如果轴列和值列相同,也可以使用此查询:

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

请格式化您的代码。你的问题是什么?它存储在哪个数据库中?完成-很抱歉。根据什么判断正确和错误,或者按1,2,3@Rambazamba如果机器和零件已分配,即机器中有匹配行,请将其分配为正确,否则其应为错误。在1次传递中,很容易获得0和1的正确/错误。是的,但如果您在部分中添加行,您需要更改sql?在这种情况下,您可以使用动态sql查询来计算pivot表的列非常感谢!SQL Server 2008 R2(应该在最初的问题中说明这一点)不支持CONCAT或IIF,但我设法将您的答案和rigertas答案结合起来,以满足我的需要。真棒的解决方案!非常感谢你!SQL Server 2008 R2(应该在最初的问题中说明这一点)不支持IIF,因此我使用了:“CASE WHEN(mpa.ID为NULL)然后0 ELSE 1结束为machinepart”。这应该是一样的。真棒的解决方案!是的,应该是这样。那太好了!快乐编码!:)