sql - 将一行拆分为多行——表转换

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

...但失败并显示

Cannot insert the value NULL into column 'UTC', table '1000574.dbo.line_sensor_values'; column does not allow nulls. INSERT fails.

很明显,应该以不同的方式使用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

谢谢你的帮助,彼得

最佳答案

如果您需要做的就是将一个四列的表转换为一个表,其中每行代表源表中的行号和源表中的列,那么这里是一个示例:

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

关于sql - 将一行拆分为多行——表转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11892132/

相关文章:

mysql - 检查 mysql IN 的条件是否不工作

java - JPA - 选择、连接、子查询和最大值

sql-server - 仅当第一个表包含某些值时才连接两个表(如果不连接另一个表)

sql-server - 将两个 select 语句组合成 where 子句

sql - 带拆分的 T-SQL 分组

SQL 产品矩阵

mysql - ORA-02070 : database [MySQL] does not support subqueries in this context. - 甲骨文

sql - 使用 group by 检索主键上的聚合行

php - 查询在本地主机上工作但不在实时服务器上工作

java - 使用 MSSQL Server 2008 进行 hibernate