用其他值更新空值

Hab*_*man 3 sql-server t-sql sql-server-2008-r2

我是 SQL Server 世界的新手。在下面的示例中,我有两家公司:

  • 建筑模块
  • 红水泥

每个公司只有一个条目具有列的值power1, power2, power3

我需要更新表,以便每个公司的所有列都有一个值。换句话说,我需要更新所有空白(空)值。

使用虚构数据的示例:

Create Table #Rentarious
(
  c1 varchar(200)
  ,yryryryr datetime
  ,power1 varchar(200)
  ,power2 varchar(200)
  ,power3 varchar(200)
)   

Insert Into #Rentarious VALUES
('Building Blocks','2016','Red','Blue','Green')
,('Red Cement', '2012', 'Pink','Purple','Orange')

Insert Into #Rentarious(c1, yryryryr) VALUES 
('Building Blocks', '2012')
,('Building Blocks', '2013')
,('Building Blocks', '2014')
,('Red Cement', '2016')
,('Red Cement', '2011')
Run Code Online (Sandbox Code Playgroud)

编写更新语句以将列的空值更新为表power1, power2, power3中已列出的值的语法是什么?

And*_*y M 6

编写更新语句以power1, power2, power3使用表中已列出的值更新字段的空值的语法是什么?

我认为这意味着每个公司的空值都应该根据从一个填充条目中获取的值进行填充,正如您在帖子的其他地方所说,每个公司都有。因此,在伪代码中,您的 UPDATE 语句需要如下所示:

UPDATE
  #Rentarious
SET
  power1 = power1 from the same company’s populated row,
  power2 = power2 from the same company’s populated row,
  power3 = power3 from the same company’s populated row
WHERE power1 IS NULL
  AND power2 IS NULL
  AND power3 IS NULL
;
Run Code Online (Sandbox Code Playgroud)

实现上述模式的一种方法是使用相关子查询:

UPDATE
  #Rentarious
SET
  power1 = (SELECT power1 FROM #Rentarious AS src WHERE src.c1 = #Rentarious.c1 AND src.power1 IS NOT NULL),
  power2 = (SELECT power2 FROM #Rentarious AS src WHERE src.c1 = #Rentarious.c1 AND src.power2 IS NOT NULL),
  power3 = (SELECT power3 FROM #Rentarious AS src WHERE src.c1 = #Rentarious.c1 AND src.power3 IS NOT NULL)
WHERE power1 IS NULL
  AND power2 IS NULL
  AND power3 IS NULL
;
Run Code Online (Sandbox Code Playgroud)

虽然这可行,但这样的更新可能效率不高,因为同一个表会额外接触三次以获取源值。由于您知道每家公司的非空值都存储在一行中,因此您可以使用派生表和专有的“使用连接更新”语法,在一次额外的表中获取它们:

UPDATE
  #Rentarious
SET
  power1 = sub.power1,
  power2 = sub.power2,
  power3 = sub.power3
FROM
  (
    SELECT
      c1,
      power1,
      power2,
      power3
    FROM
      #Rentarious
    WHERE power1 IS NOT NULL
      AND power2 IS NOT NULL
      AND power3 IS NOT NULL
  ) AS sub
WHERE #Rentarious.c1 = sub.c1
  AND #Rentarious.power1 IS NULL
  AND #Rentarious.power2 IS NULL
  AND #Rentarious.power3 IS NULL
;
Run Code Online (Sandbox Code Playgroud)

您还可以重写它以使用显式 JOIN 语法:

UPDATE
  tgt
SET
  power1 = src.power1,
  power2 = src.power2,
  power3 = src.power3
FROM
  #Rentarious AS tgt
  INNER JOIN
  (
    SELECT
      c1,
      power1,
      power2,
      power3
    FROM
      #Rentarious
    WHERE power1 IS NOT NULL
      AND power2 IS NOT NULL
      AND power3 IS NOT NULL
  ) AS sub
  ON tgt.c1 = sub.c1
WHERE tgt.power1 IS NULL
  AND tgt.power2 IS NULL
  AND tgt.power3 IS NULL
;
Run Code Online (Sandbox Code Playgroud)

如您所见,两种变体中只有一个子查询,它提供所有三个值来填充其他行。

但请注意,也可以在不进行任何额外扫描的情况下解决此问题。首先,如果这是一个 SELECT 语句,您可以使用窗口聚合函数返回每一行中填充行的值,如下所示:

SELECT
  c1,
  yryryryr,
  power1,
  power2,
  power3,
  populatedPower1 = MAX(power1) OVER (PARTITION BY c1),
  populatedPower2 = MAX(power2) OVER (PARTITION BY c1),
  populatedPower3 = MAX(power3) OVER (PARTITION BY c1)
FROM
  #Rentarious
;
Run Code Online (Sandbox Code Playgroud)

MAX功能可在这种情况下,因为它只返回跨越指定集合中的非空值中的最大值。在您的情况下,c1在这三种情况中的每一种情况下,每个分区都只有一个非空值,因此该函数将返回该值。这将是您问题中示例的查询结果:

c1               yryryryr    power1  power2  power3  populatedPower1  populatedPower2  populatedPower3
---------------  ----------  ------  ------  ------  ---------------  ---------------  ---------------
Building Blocks  2012-01-01  NULL    NULL    NULL    Red              Blue             Green
Building Blocks  2013-01-01  NULL    NULL    NULL    Red              Blue             Green
Building Blocks  2014-01-01  NULL    NULL    NULL    Red              Blue             Green
Building Blocks  2016-01-01  Red     Blue    Green   Red              Blue             Green
Red Cement       2012-01-01  Pink    Purple  Orange  Pink             Purple           Orange
Red Cement       2016-01-01  NULL    NULL    NULL    Pink             Purple           Orange
Red Cement       2011-01-01  NULL    NULL    NULL    Pink             Purple           Orange
Run Code Online (Sandbox Code Playgroud)

所以唯一剩下的就是

SET
  power1 = populatedPower1,
  power2 = populatedPower2,
  power3 = populatedPower3
Run Code Online (Sandbox Code Playgroud)

而在 SQL Server 中确实可以这样做,因为上述 SELECT 查询的结果可以用作您的 UPDATE 语句的目标。您可以将其用作派生表:

UPDATE
  tgt
SET
  power1 = populatedPower1,
  power2 = populatedPower2,
  power3 = populatedPower3
FROM
  (
    SELECT
      c1,
      yryryryr,
      power1,
      power2,
      power3,
      populatedPower1 = MAX(power1) OVER (PARTITION BY c1),
      populatedPower2 = MAX(power2) OVER (PARTITION BY c1),
      populatedPower3 = MAX(power3) OVER (PARTITION BY c1)
    FROM
      #Rentarious
  ) AS tgt
WHERE power1 IS NULL
  AND power2 IS NULL
  AND power3 IS NULL
;
Run Code Online (Sandbox Code Playgroud)

或将其实现为 CTE(Common Table Expression)并使用 CTE 的别名作为目标:

WITH tgt AS
  (
    SELECT
      c1,
      yryryryr,
      power1,
      power2,
      power3,
      populatedPower1 = MAX(power1) OVER (PARTITION BY c1),
      populatedPower2 = MAX(power2) OVER (PARTITION BY c1),
      populatedPower3 = MAX(power3) OVER (PARTITION BY c1)
    FROM
      #Rentarious
  )
UPDATE
  tgt
SET
  power1 = populatedPower1,
  power2 = populatedPower2,
  power3 = populatedPower3
WHERE power1 IS NULL
  AND power2 IS NULL
  AND power3 IS NULL
;
Run Code Online (Sandbox Code Playgroud)

两者都可以很好地工作,并导致所有空值都替换为相应的值,即:

c1               yryryryr    power1  power2  power3
---------------  ----------  ------  ------  ------
Building Blocks  2016-01-01  Red     Blue    Green
Red Cement       2012-01-01  Pink    Purple  Orange
Building Blocks  2012-01-01  NULL    NULL    NULL
Building Blocks  2013-01-01  NULL    NULL    NULL
Building Blocks  2014-01-01  NULL    NULL    NULL
Red Cement       2016-01-01  NULL    NULL    NULL
Red Cement       2011-01-01  NULL    NULL    NULL
Run Code Online (Sandbox Code Playgroud)

对此:

c1               yryryryr    power1  power2  power3
---------------  ----------  ------  ------  ------
Building Blocks  2016-01-01  Red     Blue    Green
Red Cement       2012-01-01  Pink    Purple  Orange
Building Blocks  2012-01-01  Red     Blue    Green
Building Blocks  2013-01-01  Red     Blue    Green
Building Blocks  2014-01-01  Red     Blue    Green
Red Cement       2016-01-01  Pink    Purple  Orange
Red Cement       2011-01-01  Pink    Purple  Orange
Run Code Online (Sandbox Code Playgroud)