如果上一列值不为空,则更新表中的下一列

Posted

技术标签:

【中文标题】如果上一列值不为空,则更新表中的下一列【英文标题】:Update next column in table if previous column value is not null 【发布时间】:2020-11-16 13:52:17 【问题描述】:

当一个人收到分数时,会在表中添加一个条目@uniqueScores

Pid | Date | Score

我有一个存储过程返回一个表 @people,其中包含来自 @uniqueScores 的数据(在过去 3 个月内)的分数列

Pid | S1 | S2 | S3 | S4 | S5

我有一个小型测试数据集,但是我无法让任何分数超出注册给用户的第一个分数以显示在 Score2 或更高版本中。

这是我的测试数据集

Pid | Date       | Score
 #1 | 2020/07/01 | 8
 #1 | 2020/09/15 | 8
 #2 | 2020/09/21 | 3
 #3 | 2020/10/01 | 5
 #4 | 2020/10/18 | 6
 #4 | 2020/10/31 | 2

我的更新语句,用数据更新 Person 列

BEGIN
  UPDATE @people
  SET    [Score5] = (CASE WHEN ( [p].[Score4] is not null and [p].[Score5] is null ) THEN [us].[Score] ELSE NULL END)
         ,[Score4] = (CASE WHEN ( [p].[Score3] is not null and [p].[Score4] is null ) THEN [us].[Score] ELSE NULL END)
         ,[Score3] = (CASE WHEN ( [p].[Score2] is not null and [p].[Score3] is null ) THEN [us].[Score] ELSE NULL END)
         ,[Score2] = (CASE WHEN ( [p].[Score1] is not null and [p].[Score2] is null ) THEN [us].[Score] ELSE NULL END)
         ,[Score1] = (CASE WHEN ( [p].[Score1] is null ) THEN [us].[Score] ELSE NULL END)
  FROM   @people [p] inner join  @uniqueScores [us]
         on [p].[PersonID] = [us].[PersonID]
  WHERE  [Date] >= @DateLimit -- within the previous 3 months
END

但是,除了第一个符合条件的值之外,查询不会更新表。返回的表是这样的

Pid | S1 | S2   | S3   | S4   | S5
#1  | 8  | null | null | null | null
#2  | 3  | null | null | null | null
#3  | 5  | null | null | null | null
#4  | 6  | null | null | null | null

不包括第一个不符合条件的表格条目,这很好,但也缺少第 4 个人的第二个分数。

我一直在查看PIVOTWHILECURSOR,但我离完成这项工作还差得远。我确定我错过了一些简单的东西,但是我看不到它。

【问题讨论】:

事实上,这似乎是一个设计缺陷。当一个人获得第 6 分时会发生什么?还是第7?似乎每个分数应该是一行,而不是一列。 5 是 5 个月窗口内可能出现的绝对最大分数。 直到下周要求更改。 ;) 以这种方式返回分数的表是因为显示数据的前端数据表将按照产品所有者的要求将数据以这种方式布局。这些表格的排列方式使得分数(人物、cmets 和媒体)保存在单独的表格中,这意味着可以将每种类型的多个添加到父“报告”中。必须为数据表返回多人分数引发了这个问题。我不是 SQL 方面的专家,但它看起来不像是设计缺陷。表结构只是动态的。 展示需求不应该定义设计。像你这样存储分数打破了范式的基本原则之一;因此,为什么它被视为设计缺陷。它不能很好地扩展,并且一旦需求改变就不会工作(即使你认为它们不好,也不意味着它们不会,因为许多不幸的灵魂已经学会了艰难的方式)。 【参考方案1】:

UPDATE 更新每一行一次。预聚合多个更新:

UPDATE p
    SET Score1 = us.score_1,
        Score2 = us.score_2,
        Score3 = us.score_3,
        Score4 = us.score_4,
        Score5 = us.score_5
    FROM @people [p] inner join
         (SELECT us.PersonID,
                 MAX(CASE WHEN seqnum = 1 THEN Score END) as score_1,
                 MAX(CASE WHEN seqnum = 2 THEN Score END) as score_2,
                 MAX(CASE WHEN seqnum = 3 THEN Score END) as score_3,
                 MAX(CASE WHEN seqnum = 4 THEN Score END) as score_4,
                 MAX(CASE WHEN seqnum = 5 THEN Score END) as score_5
          FROM (SELECT us.*,
                       ROW_NUMBER() OVER (PARTITION BY PersonId ORDER BY Date) as seqnum
                FROM @uniqueScores us
                WHERE [Date] >= @DateLimit -- within the previous 3 months
               ) us
          GROUP BY us.PersonID
         ) s
         ON us.PersonID = p.PersonId;

注意:您无需指定分数的顺序。这会将最旧的分数放在首位。如果您想先使用较新的,请使用 ORDER BY DESC

【讨论】:

此代码返回错误:Msg 8120, Level 16, State 1, Line 51 列 '@uniqueScores.Date' 在选择列表中无效,因为它不包含在聚合函数或GROUP BY 子句不确定是否要添加 group by 子句,因为我不是在寻找字段的总和。 @Vurbetan 。 . .糟糕,GROUP BY 在错误的子查询中。 这只是将错误移动到第 60 行和“us.seqnum”。还会在 Max Case 子句中的每个 'Score' colmin 上导致 Invalid Column Name 错误。你也有一个错字“)s”应该是“)我们” 感谢 Gordon 的更新!这已经解决了这个问题。还要注意的是,在先前的查询中,数据首先被预排序为最旧的。 @Vurbetan 。 . .没有“排序表”之类的东西。 SQL 表代表 无序 集。您可以拥有一个按特定顺序排列的标识列。或者你可以有一个聚集索引,它物理上按顺序存储数据——但这些都不能保证数据是按顺序读取的。

以上是关于如果上一列值不为空,则更新表中的下一列的主要内容,如果未能解决你的问题,请参考以下文章

判断:ORACLE中,用==NULL来判断列值是不是为空,

MERGE - 有条件的“当匹配然后更新”

如果列值不为 NULL,则 Python pandas 应用函数

选择性过滤列值不为空的行 PostgreSQL

如果值不为空,如何仅在对象中添加字段? Javascript

mysql如何根据一列值更新另一列的值?