在SQL Server

时间:2017-04-07 06:43:22

标签: c# sql sql-server join

我无法理解以下问题的解决方案: 我有三个表(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 中的匹配行 true (或 1 )时会出现这种情况,否则应该是 false (或 0 )。我还可以为每个零件/机器组合插入一行到 MachinePartAssign 并包含一个额外的布尔值(位)如果这使得它更容易。我仍然需要类似的数据透视查询。(?))

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

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

目前我正在使用c#中的循环执行此操作:首先选择每台机器,然后选择所有部件,然后为该特定机器/部件组合选择MachinePartAssign。如果我得到> 1行返回它的真实。这意味着每个机器/部件都有一个查询。

我确信这是一种更优雅的方式。我知道MSSQL提供了PIVOT功能,但我不确定如何在我的情况下使用它。

非常感谢!

4 个答案:

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

答案 1 :(得分: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,这是一个很酷的工具!

答案 2 :(得分: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您可以看到此查询的结果。

答案 3 :(得分:0)

如果pivot列和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