【问题标题】:SQL - subtract value from same columnSQL - 从同一列中减去值
【发布时间】:2014-05-05 11:22:38
【问题描述】:

我有一张如下表

fab_id  x  y  z  m  
12      14 10 3  5
12      10 10 3  4

这里我在 id 上使用 group by 子句。现在我想减去那些具有相似 id 的列值。 例如,按 id (12) 分组。现在减去 (14-10)X, (10-10)Y, (3-3)z, (5-4)m

我知道有一个聚合函数 sum 用于加法,但是有什么函数可以用来减去这个值。

或者有没有其他方法可以达到效果。

注意--ve 中的值可能会有所变化。那么有什么函数可以处理这个吗?

再举一个例子 - (按correction_date desc 排序,结果将首先显示最近的更正)

fab_id  x  y  z  m  correction_date
14      20 12 4  4   2014-05-05 09:03
14      24 12 4  3   2014-05-05 08:05
14      26 12 4  6   2014-05-05 07:12

所以结果是在 id (14) 上实现分组。现在减去 (26-20)X, (12-12)Y, (4-4)z, (6-4)m

【问题讨论】:

  • 要提供准确的答案,我们需要了解所有要求。你总是有2行吗?还是N 行?
  • 如何定义行的顺序,即从哪一行中减去哪一行?如果您有三行具有相同的fab_id,您期望会发生什么?
  • 我也有日期栏。我使用 order by Correction_date desc 以便最近对 fab 进行的更正首先出现,然后依此类推。
  • 它总是只有两行。但如果我动态地做会更好(不取决于 2 或 3 行)
  • 这越来越模糊了。请提供一个包含三行(甚至更多)的示例,其中包含日期和预期结果。

标签: sql sql-server-2008


【解决方案1】:

现在,您已经提供了有关如何处理更多记录的更多信息,并且您透露涉及时间列,这是一个可能的解决方案。查询选择每个 fab_id 的第一条和最后一条记录并减去这些值:

select 
  fab_info.fab_id,
  earliest_fab.x - latest_fab.x,
  earliest_fab.y - latest_fab.y,
  earliest_fab.z - latest_fab.z,
  earliest_fab.m - latest_fab.m
from 
(
  select 
    fab_id, 
    min(correction_date) as min_correction_date,
    max(correction_date) as max_correction_date
  from fab
  group by fab_id
) as fab_info
inner join fab as earliest_fab on 
  earliest_fab.fab_id = fab_info.fab_id and 
  earliest_fab.min_correction_date = fab_info.min_correction_date
inner join fab as latest_fab on 
  latest_fab.fab_id = fab_info.fab_id and 
  latest_fab.min_correction_date = fab_info.max_correction_date;

【讨论】:

    【解决方案2】:

    假设您总是想从最大值中减去最小值:

    select 
      fab_id, 
      max(x) - min(x), 
      max(y) - min(y), 
      max(z) - min(z), 
      max(m) - min(m)
    from fab
    group by fab_id;
    

    【讨论】:

    • 我又添加了一个例子。还有一件事,可能存在 -ve 或 +ve 值,这取决于或多或少的修正。所以我不能使用 min 或 max 函数
    • 不是他需要的,他正在寻找最近的行和最早的行,这可能不是该列的最大值/最小值。
    • @Clockwork-Muse:确实如此。这回答了原始请求,但没有解决更改后的请求。
    【解决方案3】:

    正如你所说,总会有两行,你可以简单地做一个“自连接”并相互减去值:

    SELECT t1.fab_id, t1.x - t2.x as diffx, t1.y - t2.y as diffy, <remainder columns here>
    from <table> t1
    inner join <table> t2 on t1.fab_id = t2.fab_id and t1.correctiondate > t2.correctiondate
    

    如果您有超过两行,那么您需要进行子查询或使用窗口排名函数来计算每个 fab_id 的最大和最小更正日期,然后您可以通过加入这两个子查询来执行与上述相同的操作在一起而不是

    【讨论】:

    • 假设只有两行,是的...我有点担心 OP 想要一个不需要的答案,坦率地说。跨度>
    • 如前所述 - 如果存在多于两行,只需将每个 变成一个选择最高和最低日期的子查询,并且连接仍然有效。
    【解决方案4】:

    不幸的是,SQL Server 2012 有方便的FIRST_VALUE()/LAST_VALUE() OLAP 函数,所以在超过 2 行的情况下,我们必须做一些不同的事情:

    SELECT fab_id, SUM(CASE WHEN latest = 1 THEN -x ELSE x END) AS x,
                   SUM(CASE WHEN latest = 1 THEN -y ELSE y END) AS y,
                   SUM(CASE WHEN latest = 1 THEN -z ELSE z END) AS z,
                   SUM(CASE WHEN latest = 1 THEN -m ELSE m END) AS m
    
    FROM (SELECT fab_id, x, y, z, m,
                 ROW_NUMBER() OVER(PARTITION BY fab_id 
                                   ORDER BY correction_date ASC) AS earliest,
                 ROW_NUMBER() OVER(PARTITION BY fab_id 
                                   ORDER BY correction_date DESC) AS latest
          FROM myTable) fab
    WHERE earliest = 1
          OR latest = 1
    GROUP BY fab_id
    HAVING COUNT(*) >= 2
    

    (和working fiddle。感谢@AK47 的初始设置。)

    产生预期的结果:

    FAB_ID   X   Y   Z   M
    12       4   0   0   1
    14       6   0   0   2
    

    请注意,HAVING COUNT(*) &gt;= 2 仅考虑发生更改的行(否则您会得到一些空结果列)。

    【讨论】:

      【解决方案5】:
      ;with Ordered as 
      (
      select 
          fab_id,x,y,z,m,date, 
          row_Number() over (partition by fab_id order by date desc) as Latest,
          row_Number() over (partition by fab_id order by date) as Oldest
      from fab
      )
      select 
         O1.fab_id, 
         O1.x-O2.x,
         O1.y-O2.y, 
         O1.z-O2.z, 
         O1.m-O2.m
      from Ordered O1 
      join Ordered O2 on 
      O1.fab_id = O2.fab_id
      where O1.latest = 1 and O2.oldest = 1  
      

      【讨论】:

      • 哦,很接近 - 但他想减去 最早 日期,在超过 2 行的情况下不会有 2 的索引。跨度>
      【解决方案6】:

      我认为如果你有一致的集合或两行,那么下面的代码应该适合你。

      select fab_id ,max(x) - min(x) as x
              ,max(y) - min(y) as y
                  ,max(z) - min(z) as z
                  ,max(m) - main(m) as m
                  from Mytable
      group by fab_id
      

      即使您在一个组中获得超过 2 行,它也会起作用,但减法将从最小值的最大值开始。希望对你有帮助。

      编辑:SQL 小提琴DEMO

      【讨论】:

      • OP特意表示不知道变化的方向,所以不能用MAX()/MIN()。如果超过 2 行,这也会严重失败,因为我们不知道各个值会是什么。
      【解决方案7】:

      CTE 可以提供帮助:

      WITH cte AS (
          SELECT
              -- Get the row numbers per fab_id ordered by the correction date
              ROW_NUMBER() OVER (PARTITION BY fab_id ORDER BY correction_date ASC) AS rid
              , fab_id, x, y, z, m
          FROM
              YourTable
      )
      SELECT
          fab_id
          -- If the row number is 1 then, this is our base value
          -- If the row number is not 1 then, we want to subtract it (or add the negative value)
          , SUM(CASE WHEN rid = 1 THEN x ELSE x * -1 END) AS x
          , SUM(CASE WHEN rid = 1 THEN y ELSE y * -1 END) AS y
          , SUM(CASE WHEN rid = 1 THEN z ELSE z * -1 END) AS z
          , SUM(CASE WHEN rid = 1 THEN m ELSE m * -1 END) AS m
      FROM
          cte
      GROUP BY
          fab_id
      

      记住,40-10-20 等于 40 + (-10) + (-20)

      【讨论】:

      • 他只是在寻找两行,所以这不会得到正确的结果;具体来说,他正在寻找最近的和最早的。
      猜你喜欢
      • 1970-01-01
      • 2021-02-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-30
      • 2021-04-19
      • 2021-01-28
      相关资源
      最近更新 更多