【问题标题】:How to replace leading null values with the first non-null value in a row?如何用一行中的第一个非空值替换前导空值?
【发布时间】:2010-08-11 16:34:35
【问题描述】:

我会尽量避免在这里描述背景。我现在有一个查询结果(不是表),其中包含如下行:

编号 SP1 SP2 SP3 SP4 SP5 SP6 SP7 SP8 1 null null 2500 1400 700 null null null

在一段非空值周围可能有前导和/或尾随空值(这实际上表示一个递减过程)。而我想要的是这样的:

编号 SP1 SP2 SP3 SP4 SP5 SP6 SP7 SP8 1 2500 2500 2500 1400 700 0 0 0

这意味着,用第一个非空值替换前导空值,用 0 替换尾随空值。
请指教。我正在使用 SQL Server 2000。

【问题讨论】:

    标签: sql sql-server sql-server-2000 null


    【解决方案1】:
    SELECT
        ID,
        COALESCE(SP1,SP2,SP3,SP4,SP5,SP6,SP7,SP8,0) as SP1,
        COALESCE(SP2,SP3,SP4,SP5,SP6,SP7,SP8,0) as SP2,
        COALESCE(SP3,SP4,SP5,SP6,SP7,SP8,0) as SP3,
        COALESCE(SP4,SP5,SP6,SP7,SP8,0) as SP4,
        COALESCE(SP5,SP6,SP7,SP8,0) as SP5,
        COALESCE(SP6,SP7,SP8,0) as SP6,
        COALESCE(SP7,SP8,0) as SP7,
        COALESCE(SP8,0) as SP8
    FROM
        (<your existing query>) t
    

    COALESCE 接受多个表达式,并返回第一个非空值。

    【讨论】:

    • 实际上这比我的多视图选项要好一些,但请仍然检查性能。每行函数永远无法很好地扩展。
    • 我刚刚对 100000 行样本数据进行了分析(远高于我对报告的预期),与从表中进行的普通选择查询相比,它似乎增加了 2% 的开销。显然,如果基本查询(我们已经知道存在)有更多复杂性,我们希望这种开销会减少。 YMMV。
    • 那我就选这个。 2% 似乎并不算太​​糟糕,而且您是对的 - 如果底层查询比简单的行提取更复杂,那么该比率应该会降低更多。
    【解决方案2】:

    你应该可能重新做你的架构。每当您在一行中有一个看起来像数组的东西时,通常最好将其拆分为单独的行。

    但是,假设您坚持使用当前架构,我会选择一个简单的方法:

    start transaction;
    update TBL set SP8 =   0 where SP8 is null;
    update TBL set SP7 = SP8 where SP7 is null;
    update TBL set SP6 = SP7 where SP6 is null;
    update TBL set SP5 = SP6 where SP5 is null;
    update TBL set SP4 = SP5 where SP4 is null;
    update TBL set SP3 = SP4 where SP3 is null;
    update TBL set SP2 = SP3 where SP2 is null;
    update TBL set SP1 = SP2 where SP1 is null;
    commit;
    

    (如果需要,替换为正确的 SQL Server 事务语法)。

    如果您不想真正更改底层数据,您可以使用视图,但它可能很可怕,您可能希望选择在任何应用程序中进行转换'用于执行 SQL。

    一种可能性,但我强烈敦促您不要这样做,并且可能有更好的供应商特定方式:

    • 在表上创建一个视图view8,除SP8 变为coalesce(sp8,0)(或SQL Server 等效值-SP8,如果它不为NULL,否则为0)之外,所有列都保持不变。
    • 在视图view8 之上创建一个视图view7,除了SP7 变为coalesce(sp7,sp8) 之外,所有列都保持不变。
    • 在视图view7 之上创建一个视图view6,除了SP6 变为coalesce(sp6,sp7) 之外,所有列都保持不变。
    • 废话,废话,废话。
    • 在视图view2 之上创建一个视图view1,除了SP1 变为coalesce(sp1,sp2) 之外,所有列都保持不变。
    • 使用视图view1

    正如我所说,一个巨大的杂物,为了你所信仰的任何神灵的爱,请不要使用它。但有时需要决定我们的行动,所以我把它放在那里以防万一。

    全心全意,不负责任,自己测试(和描述)它。


    而且,在发布该内容并发现 Damien 有一个更紧凑的版本后,我还想提供以下内容。

    有时以牺牲空间换取时间很有用(以占用更多磁盘空间为代价使事情变得更快)。

    您可以创建另外 8 列,MORPHSP1MORPHSP8,来存储我在第一个解决方案中建议的变形值。

    这通常会违反 3NF,但如果您做两件事,这实际上是可以的:(1)了解后果; (2) 减少数据不一致的可能性。

    通过使用插入/更新触发器,您实际上可以保证数据将保持一致。

    让您的触发器在行更改时执行以下操作。

    set MORPHSP8 to coalesce (SP8,0)
    set MORPHSP7 to coalesce (SP7,MORPHSP8)
    set MORPHSP6 to coalesce (SP6,MORPHSP7)
    set MORPHSP5 to coalesce (SP5,MORPHSP6)
    set MORPHSP4 to coalesce (SP4,MORPHSP5)
    set MORPHSP3 to coalesce (SP3,MORPHSP4)
    set MORPHSP2 to coalesce (SP2,MORPHSP3)
    set MORPHSP1 to coalesce (SP1,MORPHSP2)
    

    这样,您只会在数据更改时产生成本,而不是每次使用数据。在读取次数超过写入次数(绝大多数情况下)的表上,这可以带来令人印象深刻的性能提升。

    【讨论】:

    • Erm...架构看起来不像这样。事实上,我已经努力将行动态转换为列以使其成为这样。格式由我要制作的报告决定。
    • 嗯,我认为这是出于报告目的,而不是实际的表结构。我猜数据已经被旋转,而在旋转发生之前修复数据可能更合适。至于视图/COALESCE,在这种情况下并不太可怕(请参阅我的回答),请记住 COALESCE 可以采用多个表达式(不仅仅是 2 个)。
    • 我的建议是不要使用 SQL 来生成报告。它是一种用于创建数据集的关系代数,通常用于获取数据要好得多。获得数据后生成报告。
    • @Damien 如何在数据透视发生之前修复数据?然后,每个空值都是缺失的数据行。
    • @paxdiablo 然后我必须在 Excel 中使用 VBA。我认为 SQL 更准确、更高效。
    猜你喜欢
    • 2021-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-14
    • 1970-01-01
    • 1970-01-01
    • 2018-03-19
    相关资源
    最近更新 更多