2017-04-07 140 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