HiveSQL un pequeño truco al día: ¿cómo encontrar con precisión los indicadores de los últimos 30 días?

1 necesidades

Ahora la tabla de prueba tiene tres campos usuarios: id_usuario fecha: precio de la cantidad del pedido dt,

Calcule la fecha en que la cantidad de consumo acumulada de un consumidor alcanza 1W en un período de casi 30 días "por primera vez" en la historia

2 Análisis

  

(1) Preparación de datos

 create table test as 
 select 'a' as user_id,7000 as price,'2022-07-01' as dt
    union all 
   select 'a' as user_id,4000 as price,'2022-08-22' as dt
   union all 
   select 'a' as user_id,8000 as price,'2022-08-23' as dt

(2) análisis

Campos objetivo: Consumidor, Fecha

Condición: la fecha en que la cantidad de consumo acumulado alcanza 1W en los últimos 30 días por primera vez

Paso 1: Cómo calcular la cantidad de consumo acumulado en los últimos 30 días

En general, podemos pensar fácilmente en las siguientes soluciones a tales problemas

sum(price) over(partition by user_id order by dt rows between prceding 30 and current row)

Pero hay un problema con el método de solución. Cuando usamos filas, calculamos el número real de filas físicas, pero el tiempo del usuario en los datos reales no es continuo, es decir, hay intervalos de tiempo o fenómenos que faltan. En este momento , el resultado real calculado por filas será demasiado grande, lo que obviamente es incorrecto. Para el motor de cálculo de Hive, se proporciona el método de cálculo de rango, que representa el valor de cálculo lógico de la fila ordenada, y todos los datos dentro de este rango, es decir, [dt -30, dt], solo reflejan el significado que se va a expresar. resultados de los últimos 30 días. Por lo tanto, se puede calcular de la siguiente manera

sum(price) over(partition by user_id order by cast (dt as date) range between prceding 30 and current row)

Paso 2: encuentra la primera fecha 

Primera vez: min(dt) --más temprano

Expansión: reciente, más reciente, última fecha máx. (dt)

El SQL completo es el siguiente:

select user_id,min(dt)
from (
         select dt
              , user_id
              , sum(price)
                over (partition by user_id order by cast(dt as date) range between 30 preceding and current row) as order_price
         from (select 'a' as user_id, 7000 as price, '2022-07-01' as dt
               union all
               select 'a' as user_id, 4000 as price, '2022-08-22' as dt
               union all
               select 'a' as user_id, 8000 as price, '2022-08-23' as dt
              ) t
     ) t
where order_price > 10000
group by user_id

Compare las filas para obtener el resultado:

select user_id, min(dt)
from (
         select dt
              , user_id
              , sum(price)
                over (partition by user_id order by dt rows between 30 preceding and current row) as order_price
         from (select 'a' as user_id, 7000 as price, '2022-07-01' as dt
               union all
               select 'a' as user_id, 4000 as price, '2022-08-22' as dt
               union all
               select 'a' as user_id, 8000 as price, '2022-08-23' as dt
              ) t
     ) A
where order_price > 10000
group by user_id

Obviamente el resultado obtenido por filas es incorrecto, y la fecha de 2022-07-01 no está dentro del rango de fechas de 2022-08-22 por casi 30 días

Los resultados intermedios son los siguientes:

Para algunas bases de datos que no tienen una función de rango, ¿cómo encontrarla en este momento? Podemos usar la tabla de dimensiones de tiempo para completar los datos de fecha, que también es un método general común. Por ejemplo, tenemos una tabla de dimensiones dim_date con una fecha completa

Se puede ver que las fechas son continuas. Dado que la partición debe agruparse según el usuario (user_id), la dimensión del usuario debe completarse en la tabla de dimensiones de tiempo. Generalmente usamos SQL autoasociado para completar esta dimensión. operación de la siguiente manera:

with data as
         (select 'a' as user_id, 7000 as price, '2022-07-01' as dt
          union all
          select 'a' as user_id, 4000 as price, '2022-08-22' as dt
          union all
          select 'a' as user_id, 8000 as price, '2022-08-23' as dt
         )
,dim_user AS
    (select 'a' user_id
     UNION ALL
     select 'b' user_id
     UNION ALL
     select 'c' user_id
    )
select *
from
(     select d.date_id, u.user_id
               from (select date_id
                     from dim.dim_date
                     where date_format(date_id, 'yyyy-MM') >= '2022-06'
                    ) d,
                    dim_user u
              ) d
             

Los resultados específicos son los siguientes:

Se puede observar que en cada registro de tiempo se obtiene el valor de la dimensión del usuario correspondiente.

Finalmente, usamos esta tabla como la tabla principal de la tabla de datos de combinación izquierda, y corresponde de manera única a los datos a través de la condición de asociación.

with data as
         (select 'a' as user_id, 7000 as price, '2022-07-01' as dt
          union all
          select 'a' as user_id, 4000 as price, '2022-08-22' as dt
          union all
          select 'a' as user_id, 8000 as price, '2022-08-23' as dt
         )
,dim_user AS
    (select 'a' user_id
     UNION ALL
     select 'b' user_id
     UNION ALL
     select 'c' user_id
    )
select *
from
(     select d.date_id, u.user_id
               from (select date_id
                     from dim.dim_date
                     where date_format(date_id, 'yyyy-MM') >= '2022-06'
                    ) d,
                    dim_user u
              ) d
              left join data
on d.date_id = data.dt and d.user_id=data.user_id

Los resultados específicos son los siguientes:

我们可以看到主表是比较全的维表,拥有所有的时间、用户属性,order by 后的日期应该是维表中的日期,partition by后的user_id应该为主表中的user_id,此时再用rows 求解就没有问题。

最终SQL如下:

with data as
         (select 'a' as user_id, 7000 as price, '2022-07-01' as dt
          union all
          select 'a' as user_id, 4000 as price, '2022-08-22' as dt
          union all
          select 'a' as user_id, 8000 as price, '2022-08-23' as dt
         )
,dim_user AS
    (select 'a' user_id
     UNION ALL
     select 'b' user_id
     UNION ALL
     select 'c' user_id
    )
select user_id, min(dt)
from (
         select dt
              , d.user_id
              , sum(price)
                over (partition by d.user_id order by d.date_id rows between 30 preceding and current row) as order_price
         from (
               select d.date_id, u.user_id
               from (select date_id
                     from dim.dim_date
                     where date_format(date_id, 'yyyy-MM') >= '2022-06'
                    ) d,
                    dim_user u
              ) d
              left join data
            on d.date_id = data.dt and d.user_id=data.user_id
     ) A
where order_price > 10000
group by user_id

可以看出最终求解的结果值和range的结果是一致 的。

小结:是否需要补全其他维度值,看partition by后的分组字段,有多少个就需要补全哪些,因为直接用时间维度表做主表,partition by无法正确分组,需要补全 后面的分组字段才行。改方法性能上肯定比较差,但也是比较通用的方法,对于一些窗口不支持range子句的则也只能采取这样的方法。

3 小结

本文讲解了一种求近30天消费金额的方法,给出了2种思路,2种方法都比较通用,都需要掌握。

Supongo que te gusta

Origin blog.csdn.net/godlovedaniel/article/details/128863653
Recomendado
Clasificación