将一行拆分为更多行 - 表转换

时间:2012-08-09 21:22:28

标签: sql sql-server

我有一个源表应该转换为一个目标表。源表包含四列具有传感器值的列。目标表应包含四行,其中传感器值为单个,传感器编号为一列 - 源表中的每一行。换句话说,目标表的行数将增加四倍。 (我相信这被称为标准化。至少,我认为将来使用更多或更少或不同的传感器将更加实用。)

更多背景资料来解释。我已经成功尝试了一个插入触发器,可以为一行执行此操作:

CREATE TRIGGER dbo.temperatures_to_sensors
  ON dbo.Data
  AFTER INSERT
AS
BEGIN
  DECLARE @line_no TINYINT;
  SET @line_no = 2;        -- hardwired for the production line

  DECLARE @UTC DATETIME;
  DECLARE @value1 FLOAT;
  DECLARE @value2 FLOAT;
  DECLARE @value3 FLOAT;
  DECLARE @value4 FLOAT;

  SELECT
      @UTC = CAST((CAST(LEFT(inserted.UTC, 16) AS FLOAT) - 2415020.5) AS DATETIME),
      @value1 = inserted.temperature_1,
      @value2 = inserted.temperature_2,
      @value3 = inserted.temperature_3,
      @value4 = inserted.temperature_4
    FROM inserted;

  INSERT INTO dbo.line_sensor_values
         (UTC, line_no, sensor_no, sensor_value)
  VALUES (@UTC, @line_no, 1, @value1),
         (@UTC, @line_no, 2, @value2),
         (@UTC, @line_no, 3, @value3),
         (@UTC, @line_no, 4, @value4);
END;
GO

现在,我想从旧表中初始化目标表一次。之后,触发器将继续填充值。

我不擅长SQL。我试过了:

CREATE PROCEDURE dbo.init_line_sensor_values
AS
BEGIN
  DECLARE @line_no TINYINT;
  SET @line_no = 2;        -- hardwired for the production line

  DECLARE @UTC DATETIME;
  DECLARE @value1 FLOAT;
  DECLARE @value2 FLOAT;
  DECLARE @value3 FLOAT;
  DECLARE @value4 FLOAT;

  INSERT INTO dbo.line_sensor_values
         (UTC, line_no, sensor_no, sensor_value)
  VALUES (@UTC, @line_no, 1, @value1),
         (@UTC, @line_no, 2, @value2),
         (@UTC, @line_no, 3, @value3),
         (@UTC, @line_no, 4, @value4)
  SELECT
      @UTC = CAST((CAST(LEFT(t.UTC, 16) AS FLOAT) - 2415020.5) AS DATETIME),
      @value1 = t.temperature_1,
      @value2 = t.temperature_2,
      @value3 = t.temperature_3,
      @value4 = t.temperature_4
    FROM dbo.Data AS t;


END;
GO

EXECUTE dbo.init_line_sensor_values
GO

...但是

失败了
  

无法将值NULL插入“UTC”列,表'1000574.dbo.line_sensor_values';列不允许空值。 INSERT失败。

很明显SELECT应该以不同的方式用于提供INSERT。或者我必须使用循环? (已创建光标且FETCH NEXT...WHILE...

已更新

源表可以这种方式创建(简化):

CREATE TABLE dbo.Data(
    UTC varchar(32) NOT NULL,
    temperature_1 float NULL,
    temperature_2 float NULL,
    temperature_3 float NULL,
    temperature_4 float NULL

PRIMARY KEY CLUSTERED 
(
    UTC ASC
)
GO

目标表是以这种方式创建的:

CREATE TABLE dbo.line_sensor_values (
    UTC DATETIME NOT NULL,
    line_no TINYINT NOT NULL,   -- line number: 1, 2, 3, etc.
    sensor_no TINYINT NOT NULL, -- sensor number: 1, 2, 3, etc.
    sensor_value float NULL,    -- the measured value

  PRIMARY KEY CLUSTERED (
    UTC ASC,
    line_no ASC,
    sensor_no ASC
  )
)
GO

感谢您的帮助,Petr

2 个答案:

答案 0 :(得分:3)

如果您只需将具有四列的表转换为单个表,其中每行表示源表中的行号和源表中的列,那么这是一个示例:

这是SQLFiddle

create table fourColumns
(
    column1 varchar(50),
    column2 varchar(50),
    column3 varchar(50),
    column4 varchar(50)
)

insert into fourColumns select 'A','B','C','D'
insert into fourColumns select 'E','F','G','H'

    ;with MyCTE (lineNumber, columnNumber, Result)
as
(
    select ROW_NUMBER() OVER(ORDER BY column1 ASC) AS Row, 1, column1  
    from fourColumns
    union all
    select ROW_NUMBER() OVER(ORDER BY column2 ASC) AS Row, 2, column2  
    from fourColumns
    union all
    select ROW_NUMBER() OVER(ORDER BY column3 ASC) AS Row, 3, column3  
    from fourColumns
    union all
    select ROW_NUMBER() OVER(ORDER BY column4 ASC) AS Row, 4, column4  
    from fourColumns        
)
    -- add insert here
select lineNumber, 
       columnNumber,
       Result
 from MyCTE
 order by lineNumber

答案 1 :(得分:1)

INSERT INTO dbo.line_sensor_value
(UTC, line_no, sensor_no, sensor_value)
select UTC, line_no, sensor_no, temperature_1 as sensor_value from dbo.Data
union
select UTC, line_no, sensor_no, temperature_2 as sensor_value from dbo.Data
union
select UTC, line_no, sensor_no, temperature_3 as sensor_value from dbo.Data
union
select UTC, line_no, sensor_no, temperature_4 as sensor_value from dbo.Data