父级依赖于SQL Server中的子级时的嵌套情况

时间:2018-04-18 20:11:27

标签: sql sql-server case

我想检查一下嵌套的情况,其中父级依赖于子级,是否有更好的方法将查询抛出到from子句中。

请注意逻辑的细节,但这是我想要完成的蓝图和它的工作。代码看起来很草率。是否有更好/更简洁的方法来实现这一目标?

SELECT 
    *,
    CASE 
       WHEN b.NewCalcColRate IS NULL
          THEN 0 
          ELSE b.NewCalcColRate * 1000 
    END AS FinalCalcColRate
FROM
    (SELECT
         a.*,
         CASE 
            WHEN a.calcColRate IS NULL
               THEN 0 
               ELSE a.calcColRate * 100 
         END AS NewCalcColRate
     FROM
         (SELECT
              *,
              CASE 
                 WHEN f.AnnualCost IS NULL
                    THEN 0 
                    ELSE f.AnnualCost / 12 
              END AS calcColRate
          FROM 
              Rates) AS a
      ) AS b

2 个答案:

答案 0 :(得分:3)

首先,NewCalcColRatecalcColRate永远不会是NULL,因为在嵌套CASE中,当NULL CASE WHEN f.AnnualCost IS NULL THEN 0时,您将其设置为0 f 1}}),这样逻辑就没有意义了。

此外,您的此列包含Rate引用,但该子查询中的SELECT *, CASE WHEN AnnualCost IS NULL THEN 0 ELSE (AnnualCost / 12) * 1000000 END AS calcColRate FROM Rate 表中没有别名。

从我可以收集到的内容,可以简化为:

SELECT 
   *,
   (ISNULL(AnnualCost,0)/12) * 1000000 
from 
   Rate

或者,正如Jabs所指出的那样......

AnnualCost

另外注意,根据INTEGER的数据类型,您可能需要考虑除以小数,这样您就不会进行(AnnualCost / 12.0) * 1000000.0 除法并成为精度丢失的牺牲品。

select 
     (1 / 12)   * 1000000  --Returns 0
    ,(1 / 12.0) * 1000000  --Returns 83333.000000

示例

.flex-grid {
  background: #f00;
  display: grid;
  grid-template-columns: auto 512px 512px auto;
  grid-template-areas: 
    "space1 left right space2";
}
.flex-child {
  text-align:center;
}
.space1, #left {
  background: #0f0;
}
.space2, #right {
  background: #00f;
}
.space1 {
  grid-area: space1;
}
.space2 {
  grid-area: space2;
}
#left {
  grid-area: left;
}
#right {
  grid-area: right;
}

将来,如果您的代码有效并且您只是在寻找改进,我会将其发布在Code Review上,因为它更适合此类请求。

答案 1 :(得分:2)

您的问题中似乎缺少一些重要信息。如果您想要的所有想要的是FinalCalcColRate表格中每一行的Rates,那么您可以像@scsimon在他/她的回答中所做的那样执行一步:

select
    r.*,
    FinalCalcColRate = case when r.AnnualCost is null then 0 else r.AnnualCost / 12 * 100000 end
from
    dbo.Rates r;

或者在SQL Server中使用coalesce(或isnull)的类似实现;有关差异的详细信息,请参阅this question

select
    r.*,
    FinalCalcColRate = coalesce(r.AnnualCost / 12 * 100000, 0)
from
    dbo.Rates r;

然而,@ scsimon的查询与原始查询之间的一个区别是前者仅输出最终计算值,而后者也产生所有中间值。从您的问题中不清楚此查询的消费者是否需要这些值。如果他们愿意,那么你可以简单地包括它们:

select
    r.*,
    CalcColRate = coalesce(r.AnnualCost / 12, 0),
    NewCalcColRate = coalesce(r.AnnualCost / 12 * 100, 0),
    FinalCalcColRate = coalesce(r.AnnualCost / 12 * 100000, 0)
from
    dbo.Rates r;

这里有一些重复的逻辑 - 例如,如果您要更改CalcColRate的定义,您还必须手动更改NewCalcColRateFinalCalcColRate的表达式 - 但它足够小,我怀疑它值得担心。尽管如此,如果原始查询中的构造是出于避免这种重复的愿望,那么您可以重构查询以使用CTE而不是嵌套查询:

with CalcCTE as
(
    select
        r.*,
        CalcColRate = coalesce(r.AnnualCost / 12, 0)
    from
        dbo.Rates r
),
NewCalcColCTE as
(
    select
        c.*,
        NewCalcColRate = c.CalcColRate * 100
    from
        CalcCTE c
)
select
    n.*,
    FinalCalcColRate = n.NewCalcColRate * 1000
from
    NewCalcColCTE n;

这显然比我之前独立定义所有值的查询更长,更难以理解,但它确实具有以下优点:每个步骤都是在最后一步构建的,并且CTE公式往往更具可读性比同等的嵌套查询集合,因为步骤是按照它们被评估的顺序编写的,而对于嵌套查询,你必须找到最里面的点并向外工作,这可能会让人感到困惑。