¿Cómo aprovechar al máximo las capacidades de SQL?

Cómo aprovechar al máximo las capacidades de SQL es el tema de este artículo. Este artículo intenta encontrar un enfoque único, enfatizando que a través del pensamiento de procesamiento de datos flexible y divergente, se pueden resolver escenarios de datos complejos utilizando la sintaxis más básica.

I. Introducción

1.1 Intención original

Cómo utilizar MaxCompute (ODPS) SQL de manera eficiente y aplicar la sintaxis básica de SQL al extremo.

Hoy en día, cuando Big Data es tan popular, no sólo el personal profesional de datos necesita tratar con SQL con frecuencia, sino que incluso los estudiantes no técnicos, como los de productos y operaciones, utilizarán SQL en mayor o menor medida. ?, y entonces se vuelve particularmente importante aprovechar el poder de los datos.

MaxCompute (ODPS) SQL se ha desarrollado con bastante madurez hoy en día. Como dialecto SQL, su sintaxis SQL es totalmente compatible y tiene un conjunto muy rico de funciones integradas. Admite funciones de ventanas, funciones definidas por el usuario, tipos definidos por el usuario y muchos otras funciones avanzadas, que se pueden aplicar de manera eficiente en varios escenarios de procesamiento de datos.

Cómo aprovechar al máximo las capacidades de SQL es el tema de este artículo. Este artículo intenta encontrar un enfoque único, enfatizando que a través del pensamiento de procesamiento de datos flexible y divergente, se pueden resolver escenarios de datos complejos utilizando la sintaxis más básica.

1.2 Adecuado para la multitud

Ya sea principiante o experimentado, este artículo puede resultar útil, pero es más adecuado para lectores intermedios y avanzados.

Este artículo se centra en el pensamiento de procesamiento de datos y no involucra demasiada sintaxis de alto nivel. Al mismo tiempo, para evitar divergencias en el tema, las funciones y características gramaticales involucradas en el artículo no se presentarán en detalle. sus propias decisiones basadas en sus propias circunstancias.

1.3 Estructura del contenido

Este artículo presentará temas como la generación de secuencias, la transformación de intervalos, la permutación y combinación y la discriminación continua, y adjuntará casos para explicar aplicaciones prácticas. Existe una ligera dependencia entre cada tema, por lo que es mejor leerlos en secuencia.

1.4 Información inmediata

Las declaraciones SQL involucradas en este artículo solo utilizan las características de sintaxis básica de MaxCompute (ODPS) SQL. En teoría, todo SQL se puede ejecutar en la última versión. Al mismo tiempo, se señala especialmente que problemas como el entorno operativo y la compatibilidad No están dentro del alcance de este artículo.

2. secuencia numérica

Las secuencias son una de las formas de datos más comunes y las que se encuentran en escenarios reales de desarrollo de datos son básicamente secuencias finitas. Esta sección comenzará con la secuencia creciente más simple, descubrirá el método general y lo extenderá a escenarios más generales.

2.1 Secuencia común

2.1.1 Una secuencia creciente simple

Primero, se presenta un escenario de secuencia entera creciente simple:

  • A partir del valor 0;
  • Cada valor posterior aumenta en 1;
  • Fin del valor 3;

¿Cómo generar una secuencia que satisfaga las tres condiciones anteriores? Eso es [0,1,2,3].

De hecho, hay muchas formas de generar esta secuencia y aquí hay una solución simple y general.

-- SQL - 1
select
    t.pos as a_n
from (
    select posexplode(split(space(3), space(1), false))
) t;

Del fragmento de SQL anterior, podemos saber que generar una secuencia creciente solo requiere tres pasos:

1) Genere una matriz de longitud adecuada y no es necesario que los elementos de la matriz tengan un significado real;

2) Utilice la función UDTF poseexplode para generar un subíndice de índice para cada elemento de la matriz;

3) Obtenga el subíndice índice de cada elemento. Los tres pasos anteriores se pueden extender a escenarios de secuencia más generales: secuencia aritmética y secuencia geométrica. A continuación se proporcionará directamente la plantilla de implementación final basada en esto.

2.1.2 Progresión aritmética

Implementación de SQL:

-- SQL - 2
select
    a + t.pos * d as a_n
from (
    select posexplode(split(space(n - 1), space(1), false))
) t;

2.1.3 Secuencia geométrica

Implementación de SQL:

-- SQL - 3
select
    a * pow(q, t.pos) as a_n
from (
    select posexplode(split(space(n - 1), space(1), false))
) t;

Consejo: También puede utilizar directamente la secuencia de funciones del sistema MaxCompute (ODPS) para generar rápidamente una secuencia.

-- SQL - 4
select sequence(1, 3, 1);

-- result
[1, 2, 3]

2.2 Ejemplos de escenarios de aplicación

2.2.1 Restaurar nombres de familias de columnas de dimensiones en cualquier combinación de dimensiones

En escenarios de análisis multidimensional,   se pueden utilizar funciones de agregación de alto orden, como cubo  ,  resumen  ,  conjuntos de agrupación , etc., para realizar estadísticas de agregación sobre datos en diferentes combinaciones de dimensiones.

descripción de la escena

Existe una tabla de registro de acceso de usuarios visit_log. Cada fila de datos representa un registro de acceso de usuarios.

-- SQL - 5
with visit_log as (
    select stack (
        6,
        '2024-01-01', '101', '湖北', '武汉', 'Android',
        '2024-01-01', '102', '湖南', '长沙', 'IOS',
        '2024-01-01', '103', '四川', '成都', 'Windows',
        '2024-01-02', '101', '湖北', '孝感', 'Mac',
        '2024-01-02', '102', '湖南', '邵阳', 'Android',
        '2024-01-03', '101', '湖北', '武汉', 'IOS'
    ) 
    -- 字段:日期,用户,省份,城市,设备类型
    as (dt, user_id, province, city, device_type)
)
select * from visit_log;

Ahora, para las columnas de tres dimensiones de provincia, ciudad y tipo de dispositivo tipo_dispositivo, las visitas del usuario en diferentes combinaciones de dimensiones se obtienen mediante estadísticas de agregación de conjuntos de agrupaciones . Pregunta: 1) ¿Cómo saber de qué columnas de dimensión se agrega un resultado estadístico?

2) Si desea generar  el nombre de la columna de dimensión agregada para la visualización del informe posterior y otros escenarios, ¿cómo debe manejarlo?

Soluciones

Se puede  resolver con la ayuda de GROUPING__ID proporcionado por MaxCompute (ODPS), y el método principal es implementar GROUPING__ID a la inversa.

1. Prepare todos los GROUPING__ID.

GRUPO__ID bits
0 {..., 0, 0, 0}
1 {..., 0, 0, 1}
2 {..., 0, 1, 0}
3 {..., 0, 1, 1}
... ...
2n2n ...

2. Prepare todos los nombres de las dimensiones.

{ dim_name_1, dim_name_2, ..., dim_name_n }

3. Asigne GROUPING__ID al nombre de la columna de dimensión.

Para cada valor en la matriz incremental GROUPING__ID, asigne cada bit binario del valor al subíndice de la secuencia de nombres de dimensiones y genere todos los nombres de dimensiones con los bits 0 correspondientes. Por ejemplo:

GROUPING__ID:3 => { 0, 1, 1 }
维度名称序列:{ 省份, 城市, 设备类型 }

映射:{ 0:省份, 1:城市, 1:设备类型 }

GROUPING__ID 为 3 的数据行聚合维度即为:省份

implementación de SQL

-- SQL - 6
with group_dimension as (
    select -- 每种分组对应的维度字段
        gb.group_id, concat_ws(",", collect_list(case when gb.placeholder_bit = 0 then dim_col.val else null end)) as dimension_name
    from (
        select groups.pos as group_id, pe.*
        from (
            select posexplode(split(space(cast(pow(2, 3) as int) - 1), space(1), false))
        ) groups -- 所有分组
        lateral view posexplode(regexp_extract_all(lpad(conv(groups.pos,10,2), 3, "0"), '(0|1)')) pe as placeholder_idx, placeholder_bit -- 每个分组的bit信息
    ) gb
    left join ( -- 所有维度字段
        select posexplode(split("省份,城市,设备类型", ','))
    ) dim_col on gb.placeholder_idx = dim_col.pos
    group by gb.group_id
)
select 
    group_dimension.dimension_name,
    province, city, device_type,
    visit_count
from (
    select
        grouping_id(province, city, device_type) as group_id,
        province, city, device_type,
        count(1) as visit_count
    from visit_log b
    group by province, city, device_type
    GROUPING SETS(
        (province),
        (province, city),
        (province, city, device_type)
    )
) t
join group_dimension on t.group_id = group_dimension.group_id
order by group_dimension.dimension_name;
nombre_dimensión provincia ciudad tipo de dispositivo recuento_visitas
provincia Hubei NULO NULO 3
provincia hunan NULO NULO 2
provincia Sichuan NULO NULO 1
ciudad provincial Hubei Wuhan NULO 2
ciudad provincial hunan Changshá NULO 1
ciudad provincial hunan shaoyang NULO 1
ciudad provincial Hubei Xiaogan NULO 1
ciudad provincial Sichuan Chengdú NULO 1
Provincia, ciudad, tipo de equipo. Hubei Xiaogan Mac 1
Provincia, ciudad, tipo de equipo. hunan Changshá IOS 1
Provincia, ciudad, tipo de equipo. hunan shaoyang Androide 1
Provincia, ciudad, tipo de equipo. Sichuan Chengdú ventanas 1
Provincia, ciudad, tipo de equipo. Hubei Wuhan Androide 1
Provincia, ciudad, tipo de equipo. Hubei Wuhan IOS 1

3. Intervalo

Los intervalos tienen características de datos diferentes en comparación con la secuencia, pero en aplicaciones prácticas, el procesamiento de secuencia e intervalo tiene muchas similitudes. Esta sección presentará algunos escenarios de intervalos comunes y soluciones comunes abstractas.

3.1 Operaciones de intervalo comunes

3.1.1 Segmentación de intervalos

Implementación de SQL:

-- SQL - 7
select
    a + t.pos * d as sub_interval_start, -- 子区间起始值
    a + (t.pos + 1) * d as sub_interval_end -- 子区间结束值
from (
    select posexplode(split(space(n - 1), space(1), false))
) t;

3.1.2 Cruce de intervalos

Se sabe que los dos intervalos de fechas se cruzan ['2024-01-01', '2024-01-03'] y ['2024-01-02', '2024-01-04']. preguntar:

1) ¿Cómo fusionar dos rangos de fechas y devolver el nuevo rango combinado?

2) ¿Cómo saber qué fechas son fechas de cruce y devolver el número de cruces para esa fecha?

Hay muchas maneras de resolver el problema anterior, aquí hay una solución simple y general. La idea central es combinar los métodos de generación de secuencia y segmentación de intervalos para descomponer primero el intervalo de fechas en la unidad de procesamiento más pequeña, es decir, una secuencia compuesta de múltiples fechas, y luego hacer estadísticas basadas en la granularidad de las fechas. Los pasos específicos son los siguientes:

1) Obtener el número de días incluidos en cada intervalo de fechas; 2) Dividir el intervalo de fechas en un número correspondiente de secuencias de fechas crecientes en función del número de días incluidos en el intervalo de fechas;

3) Cuente los intervalos fusionados y el número de cruces a través de series de fechas.

Implementación de SQL:

-- SQL - 8
with dummy_table as (
    select stack(
        2,
        '2024-01-01', '2024-01-03',
        '2024-01-02', '2024-01-04'
    ) as (date_start, date_end)
)
select 
    min(date_item) as date_start_merged, 
    max(date_item) as date_end_merged, 
    collect_set( -- 交叉日期计数
        case when date_item_cnt > 1 then concat(date_item, ':', date_item_cnt) else null end
    ) as overlap_date
from (
    select 
        -- 拆解后的单个日期
        date_add(date_start, pos) as date_item,
        -- 拆解后的单个日期出现的次数
        count(1) over (partition by date_add(date_start, pos)) as date_item_cnt
    from dummy_table
    lateral view posexplode(split(space(datediff(date_end, date_start)), space(1), false)) t as pos, val
) t;
fecha_inicio_merged fecha_end_merged fecha_superposición
2024-01-01 2024-01-04 ["2024-01-02:2","2024-01-03:2"]

¡Hazlo un poco más difícil!

Cómo resolver el problema anterior si hay varios intervalos de fechas y se desconoce el estado de intersección entre los intervalos. Ahora mismo:

1) ¿Cómo fusionar varios rangos de fechas y devolver varios rangos combinados nuevos?

2) ¿Cómo saber qué fechas son fechas de cruce y devolver el número de cruces para esa fecha?

Implementación de SQL:

-- SQL - 9
with dummy_table as (
    select stack(
        5,
        '2024-01-01', '2024-01-03',
        '2024-01-02', '2024-01-04',
        '2024-01-06', '2024-01-08',
        '2024-01-08', '2024-01-08',
        '2024-01-07', '2024-01-10'
    ) as (date_start, date_end)
)
select
    min(date_item) as date_start_merged, 
    max(date_item) as date_end_merged,
    collect_set( -- 交叉日期计数
        case when date_item_cnt > 1 then concat(date_item, ':', date_item_cnt) else null end
    ) as overlap_date
from (
    select 
        -- 拆解后的单个日期
        date_add(date_start, pos) as date_item,
        -- 拆解后的单个日期出现的次数
        count(1) over (partition by date_add(date_start, pos)) as date_item_cnt,
        -- 对于拆解后的单个日期,重组为新区间的标记
        date_add(date_add(date_start, pos), 1 - dense_rank() over (order by date_add(date_start, pos))) as cont
    from dummy_table
    lateral view posexplode(split(space(datediff(date_end, date_start)), space(1), false)) t as pos, val
) t
group by cont;
fecha_inicio_merged fecha_end_merged fecha_superposición
2024-01-01 2024-01-04 ["2024-01-02:2","2024-01-03:2"]
2024-01-06 2024-01-10 ["2024-01-07:2","2024-01-08:3"]

3.2 Ejemplos de escenarios de aplicación

3.2.1 Estadísticas basadas en cualquier período de tiempo

descripción de la escena

Existe una tabla de plan de pago de usuario usuario_repago. Un dato en esta tabla indica que el usuario reembolsará el yuan de pago todos los días dentro del intervalo de fechas especificado [fecha_inicio, fecha_final].

-- SQL - 10
with user_repayment as (
    select stack(
        3,
        '101', '2024-01-01', '2024-01-15', 10,
        '102', '2024-01-05', '2024-01-20', 20,
        '103', '2024-01-10', '2024-01-25', 30
    ) 
    -- 字段:用户,开始日期,结束日期,每日还款金额
    as (user_id, date_start, date_end, repayment)
)
select * from user_repayment;

¿Cómo contar el pago total adeudado por todos los usuarios todos los días durante cualquier período de tiempo (por ejemplo: 2024-01-15 al 2024-01-16)?

Soluciones

La idea central es convertir el intervalo de fechas en una secuencia de fechas y luego realizar estadísticas resumidas basadas en la secuencia de fechas.

implementación de SQL

-- SQL - 11
select 
    date_item as day, 
    sum(repayment) as total_repayment
from (
    select 
        date_add(date_start, pos) as date_item,
        repayment
    from user_repayment
    lateral view posexplode(split(space(datediff(date_end, date_start)), space(1), false)) t as pos, val
) t
where date_item >= '2024-01-15' and date_item <= '2024-01-16'
group by date_item
order by date_item;
día pago_total
2024-01-15 60
2024-01-16 50

4. Disposición y combinación.

La permutación y combinación son métodos de organización de datos comúnmente utilizados para datos discretos. Esta sección presentará los métodos de implementación de permutación y combinación respectivamente, y se centrará en el procesamiento de datos mediante combinación con ejemplos.

4.1 Operaciones comunes de permutación y combinación

4.1.1 Disposición

Dada la secuencia de caracteres ['A', 'B', 'C'], se pueden seleccionar 2 caracteres repetidamente de esta secuencia cada vez. ¿Cómo obtener todas las permutaciones?

Se puede resolver con la ayuda de múltiples vistas laterales y la implementación general es relativamente simple.

-- SQL - 12
select 
    concat(val1, val2) as perm
from (select split('A,B,C', ',') as characters) dummy
lateral view explode(characters) t1 as val1
lateral view explode(characters) t2 as val2;
permanente
Automóvil club británico
AB
C.A.
licenciado en Letras
CAMA Y DESAYUNO
antes de Cristo
California
CB
CC

4.1.2 Combinación

Dada la secuencia de caracteres ['A', 'B', 'C'], se pueden seleccionar 2 caracteres repetidamente de esta secuencia cada vez. ¿Cómo obtener todas las combinaciones?

Se puede resolver con la ayuda de múltiples vistas laterales y la implementación general es relativamente simple.

-- SQL - 13
select 
    concat(least(val1, val2), greatest(val1, val2)) as comb
from (select split('A,B,C', ',') as characters) dummy
lateral view explode(characters) t1 as val1
lateral view explode(characters) t2 as val2
group by least(val1, val2), greatest(val1, val2);
peine
Automóvil club británico
AB
C.A.
CAMA Y DESAYUNO
antes de Cristo
CC


Consejo: También puede utilizar directamente las combinaciones de funciones del sistema MaxCompute (ODPS) para generar combinaciones rápidamente.

-- SQL - 14
select combinations(array('foo', 'bar', 'boo'),2);

-- result
[['foo', 'bar'], ['foo', 'boo']['bar', 'boo']]

4.2 Ejemplos de escenarios de aplicación

4.2.1 Estadísticas de comparación de grupos

descripción de la escena

Tabla de conversión de estrategias de entrega existente. Un dato en esta tabla representa el volumen de pedidos generado por una determinada estrategia de entrega en un día.

-- SQL - 15
with strategy_order as (
    select stack(
        3,
        '2024-01-01', 'Strategy A', 10,
        '2024-01-01', 'Strategy B', 20,
        '2024-01-01', 'Strategy C', 30
    ) 
    -- 字段:日期,投放策略,单量
    as (dt, strategy, order_cnt)
)
select * from strategy_order;

¿Cómo crear grupos de comparación por pares según las estrategias de entrega y comparar y mostrar el estado de conversión único de diferentes estrategias mediante comparación de grupos?

grupo de comparación estrategia de entrega Convertir una cantidad única
Estrategia A-Estrategia B Estrategia A xxx
Estrategia A-Estrategia B Estrategia B xxx

Soluciones

La idea central es extraer dos estrategias sin duplicación de la lista de todas las estrategias de entrega, generar todos los resultados combinados y luego asociar la tabla estrategia_orden que agrupa los resultados estadísticos.

implementación de SQL

-- SQL - 16
select /*+ mapjoin(combs) */
    combs.strategy_comb,
    so.strategy,
    so.order_cnt
from strategy_order so
join ( -- 生成所有对比组
    select 
        concat(least(val1, val2), '-', greatest(val1, val2)) as strategy_comb,
        least(val1, val2) as strategy_1, greatest(val1, val2) as strategy_2
    from (
        select collect_set(strategy) as strategies
        from strategy_order
    ) dummy
    lateral view explode(strategies) t1 as val1
    lateral view explode(strategies) t2 as val2
    where val1 <> val2
    group by least(val1, val2), greatest(val1, val2)
) combs on 1 = 1
where so.strategy in (combs.strategy_1, combs.strategy_2)
order by combs.strategy_comb, so.strategy;
grupo de comparación estrategia de entrega Convertir una cantidad única
Estrategia A-Estrategia B Estrategia A 10
Estrategia A-Estrategia B Estrategia B 20
Estrategia A-Estrategia C Estrategia A 10
Estrategia A-Estrategia C Estrategia C 30
Estrategia B-Estrategia C Estrategia B 20
Estrategia B-Estrategia C Estrategia C 30

5. Continuo

Esta sección presenta principalmente problemas de continuidad y se centra en describir escenarios activos continuos comunes. Para la actividad continua de tipos estáticos y la actividad continua de tipos dinámicos, se explican respectivamente diferentes soluciones de implementación.

5.1 Estadísticas generales de actividad continua

descripción de la escena

Existe una tabla de registro de acceso de usuarios visit_log. Cada fila de datos representa un registro de acceso de usuarios.

-- SQL - 17
with visit_log as (
    select stack (
        6,
        '2024-01-01', '101', '湖北', '武汉', 'Android',
        '2024-01-01', '102', '湖南', '长沙', 'IOS',
        '2024-01-01', '103', '四川', '成都', 'Windows',
        '2024-01-02', '101', '湖北', '孝感', 'Mac',
        '2024-01-02', '102', '湖南', '邵阳', 'Android',
        '2024-01-03', '101', '湖北', '武汉', 'IOS'
    ) 
    -- 字段:日期,用户,省份,城市,设备类型
    as (dt, user_id, province, city, device_type)
)
select * from visit_log;

¿Cómo obtener usuarios cuyas visitas consecutivas sean mayores o iguales a 2 días?

Al analizar la continuidad para los problemas anteriores, el resultado de obtener continuidad estará sujeto a exceder un umbral fijo , que se clasifica aquí como  estadísticas de escena activa continua ordinaria donde la actividad continua es mayor que el umbral de N días .

implementación de SQL

Implementado en base a diferencias de fechas adyacentes (versión retrasada/adelante)

La implementación general es relativamente simple.

-- SQL - 18
select user_id
from (
    select 
        *,
        lag(dt, 2 - 1) over (partition by user_id order by dt) as lag_dt
    from (select dt, user_id from visit_log group by dt, user_id) t0
) t1
where datediff(dt, lag_dt) + 1 = 2
group by user_id;
ID_usuario
101
102

Implementación basada en diferencia de fechas adyacentes (versión ordenada)

La implementación general es relativamente simple.

-- SQL - 19
select user_id
from (
    select *, 
        dense_rank() over (partition by user_id order by dt) as dr
    from visit_log
) t1
where datediff(dt, date_add(dt, 1 - dr)) + 1 = 2
group by user_id;
ID_usuario
101
102

Basado en el número de días activos consecutivos

Puede considerarse como  una versión derivada de la implementación basada en diferencias de fechas adyacentes (versión ordenada)  , que puede obtener más información, como el número de días activos consecutivos.

-- SQL - 20
select user_id
from (
    select 
        *,
        -- 连续活跃天数
        count(distinct dt) 
            over (partition by user_id, cont) as cont_days
    from (
        select 
            *, 
            date_add(dt, 1 - dense_rank() 
                over (partition by user_id order by dt)) as cont
        from visit_log
    ) t1
) t2
where cont_days >= 2
group by user_id;
ID_usuario
101
102

Implementado en base a un intervalo activo continuo

Puede considerarse como  una versión derivada de la implementación basada en diferencias de fechas adyacentes (versión ordenada)  , que puede obtener más información, como intervalos activos continuos.

-- SQL - 21
select user_id
from (
    select 
        user_id, cont, 
        -- 连续活跃区间
        min(dt) as cont_date_start, max(dt) as cont_date_end
    from (
        select 
            *, 
            date_add(dt, 1 - dense_rank() 
                over (partition by user_id order by dt)) as cont
        from visit_log
    ) t1
    group by user_id, cont
) t2
where datediff(cont_date_end, cont_date_start) + 1 >= 2
group by user_id;
ID_usuario
101
102

5.2 Estadísticas dinámicas de actividad continua

descripción de la escena

Existe una tabla de registro de acceso de usuarios visit_log. Cada fila de datos representa un registro de acceso de usuarios.

-- SQL - 22
with visit_log as (
    select stack (
        6,
        '2024-01-01', '101', '湖北', '武汉', 'Android',
        '2024-01-01', '102', '湖南', '长沙', 'IOS',
        '2024-01-01', '103', '四川', '成都', 'Windows',
        '2024-01-02', '101', '湖北', '孝感', 'Mac',
        '2024-01-02', '102', '湖南', '邵阳', 'Android',
        '2024-01-03', '101', '湖北', '武汉', 'IOS'
    ) 
    -- 字段:日期,用户,省份,城市,设备类型
    as (dt, user_id, province, city, device_type)
)
select * from visit_log;

¿Cómo obtener los dos usuarios activos continuamente más largos y generar los usuarios, los días activos continuos más largos y el rango de fechas activas continuas más largo?

Al analizar la continuidad de los problemas anteriores, el resultado de obtener continuidad no es ni puede compararse con un umbral fijo, sino que cada uno utiliza la actividad continua más larga como un umbral dinámico , que se clasifica aquí como  estadísticas dinámicas de escena activa continua .

implementación de SQL

 Se puede ampliar según  la idea de estadísticas de escenas activas continuas ordinarias . El SQL final se proporciona directamente aquí:

-- SQL - 23
select
    user_id, 
    -- 最长连续活跃天数
    datediff(max(dt), min(dt)) + 1 as cont_days,
    -- 最长连续活跃日期区间
    min(dt) as cont_date_start, max(dt) as cont_date_end
from (
    select 
        *, 
        date_add(dt, 1 - dense_rank() 
            over (partition by user_id order by dt)) as cont
    from visit_log
) t1
group by user_id, cont
order by cont_days desc
limit 2;
ID_usuario días_continuos cont_fecha_inicio cont_date_end
101 3 2024-01-01 2024-01-03
102 2 2024-01-01 2024-01-02

6. Extensión

Conduce a escenarios más complejos, que son combinaciones y variaciones del contenido de los capítulos anteriores de este artículo.

6.1 Intervalos continuos (divididos por el subintervalo más largo)

descripción de la escena

Hay una tabla de registros user_wifi_log para los usuarios existentes que escanean o se conectan a WiFi. Cada fila de datos representa el registro de un usuario que escanea o se conecta a WiFi en un momento determinado.

-- SQL - 24
with user_wifi_log as (
    select stack (
        9,
        '2024-01-01 10:01:00', '101', 'cmcc-Starbucks', 'scan', -- 扫描
        '2024-01-01 10:02:00', '101', 'cmcc-Starbucks', 'scan',
        '2024-01-01 10:03:00', '101', 'cmcc-Starbucks', 'scan',
        '2024-01-01 10:04:00', '101', 'cmcc-Starbucks', 'conn', -- 连接
        '2024-01-01 10:05:00', '101', 'cmcc-Starbucks', 'conn',
        '2024-01-01 10:06:00', '101', 'cmcc-Starbucks', 'conn',
        '2024-01-01 11:01:00', '101', 'cmcc-Starbucks', 'conn',
        '2024-01-01 11:02:00', '101', 'cmcc-Starbucks', 'conn',
        '2024-01-01 11:03:00', '101', 'cmcc-Starbucks', 'conn'
    ) 
    -- 字段:时间,用户,WiFi,状态(扫描、连接)
    as (time, user_id, wifi, status)
)
select * from user_wifi_log;

Ahora necesitamos analizar el comportamiento del usuario: ¿Cómo dividir los diferentes intervalos de comportamiento WiFi de los usuarios ? satisfacer:

1) Los tipos de comportamiento se dividen en dos tipos: conexión (escaneo) y escaneo (conexión);

2) La definición de intervalo de comportamiento es: el mismo tipo de comportamiento y la diferencia de tiempo entre dos comportamientos adyacentes no excede los 30 minutos;

3) Se deben tomar diferentes intervalos de comportamiento siempre que cumplan con la definición;

ID_usuario Wifi estado hora_inicio tiempo_fin Observación
101 cmcc-starbucks escanear 2024-01-01 10:01:00 2024-01-01 10:03:00 WiFi escaneado por el usuario
101 cmcc-starbucks conectar 2024-01-01 10:04:00 2024-01-01 10:06:00 Usuario conectado a WiFi
101 cmcc-starbucks conectar 2024-01-01 11:01:00 2024-01-01 11:02:00 Si han pasado más de 30 minutos desde la última conexión, se considera un nuevo comportamiento de conexión.


El problema anterior es un poco más complejo y puede verse  como una variante de la racha activa más larga  introducida en  Estadísticas dinámicas de actividad continua  . Puede describirse como   un problema de dividir el subintervalo más largo combinando el umbral de continuidad y la información contextual en la secuencia de comportamiento .

implementación de SQL

Lógica central: usuarios grupales y WIFI, combinan umbrales de continuidad e información de contexto de secuencia de comportamiento para dividir intervalos de comportamiento.

pasos detallados:

1) Agrupe los datos por usuario y WIFI, y clasifique los datos en orden cronológico dentro de la ventana del grupo; 2) Atraviese dos registros adyacentes en la ventana del grupo en secuencia. Si la diferencia de tiempo entre los dos registros excede los 30 minutos, o el tiempo diferencia entre los dos registros Cuando el estado de comportamiento (estado de escaneo, estado de conexión) cambia, el intervalo de comportamiento se divide por el punto crítico. Hasta que se recorran todos los registros;

3) Resultado de salida final: usuario, WIFI, estado de comportamiento (estado de escaneo, estado de conexión), hora de inicio del comportamiento, hora de finalización del comportamiento;

-- SQL - 25
select 
    user_id, 
    wifi,
    max(status) as status,
    min(time) as start_time, 
    max(time) as end_time
from (
    select *,
        max(if(lag_status is null or lag_time is null or status <> lag_status or datediff(time, lag_time, 'ss') > 60 * 30, rn, null)) 
            over (partition by user_id, wifi order by time) as group_idx
    from (
        select *,
            row_number() over (partition by user_id, wifi order by time) as rn,
            lag(time, 1) over (partition by user_id, wifi order by time) as lag_time,
            lag(status, 1) over (partition by user_id, wifi order by time) as lag_status
        from user_wifi_log
    ) t1
) t2
group by user_id, wifi, group_idx
;
ID_usuario Wifi estado hora de inicio hora de finalización
101 cmcc-starbucks escanear 2024-01-01 10:01:00 2024-01-01 10:03:00
101 cmcc-starbucks conectar 2024-01-01 10:04:00 2024-01-01 10:06:00
101 cmcc-starbucks conectar 2024-01-01 11:01:00 2024-01-01 11:03:00


Las condiciones de discriminación de continuidad en este caso se pueden extender a más escenarios, como escenarios de datos basados ​​en diferencias de fechas, diferencias horarias, tipos de enumeración, diferencias de distancia, etc. como condiciones de discriminación de continuidad.

Conclusión

A través de un pensamiento de procesamiento de datos flexible y difuso, puede utilizar la sintaxis básica para resolver escenarios de datos complejos. Ésta  es la idea a lo largo de este artículo. Este artículo proporciona soluciones relativamente generales para escenarios comunes como generación de secuencias, transformación de intervalos, permutación y combinación y discriminación continua, y explica aplicaciones prácticas con ejemplos.

Este artículo intenta encontrar un enfoque único y enfatiza el pensamiento de procesamiento de datos flexible. Espero que haga que los lectores se sientan más brillantes y realmente pueda ayudar a los lectores. Al mismo tiempo, después de todo, las capacidades personales son limitadas y las ideas pueden no ser necesariamente óptimas e incluso pueden producirse errores. Comentarios o sugerencias son bienvenidos.

Autor | Rigo

Haga clic para probar el producto en la nube de forma gratuita ahora y comenzar su viaje práctico en la nube.

Enlace original

Este artículo es contenido original de Alibaba Cloud y no puede reproducirse sin permiso.

阿里云严重故障,全线产品受影响(已恢复) 汤不热 (Tumblr) 凉了 俄罗斯操作系统 Aurora OS 5.0 全新 UI 亮相 Delphi 12 & C++ Builder 12、RAD Studio 12 发布 多家互联网公司急招鸿蒙程序员 UNIX 时间即将进入 17 亿纪元(已进入) 美团招兵买马,拟开发鸿蒙系统 App 亚马逊开发基于 Linux 的操作系统,以摆脱 Android 依赖 Linux 上的 .NET 8 独立体积减少 50% FFmpeg 6.1 "Heaviside" 发布
{{o.name}}
{{m.name}}

Supongo que te gusta

Origin my.oschina.net/yunqi/blog/10139796
Recomendado
Clasificación