Sql 比较每月数据,同时保留每日粒度

Sql 比较每月数据,同时保留每日粒度,sql,hive,hql,hiveql,window-functions,Sql,Hive,Hql,Hiveql,Window Functions,我有以下数据,其中包含一组ID的每月目标。目标是2020年每个月的每个id。名为的表以为目标。month列表示一年中的月份 +-------+-------+----+--------+ | month | name | id | target | +-------+-------+----+--------+ | 1 | Comp1 | 1 | 6000 | +-------+-------+----+--------+ | 2 | Comp1 | 1 | 6000

我有以下数据,其中包含一组ID的每月目标。目标是2020年每个月的每个id。名为
的表以
为目标。
month
列表示一年中的月份

+-------+-------+----+--------+
| month | name  | id | target |
+-------+-------+----+--------+
| 1     | Comp1 | 1  | 6000   |
+-------+-------+----+--------+
| 2     | Comp1 | 1  | 6000   |
+-------+-------+----+--------+
| 3     | Comp1 | 1  | 6000   |
+-------+-------+----+--------+
| 1     | Comp2 | 2  | 6000   |
+-------+-------+----+--------+
| 2     | Comp2 | 2  | 6000   |
+-------+-------+----+--------+
| 3     | Comp2 | 2  | 6000   |
+-------+-------+----+--------+
| 1     | Comp3 | 3  | 6000   |
+-------+-------+----+--------+
| 2     | Comp3 | 3  | 6000   |
+-------+-------+----+--------+
| 3     | Comp3 | 3  | 6000   |
+-------+-------+----+--------+
| 1     | Comp4 | 4  | 6000   |
+-------+-------+----+--------+
| 2     | Comp4 | 4  | 6000   |
+-------+-------+----+--------+
| 3     | Comp4 | 4  | 6000   |
+-------+-------+----+--------+
然后,我有第二个表,其中包含一组ID的每日数据,并每天更新。在我的实际数据集中,我得到了从2019-01-01到今天的数据

+------------+-------+----+--------+--------+
| yyyy_mm_dd | name  | id | actual | region |
+------------+-------+----+--------+--------+
| 2019-01-01 | Comp1 | 1  | 1000   | LATAM  |
+------------+-------+----+--------+--------+
| 2019-01-01 | Comp1 | 1  |   0    |  EU    |
+-------------------------------------------+
| 2019-01-02 | Comp1 | 1  | 2000   |  EU    |
+------------+-------+----+--------+--------+
| 2019-01-03 | Comp1 | 1  | 4000   |  EU    |
+------------+-------+----+--------+--------+
| 2019-01-01 | Comp2 | 2  | 1000   |  EU    |
+------------+-------+----+--------+--------+
| 2019-01-02 | Comp2 | 2  | 2000   |  EU    |
+------------+-------+----+--------+--------+
| 2019-01-03 | Comp2 | 2  | 3000   |  EU    |
+------------+-------+----+--------+--------+
| 2019-01-01 | Comp3 | 3  | 1000   |  EU    |
+------------+-------+----+--------+--------+
| 2019-01-02 | Comp3 | 3  | 2000   |  EU    |
+------------+-------+----+--------+--------+
| 2019-01-03 | Comp3 | 3  | 8000   |  EU    |
+------------+-------+----+--------+--------+
| 2019-01-01 | Comp4 | 4  | 1000   |  EU    |
+------------+-------+----+--------+--------+
| 2019-01-02 | Comp4 | 4  | 2000   |  EU    |
+------------+-------+----+--------+--------+
| 2019-02-03 | Comp4 | 4  | 3000   |  EU    |
+------------+-------+----+--------+--------+
基于以上两个表,我想创建第三个带有一些附加逻辑的表。最后,我想引入一个名为
payment
的新专栏。除非公司已通过月度目标,否则此列应始终为0。如果达到/通过月度目标,则支出应为该月的实际金额-该月的月度目标*1%

以下是输出数据的外观:

+------------+-------+----+--------+--------+
| yyyy_mm_dd | name  | id | actual | payout |
+------------+-------+----+--------+--------+
| 2020-01-01 | Comp1 | 1  | 1000   | 0      |
+------------+-------+----+--------+--------+
| 2020-01-02 | Comp1 | 1  | 2000   | 0      |
+------------+-------+----+--------+--------+
| 2020-01-03 | Comp1 | 1  | 4000   | 10     |
+------------+-------+----+--------+--------+
| 2020-01-01 | Comp2 | 2  | 1000   | 0      |
+------------+-------+----+--------+--------+
| 2020-01-02 | Comp2 | 2  | 2000   | 0      |
+------------+-------+----+--------+--------+
| 2020-01-03 | Comp2 | 2  | 3000   | 0      |
+------------+-------+----+--------+--------+
| 2020-01-01 | Comp3 | 3  | 1000   | 0      |
+------------+-------+----+--------+--------+
| 2020-01-02 | Comp3 | 3  | 2000   | 0      |
+------------+-------+----+--------+--------+
| 2020-01-03 | Comp3 | 3  | 8000   | 50     |
+------------+-------+----+--------+--------+
| 2020-01-01 | Comp4 | 4  | 1000   | 0      |
+------------+-------+----+--------+--------+
| 2020-01-02 | Comp4 | 4  | 2000   | 0      |
+------------+-------+----+--------+--------+
| 2020-02-03 | Comp4 | 4  | 3000   | 0      |
+------------+-------+----+--------+--------+
上述数据集中的所有名称/ID的每月
目标值为6000。因此,只有当一个姓名/身份证在当月超过该目标时,才应该有
支付。Comp1和Comp3都在1月3日通过了月度目标,因此他们从该日起直到月底都会收到付款。这将在2月份重置,因为这是一个新的月份,有了新的目标,我们将随着月份的进展获得新的每日数据


我所尝试的:

SELECT
    agg.yyyy_mm_dd,
    agg.name,
    agg.id,
    CASE WHEN agg.actual >= targets.target THEN ((agg.actual-targets.target)/100) * 1 ELSE 0 END AS payout
FROM(
    SELECT
        sum(x.actual) AS actual,
        x.yyyy_mm_dd,
        x.name,
        x.id
    FROM(
        SELECT
            yyyy_mm_dd,
            name,
            id,
            cast(actual as int) as actual
        FROM
            schema.daily_data
        WHERE
            yyyy_mm_dd >= '2020-01-01' AND (name = 'Comp1' OR name = 'Comp2')
    ) x
    GROUP BY
        2,3,4
) agg
INNER JOIN(
    SELECT
      id,
      month,
      target
    FROM
        schema.targets
) targets ON targets.id = agg.id
GROUP BY
    1,2,3,4

但是,上面的命令会为每个
名称
输出多行。这是由于daily表每天多次使用同一公司(预期)。我想我的小组会处理好的。此外,我不认为这是最简单的解决方案,我可能过度认为它/可以更有效地完成。

看起来您希望将每个公司和每个月的
实际值
的累计总和与
目标
进行比较。您可以使用连接和窗口函数来完成此操作:

select 
    d.yyyy_mm_dd, 
    case when sum(d.actual) over(partition by d.name, t.month order by d.yyyy_mm_dd) > t.target
        then (sum(d.actual) over(partition by d.name, t.month order by d.yyyy_mm_dd) - t.target) / 100.0
        else 0
    end payout
from schema.targets t
inner join schema.daily_data d
    on  month(d.yyyy_mm_dd) = t.month
    and d.name = t.name
where
    d.yyyy_mm_dd >= '2020-01-01' 
    and d.name in ('Comp1', 'Comp2')
运行(部分)实际值和的请求可以通过窗口函数轻松解决。不幸的是,我不使用Hive,所以这里是我的Postgres工作解决方案

with t (month, name, id, target) as (values
  (1 , 'Comp1', 1 , 6000 ),
  (2 , 'Comp1', 1 , 6000 ),
  (3 , 'Comp1', 1 , 6000 ),
  (1 , 'Comp2', 2 , 6000 ),
  (2 , 'Comp2', 2 , 6000 ),
  (3 , 'Comp2', 2 , 6000 ),
  (1 , 'Comp3', 3 , 6000 ),
  (2 , 'Comp3', 3 , 6000 ),
  (3 , 'Comp3', 3 , 6000 ),
  (1 , 'Comp4', 4 , 6000 ),
  (2 , 'Comp4', 4 , 6000 ),
  (3 , 'Comp4', 4 , 6000 )
), d (yyyy_mm_dd, name, id, actual, region) as (values
 ( date '2019-01-01' , 'Comp1' , 1  , 1000 , 'LATAM' ),
 ( date '2019-01-01' , 'Comp1' , 1  ,    0 , 'EU' ),
 ( date '2019-01-02' , 'Comp1' , 1  , 2000 , 'EU' ),
 ( date '2019-01-03' , 'Comp1' , 1  , 4000 , 'EU' ),
 ( date '2019-01-01' , 'Comp2' , 2  , 1000 , 'EU' ),
 ( date '2019-01-02' , 'Comp2' , 2  , 2000 , 'EU' ),
 ( date '2019-01-03' , 'Comp2' , 2  , 3000 , 'EU' ),
 ( date '2019-01-01' , 'Comp3' , 3  , 1000 , 'EU' ),
 ( date '2019-01-02' , 'Comp3' , 3  , 2000 , 'EU' ),
 ( date '2019-01-03' , 'Comp3' , 3  , 8000 , 'EU' ),
 ( date '2019-01-01' , 'Comp4' , 4  , 1000 , 'EU' ),
 ( date '2019-01-02' , 'Comp4' , 4  , 2000 , 'EU' ),
 ( date '2019-02-03' , 'Comp4' , 4  , 3000 , 'EU' )
)
select dr.yyyy_mm_dd, dr.name, dr.id, dr.actual,
       case when dr.running_sum < t.target then 0 else (dr.running_sum - t.target) / 100 end as payment
from t
join (
  select dg.*, sum(actual) over (partition by name order by yyyy_mm_dd) as running_sum
  from (
     select yyyy_mm_dd, name, id, sum(actual) as actual
     from d
     group by yyyy_mm_dd, name, id
  ) dg
) dr on dr.name = t.name
     and month(dr.yyyy_mm_dd) = t.month -- edited to hive equivalent of postgres' extract(month from dr.yyyy_mm_dd) = t.month
以t(月份、名称、id、目标)作为(值
(1,'Comp1',16000),
(2,'Comp1',16000),
(3,'Comp1',16000),
(1,'Comp2',2600),
(2,'Comp2',2600),
(3,'Comp2',2600),
(1,'Comp3',36000),
(2,'Comp3',36000),
(3,'Comp3',36000),
(1,'Comp4',46000),
(2,'Comp4',46000),
(3,'Comp4',46000)
),d(yyyy\u mm\u dd,名称,id,实际值,区域)作为(值
(日期'2019-01-01','Comp1',11000','LATAM',
(日期‘2019-01-01’、‘Comp1’、1、0、EU’,
(日期'2019-01-02','Comp1',2000','EU'),
(日期“2019-01-03”,“Comp1”,14000,“欧盟”),
(日期为“2019-01-01”,“Comp2”,21000,“欧盟”),
(日期'2019-01-02','Comp2',2000','EU'),
(日期“2019-01-03”,“Comp2”,23000,“欧盟”),
(日期“2019-01-01”,“Comp3”,31000,“欧盟”),
(日期'2019-01-02','Comp3',3,2000',EU'),
(日期'2019-01-03','Comp3',3800','EU'),
(日期“2019-01-01”,“Comp4”,41000,“欧盟”),
(日期“2019-01-02”,“Comp4”,2000年4月,“欧盟”),
(日期'2019-02-03','Comp4',43000','EU')
)
选择dr.yyyy\u mm\u dd、dr.name、dr.id、dr.actual、,
当dr.running_sum

从日期提取月份的方法可能不同,但我希望您能理解。

另一种方法是使用窗口
SUM
函数创建一个运行总计,然后在
CASE
语句中使用该函数来获取列值

SELECT d.yyyy_mm_dd
    ,d.name
    ,d.id
    ,d.actual
    ,CASE 
        WHEN 
      SUM(d.actual) 
        OVER (PARTITION BY d.id ORDER BY d.yyyy_mm_dd ROWS UNBOUNDED PRECEDING) <= t.target
            THEN 0
        ELSE 
      (
        SUM(d.actual) 
          OVER (PARTITION BY d.id ORDER BY d.yyyy_mm_dd ROWS UNBOUNDED PRECEDING) - t.target
            ) * 0.01
        END AS payout
FROM dailies AS d
JOIN targets AS t 
    ON d.month = MONTH(d.yyyy_mm_dd)
    AND d.id = d.id;
选择d.yyyy\u mm\u dd
,d.name
,d.id
,d
案例
什么时候
总额(d.实际)

OVER(按d.id顺序分区,按d.yyyy\u mm\u dd行,前面无边界)我想我现在有了一个有效的解决方案。下面给出了预期的输出。它可能会被优化一点,因为它不是最快的

SELECT
    x.yyyy_mm_dd,
    x.id,
    x.name,
    x.actual,
    x.target,
    x.actual_to_date,
    CASE WHEN x.actual_to_date > x.target THEN ((x.actual_to_date - x.target) /100) * 1 ELSE 0 END AS payout
FROM(
    SELECT
        daily.yyyy_mm_dd,
        daily.id,
        daily.name,
        daily.actual,
        t.target,
        SUM(daily.actual) OVER (PARTITION BY MONTH(daily.yyyy_mm_dd), daily.id ORDER BY daily.yyyy_mm_dd RANGE UNBOUNDED PRECEDING) AS actual_to_date
    FROM(
        SELECT
            yyyy_mm_dd,
            id,
            name,
            sum(cast(actual as int)) as actual
        FROM
            daily_data_table
        WHERE
            yyyy_mm_dd >= '2020-01-01'
        GROUP BY
            1,2,3
    ) daily
    INNER JOIN
        monthly_target_table t
        ON t.id = daily.id AND t.month = month(daily.yyyy_mm_dd)
    WHERE
        daily.name = 'Comp1'
) x

在每日数据中,
yyyy\u mm\u dd
存储为日期。monthly表没有日期,只有存储为int的
month
。@stackq:我理解。Hive的做法与其他数据库不同。我相应地修正了我的答案。有一些语法错误,我修正了它们,但这一个我不确定:
在JOIN'yyyy\u mm\u dd'中遇到的左别名和右别名。
@stackq:我也修正了一些拼写错误。您是否仍有错误?您好,更改后的代码仍然存在相同的问题<代码>在联接“1”中同时遇到左别名和右别名。
Hi。在hive中,月份提取如下:
和月份(dr.yyyy\u mm\u dd)=t.month
。然而,我每天都能得到多家公司的产出。我认为您应该在
dr
子查询中按公司分组。您能编辑解决方案吗?已编辑月比较。我以为你想要“每天多个公司”,根据你的样本输出的问题。
dr
中没有
groupby
,这是故意的,因为要计算运行总和,您需要为每个公司保留每一行。
partition by
子句将窗口拆分为多个组,但不将它们聚合。我确实希望多个公司在同一天工作,但不需要相同的
id
。例如,2020-01-01将有3家公司,但同一家公司不会出现多次。在您的解决方案中,输出在一天内为同一公司提供多次。在我的真实数据中,一些公司的
d
确实每天有多个
id
,但不是所有公司。我想将其汇总,并根据样本输出每天保存一次。希望