SQL Server 如何透视没有聚合但列数可变的数据?

lb3vh1jj  于 2023-01-08  发布在  其他
关注(0)|答案(2)|浏览(130)

我有下表:
| 计算机ID|期间ID|错误ID|
| - ------|- ------|- ------|
| AGR0.37576 |十三|错误561|
| AGR0.37576 |十四|错误561|
| AGR0.37576 |十四|错误458|
| AGR0.37576 |十五|错误561|
| AGR0.37576 |十五|错误458|
| AGR0.37576 |十六|错误458|
| AGR0.37576 |十六|错误561|
| AGR0.37576 |十七|错误561|
| AGR0.37576 |十七|错误458|
| AGR0.37576 |十八|错误458|
| AGR0.37576 |十八|错误561|
| AGR0.37576 |十九|错误561|
| AGR0.37576 |二十个|错误561|

  • machineId”是标识我们手头机器的唯一标识符[AGR(农业)0(拖拉机).37576(机器本身的SN)]
  • periodId”是以下期间的唯一标识符:例如:13(2021年12月31日)、14(2022年1月1日)等。
  • errorId”是机器在此期间出现的错误的唯一标识符。例如,ERR 561-过热,ERR 457-冷却液液位非常低,ERR 458-冷却液液位非常低。每个期间可以有一个或多个错误。如果有多个错误,则插入具有相同期间的新记录和新错误代码。

我想透视并显示该表,如下所示:
| 计算机ID|期间ID|错误1|错误2|错误3|错误4|
| - ------|- ------|- ------|- ------|- ------|- ------|
| AGR7.00012 |九|错误221| MIS 061标准|ERG 737细胞|SER003|
| AGR0.37576 |十三|错误561|零|零|零|
| ROAD.88887 |十三|错误561|零|零|零|
| AGR0.37576 |十四|错误845|错误561|错误737|零|
我有两个问题:

**1.**到目前为止,我所能找到的全部内容都或多或少地解释了如何将销售数据透视到某个期间(季度、年度等)。正如您所看到的,我可以聚合的唯一列是 periodId,这没有意义。
**2.**每个周期的错误数量是不同的,我可以在一个给定的周期内每台机器没有错误,而在另一个周期内有100个错误。有什么想法如何使用SQL实现这一点?

样本数据:

DROP TABLE IF EXISTS #sampleData
CREATE TABLE #sampleData ( [machineId] varchar(18), [periodId] int, [errorId] varchar(13) )

INSERT INTO #sampleData ([machineId], [periodId], [errorId])
VALUES
( 'AGR0.37576', 13, 'ERR561' ),
( 'AGR0.37576', 14, 'ERR561' ),
( 'AGR0.37576', 14, 'ERR458' ),
( 'AGR0.37576', 14, 'ERR737' ),
( 'AGR0.37576', 15, 'ERR561' ),
( 'AGR0.37576', 15, 'ERR458' ),
( 'AGR0.37576', 16, 'ERR458' ),
( 'AGR0.37576', 16, 'ERR561' ),
( 'AGR0.37576', 17, 'ERR561' ),
( 'AGR0.37576', 17, 'ERR458' ),
( 'AGR0.37576', 18, 'ERR458' ),
( 'AGR0.37576', 18, 'ERR561' ),
( 'AGR0.37576', 19, 'ERR561' ),
( 'AGR0.37576', 20, 'ERR561' ),
( 'AGR0.37576', 21, 'ERR561' ),
( 'AGR0.37576', 22, 'ERR561' ),
( 'AGR0.37576', 29, 'ERR561' ),
( 'AGR0.37576', 30, 'ERR561' ),
( 'AGR0.37576', 96, 'ERR561' ),
( 'AGR0.37576', 97, 'ERR561' ),
( 'AGR0.37576', 111, 'ERR561' ),
( 'AGR0.37576', 112, 'ERR561' ),
( 'AGR0.37576', 113, 'ERR561' ),
( 'AGR7.00012', 9, 'ERR221' ),
( 'AGR7.00012', 9, 'MIS061' ),
( 'AGR7.00012', 9, 'ERG737' ),
( 'AGR7.00012', 9, 'SER003' ),
( 'ROAD.88887', 13, 'ERR561' )
oxiaedzo

oxiaedzo1#

我向SampleData表中添加了两列:

  1. Id:我不知道你表中的顺序
  2. RowNum:了解必须向Temp表添加多少列。
    一般来说,您可以使用这个脚本并对其进行改进。
DROP TABLE IF EXISTS #sampleData
CREATE TABLE #sampleData ( [machineId] varchar(18), [periodId] int, [errorId] varchar(13), Id INT IDENTITY, RowNum INT)

INSERT INTO #sampleData ([machineId], [periodId], [errorId])
VALUES
( 'AGR0.37576', 13, 'ERR561' ),
( 'AGR0.37576', 14, 'ERR561' ),
( 'AGR0.37576', 14, 'ERR458' ),
( 'AGR0.37576', 14, 'ERR737' ),
( 'AGR0.37576', 15, 'ERR561' ),
( 'AGR0.37576', 15, 'ERR458' ),
( 'AGR0.37576', 16, 'ERR458' ),
( 'AGR0.37576', 16, 'ERR561' ),
( 'AGR0.37576', 17, 'ERR561' ),
( 'AGR0.37576', 17, 'ERR458' ),
( 'AGR0.37576', 18, 'ERR458' ),
( 'AGR0.37576', 18, 'ERR561' ),
( 'AGR0.37576', 19, 'ERR561' ),
( 'AGR0.37576', 20, 'ERR561' ),
( 'AGR0.37576', 21, 'ERR561' ),
( 'AGR0.37576', 22, 'ERR561' ),
( 'AGR0.37576', 29, 'ERR561' ),
( 'AGR0.37576', 30, 'ERR561' ),
( 'AGR0.37576', 96, 'ERR561' ),
( 'AGR0.37576', 97, 'ERR561' ),
( 'AGR0.37576', 111, 'ERR561' ),
( 'AGR0.37576', 112, 'ERR561' ),
( 'AGR0.37576', 113, 'ERR561' ),
( 'AGR7.00012', 9, 'ERR221' ),
( 'AGR7.00012', 9, 'MIS061' ),
( 'AGR7.00012', 9, 'ERG737' ),
( 'AGR7.00012', 9, 'SER003' ),
( 'ROAD.88887', 13, 'ERR561' )

 ;WITH T1 AS (
                SELECT [machineId], [periodId], [errorId], RowNum,
                                ROW_NUMBER() OVER (PARTITION BY [machineId], [periodId] ORDER BY Id) AS RowNumber
                FROM #sampleData
                                    )
  UPDATE T1 SET RowNum = RowNumber

 CREATE TABLE #Temp (machineId VARCHAR(18), periodId INT)

 
 INSERT INTO #Temp (machineId, periodId)
 SELECT machineId, periodId
 FROM #sampleData
 GROUP BY machineId, periodId


 DECLARE @UpdateStatment AS NVARCHAR(MAX) = ''
 DECLARE @SQLString AS NVARCHAR(MAX) = ''

 SELECT @SQLString = @SQLString + ' ALTER TABLE #Temp ADD ERROR' + CAST(S1.RowNum AS NVARCHAR) + '  VARCHAR(13);', 
        @UpdateStatment = @UpdateStatment + 
        'UPDATE T SET ERROR' + CAST(S1.RowNum AS NVARCHAR) + '=  S.ErrorId 
        FROM #Temp T 
                INNER JOIN #SampleData S ON T.machineId = S.machineId AND T.periodId = S.periodId WHERE S.RowNum = ' + CAST(S1.RowNum AS NVARCHAR) + '; '
 FROM #SampleData S1 INNER JOIN 
           (SELECT [machineId], [periodId] 
            FROM #SampleData 
            GROUP BY [machineId], [periodId] 
            HAVING MAX(RowNum) >= ALL (SELECT RowNum FROM #SampleData) ) AS S2 ON S1.[machineId] = S2.[machineId] AND S1.[periodId]  = S2.[periodId] 
 ORDER BY S1.RowNum

 SELECT @SQLString
 SELECT @UpdateStatment

 EXEC SP_executesql @SQLString
 EXEC SP_executesql @UpdateStatment
 
 SELECT * FROM #Temp

--DROP TABLE #Temp
--DROP TABLE #SampleData
cqoc49vn

cqoc49vn2#

有几种方法可以做到这一点。如果你有有限数量的错误,那么你可以做一些简单的事情,像这样

-- static column version
SELECT * FROM 
    (
    SELECT 
            *
            , ColNum = CONCAT('Col', ROW_NUMBER() OVER(PARTITION BY MachineID, PeriodID ORDER BY ErrorID) )
        FROM #sampleData 
    ) s
    PIVOT 
        (
            MIN(ErrorID) 
            FOR [ColNum] IN (Col1, Col2, Col3, Col4, Col5, Col6, Col7, Col8, Col9, Col10)
        ) pvt

我在开头添加了'Col'字符串,以使透视语句更具可读性(否则列名将以数字开头)。
这个statis版本将始终生成相同数量的列,即使它们都是空的。
如果您希望它是完全动态的,那么您必须首先生成一个列名列表,最后的语句几乎与上面的语句相同。

-- dynamic version
-- Get a list of column names required
DECLARE @ColNums varchar(1000) = ''
SELECT  @ColNums = CONCAT(@ColNums, ColNum , ', ')
     FROM (SELECT DISTINCT ColNum FROM (
                SELECT 
                        *
                        , ColNum = CONCAT('Col', ROW_NUMBER() OVER(PARTITION BY MachineID, PeriodID ORDER BY ErrorID) ) --column name per machine/period
                    FROM #sampleData ) c
            ) x

SET @ColNums = LEFT(@ColNums, LEN(@ColNums)-1) -- trim the final comma

-- Generate the final sql
DECLARE @sql varchar(max) = 
'SELECT * FROM 
    (SELECT *, ColNum = CONCAT(''Col'', ROW_NUMBER() OVER(PARTITION BY MachineID, PeriodID ORDER BY ErrorID) )
        FROM #sampleData ) s
    PIVOT 
        (MIN(ErrorID) FOR [ColNum] IN (' + @ColNums + ')) pvt 
    ORDER BY MachineID, PeriodID'

EXEC (@sql)

**注意:**动态版本还格式化了列数,因此数字部分的长度总是3个数字,这假设您不会有超过999个错误列!!如果您不需要这么多填充,那么您可以在format函数中将D3减少到D2

这将得到以下结果

相关问题