SQL の機能を最大限に活用するにはどうすればよいでしょうか?

SQL の機能を最大限に活用する方法がこの記事のテーマです。この記事では、柔軟で多様なデータ処理の考え方を通じて、最も基本的な構文を使用して複雑なデータ シナリオを解決できることを強調し、独自のアプローチを見つけようとします。

I.はじめに

1.1 当初の意図

MaxCompute (ODPS) SQL を効率的に使用し、基本的な SQL 構文を極限まで適用する方法。

ビッグ データが非常に普及している今日、プロのデータ担当者だけが SQL を頻繁に扱う必要があるわけではなく、製品や運用の学生などの非技術的な学生でも多かれ少なかれ SQL を使用することになります。 ? そして、データの力を活用することが特に重要になります。

MaxCompute (ODPS) SQL は現在、かなり成熟して開発されています。SQL 言語として、その SQL 構文は完全にサポートされており、非常に豊富な組み込み関数のセットを備えています。ウィンドウ関数、ユーザー定義関数、ユーザー定義型などをサポートしています。その他の高度な機能。さまざまなデータ処理シナリオに効率的に適用できます。

SQL の機能を最大限に活用する方法がこの記事のテーマです。この記事では、柔軟で多様なデータ処理の考え方を通じて、最も基本的な構文を使用して複雑なデータ シナリオを解決できることを強調し、独自のアプローチを見つけようとします。

1.2 群衆に適している

この記事は初心者でも経験者でも役立つかもしれませんが、中級者および上級者の読者に適しています。

この記事はデータ処理の考え方に焦点を当てており、高度な構文はあまり含まれていませんが、同時に、トピックの発散を避けるために、記事に含まれる関数や文法的特徴については詳しく紹介しません。自分自身の状況に基づいて自分で決定し、学びます。

1.3 コンテンツの構造

この記事では、数列の生成、区間変換、順列と組み合わせ、連続判別などのトピックを紹介し、実際の応用例を添付して説明します。各トピック間には若干の依存性があるため、順番に読むことをお勧めします。

1.4 プロンプト情報

この記事に含まれる SQL ステートメントは、MaxCompute (ODPS) SQL の基本的な構文機能のみを使用しています。理論上は、すべての SQL を最新バージョンで実行できます。同時に、動作環境や互換性などの問題にも特に注意してください。はこの記事の範囲外です。

二、数列

シーケンスは最も一般的なデータ形式の 1 つであり、実際のデータ開発シナリオで遭遇するものは基本的に有限シーケンスです。このセクションでは、最も単純な増加シーケンスから始めて、一般的な方法を見つけて、それをより一般的なシナリオに拡張します。

2.1 共通シーケンス

2.1.1 単純な増加シーケンス

まず、単純な整数増加シーケンスのシナリオが導入されます。

  • 値 0 から開始します。
  • 後続の各値は 1 ずつ増加します。
  • 値 3 まで。

上記の3つの条件を満たすシーケンスを生成するにはどうすればよいでしょうか? つまり[0,1,2,3]です。

実際、このシーケンスを生成するには多くの方法がありますが、ここではシンプルで一般的な解決策を示します。

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

上記の SQL フラグメントから、増加シーケンスの生成に必要な手順は 3 つだけであることがわかります。

1) 適切な長さの配列を生成します。配列内の要素は実際の意味を持つ必要はありません。

2) UDTF 関数poseexplodeを使用して、配列内の各要素のインデックス添字を生成します。

3) 各要素のインデックス添字を取得します。上記の 3 つのステップは、より一般的なシーケンス シナリオ (算術シーケンスと幾何シーケンス) に拡張できます。以下は、これに基づいて最終的な実装テンプレートを直接提供します。

2.1.2 等差数列

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 幾何学的シーケンス

SQLの実装:

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

ヒント: MaxCompute (ODPS) システム関数シーケンスを直接使用して、シーケンスをすばやく生成することもできます。

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

-- result
[1, 2, 3]

2.2 応用シナリオの例

2.2.1 ディメンションの任意の組み合わせでディメンション列ファミリー名を復元する

多次元分析シナリオでは、  cube  、  rollup  、  grouping set などの高次の集計関数を使用して、さまざまな次元の組み合わせでデータの集計統計を実行できます。

シーンの説明

既存のユーザー アクセス ログ テーブル visit_log があり、データの各行がユーザー アクセス ログを表します。

-- 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;

ここで、州、市、およびデバイス タイプ device_type の 3 つのディメンション列について、さまざまなディメンションの組み合わせでのユーザーの訪問数が、グループ化セットの集計統計を通じて取得されます。質問: 1) 統計結果がどのディメンション列から集計されているかを知るにはどうすればよいですか?

2) 下流のレポート表示やその他のシナリオのために集計されたディメンション列の名前を出力したい場合 、どのように処理すればよいですか?

ソリューション

これは、 MaxCompute (ODPS) が提供する GROUPING__ID を利用して解決できます。中心的な方法は、GROUPING__ID を逆に実装することです。

1. すべての GROUPING__ID を準備します。

グループID ビット
0 { ..., 0, 0, 0 }
1 { ..., 0, 0, 1 }
2 { ..., 0, 1, 0 }
3 { ..., 0, 1, 1 }
... ...
2n2n ...

2. すべてのディメンション名を準備します。

{ dim_name_1, dim_name_2, ..., dim_name_n }

3. GROUPING__ID をディメンション列名にマップします。

GROUPING__ID 増分配列の各値について、値の各バイナリ ビットを次元名シーケンスの添字にマップし、対応するビット 0 を持つすべての次元名を出力します。例えば:

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

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

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

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;
次元名 デバイスタイプ 訪問数
湖北省 ヌル ヌル 3
湖南省 ヌル ヌル 2
四川省 ヌル ヌル 1
県、市 湖北省 武漢 ヌル 2
県、市 湖南省 長沙 ヌル 1
県、市 湖南省 邵陽 ヌル 1
県、市 湖北省 小干 ヌル 1
県、市 四川省 成都 ヌル 1
県、市、設備の種類 湖北省 小干 マック 1
県、市、設備の種類 湖南省 長沙 IOS 1
県、市、設備の種類 湖南省 邵陽 アンドロイド 1
県、市、設備の種類 四川省 成都 ウィンドウズ 1
県、市、設備の種類 湖北省 武漢 アンドロイド 1
県、市、設備の種類 湖北省 武漢 IOS 1

3. インターバル

インターバルにはシーケンスとは異なるデータ特性がありますが、実際のアプリケーションでは、シーケンスとインターバルの処理には多くの類似点があります。このセクションでは、いくつかの一般的な間隔シナリオと抽象的な共通ソリューションを紹介します。

3.1 一般的なインターバル操作

3.1.1 区間分割

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 インターバルクロスオーバー

2 つの日付間隔が ['2024-01-01', '2024-01-03'] と ['2024-01-02', '2024-01-04'] で交差していることがわかっています。聞く:

1) 2 つの日付範囲をマージし、マージされた新しい範囲を返す方法は?

2) どの日付がクロスオーバー日であるかを確認し、その日付のクロスオーバー数を返すにはどうすればよいですか?

上記の問題を解決するには多くの方法がありますが、ここではシンプルで一般的な解決策を示します。中心的なアイデアは、シーケンス生成と間隔セグメント化の方法を組み合わせて、最初に日付間隔を最小の処理単位、つまり複数の日付で構成されるシーケンスに分解し、次に日付の粒度に基づいて統計を作成することです。具体的な手順は次のとおりです。

1) 各日付間隔に含まれる日数を取得します; 2) 日付間隔に含まれる日数に基づいて、日付間隔を対応する数の増加する日付シーケンスに分割します。

3) マージされた間隔と日付シリーズ全体のクロスオーバー数をカウントします。

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;
date_start_merged date_end_merged 重複日付
2024-01-01 2024-01-04 ["2024-01-02:2","2024-01-03:2"]

もう少し難しくしてください!

複数の日付間隔があり、間隔間の交差ステータスが不明な場合に、上記の問題を解決する方法。今すぐ:

1) 複数の日付範囲をマージし、複数の新しくマージされた範囲を返す方法は?

2) どの日付がクロスオーバー日であるかを確認し、その日付のクロスオーバー数を返すにはどうすればよいですか?

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;
date_start_merged date_end_merged 重複日付
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 応用シナリオの例

3.2.1 任意の期間に基づく統計

シーンの説明

既存のユーザー返済計画テーブル user_repayment があり、このテーブルのデータは、ユーザーが指定された日付間隔 [date_start, date_end] 内に毎日元を返済することを示しています。

-- 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;

任意の期間 (例: 2024 年 1 月 15 日から 2024 年 1 月 16 日まで) に毎日、すべてのユーザーの返済義務のある合計返済額を計算するにはどうすればよいですか?

ソリューション

中心となるアイデアは、日付間隔を日付シーケンスに変換し、その日付シーケンスに基づいて要約統計を実行することです。

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;
返済総額
2024-01-15 60
2024-01-16 50

4. 配置と組み合わせ

離散データのデータ構成方法としては順列と組み合わせがよく使われますが、ここではそれぞれ順列と組み合わせの実装方法を紹介し、組み合わせによるデータの処理を例とともに紹介します。

4.1 一般的な順列および組み合わせ演算

4.1.1 配置

文字シーケンス ['A', 'B', 'C'] を指定すると、毎回このシーケンスから 2 つの文字を繰り返し選択できます。すべての順列を取得するにはどうすればよいですか?

これは複数の側面図を利用して解決でき、全体的な実装は比較的単純です。

-- 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;
パーマ
AA
AB
交流
BA
BB
紀元前
カリフォルニア州
CB
CC

4.1.2 組み合わせ

文字シーケンス ['A', 'B', 'C'] がある場合、毎回このシーケンスから 2 つの文字を繰り返し選択できます。すべての組み合わせを取得するにはどうすればよいですか?

これは複数の側面図を利用して解決でき、全体的な実装は比較的単純です。

-- 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);
AA
AB
交流
BB
紀元前
CC


ヒント: MaxCompute (ODPS) システム関数の組み合わせを直接使用して、組み合わせをすばやく生成することもできます。

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

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

4.2 応用シナリオの例

4.2.1 グループ比較統計

シーンの説明

既存の配送戦略換算表。この表のデータは、ある配送戦略による 1 日の注文量を表します。

-- 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;

配信戦略に従ってペアごとの比較グループを作成し、グループ比較によって異なる戦略の単一コンバージョン状況を比較して表示するにはどうすればよいですか?

比較グループ 配信戦略 単一数量を変換する
戦略A-戦略B 戦略A xxx
戦略A-戦略B 戦略B xxx

ソリューション

中心的なアイデアは、すべての配信戦略のリストから重複することなく 2 つの戦略を抽出し、すべての結合結果を生成し、その後、統計結果をグループ化する Strategy_order テーブルを関連付けることです。

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;
比較グループ 配信戦略 単一数量を変換する
戦略A-戦略B 戦略A 10
戦略A-戦略B 戦略B 20
戦略A~戦略C 戦略A 10
戦略A~戦略C 戦略C 30
戦略B-戦略C 戦略B 20
戦略B-戦略C 戦略C 30

5. 継続的

このセクションでは、主に継続性の問題について紹介し、一般的な継続的アクティブ シナリオの説明に重点を置きます。静的型の継続的アクティビティと動的型の継続的アクティビティについて、それぞれ異なる実装ソリューションについて説明します。

5.1 一般的な継続的アクティビティ統計

シーンの説明

既存のユーザー アクセス ログ テーブル visit_log があり、データの各行がユーザー アクセス ログを表します。

-- 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;

連続訪問が 2 日以上のユーザーを取得するにはどうすればよいですか?

上記の問題に対して継続性を分析する場合、連続性を取得した結果は一定のしきい値を超える必要があります。ここでは、 継続的なアクティビティが N 日間のしきい値を超える通常の連続アクティブ シーン統計として分類されます。

SQLの実装

隣接する日付の差に基づいて実装 (ラグ/リード バージョン)

全体的な実装は比較的単純です。

-- 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
101
102

隣接日付差に基づく実装(ソート版)

全体的な実装は比較的単純です。

-- 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
101
102

連続アクティブ日数に基づく

 これは、連続したアクティブ日数などのより多くの情報を取得できる、隣接する日付の差に基づく実装 (ソート版)の派生バージョンとみなすことができます 。

-- 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
101
102

連続アクティブ間隔に基づいて実装されます。

これは、隣接する日付の差分に基づく実装 (ソート版)の派生バージョンとみなすことができ  、連続したアクティブ間隔など、より多くの情報を取得できます。

-- 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
101
102

5.2 動的継続アクティビティ統計

シーンの説明

既存のユーザー アクセス ログ テーブル visit_log があり、データの各行がユーザー アクセス ログを表します。

-- 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;

最も長く継続的にアクティブな 2 人のユーザーを取得し、そのユーザー、最長の連続アクティブ日数、および最長の連続アクティブ期間を出力するにはどうすればよいでしょうか?

上記の問題に対して連続性を分析する場合、連続性を取得した結果は固定のしきい値と比較することはできず、代わりに、それぞれが最も長く連続したアクティビティを動的しきい値として使用し、ここでは動的 連続アクティブ シーン統計として分類します。

SQLの実装

これは、通常の連続的なアクティブ シーンの統計のアイデアに基づいて  拡張できます。最終的な SQL はここに直接与えられます。

-- 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 cont_days cont_date_start cont_date_end
101 3 2024-01-01 2024-01-03
102 2 2024-01-01 2024-01-02

6. 拡張

これにより、この記事の前の章の内容を組み合わせたり、変形したりする、より複雑なシナリオが生まれます。

6.1 連続間隔 (最長のサブ間隔で割ったもの)

シーンの説明

WiFi をスキャンまたは接続している既存のユーザーの user_wifi_log レコード テーブルがあり、データの各行は、特定の時点でのユーザーのスキャンまたは WiFi への接続のログを表します。

-- 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;

次に、ユーザーの行動を分析する必要があります。ユーザーのさまざまな WiFi 行動の間隔をどのように分割するか? 満足する:

1) 動作タイプは、接続 (scan) とスキャン (conn) の 2 つのタイプに分類されます。

2) 行動間隔の定義は、同じ行動タイプであり、2 つの隣接する行動間の時間差が 30 分を超えないことです。

3) 定義を満たす限り、異なる行動間隔を取る必要があります。

ユーザーID Wi-Fi 状態 開始時刻 time_end 述べる
101 cmcc-スターバックス スキャン 2024-01-01 10:01:00 2024-01-01 10:03:00 ユーザーがWiFiをスキャンした
101 cmcc-スターバックス コン 2024-01-01 10:04:00 2024-01-01 10:06:00 ユーザーが WiFi に接続している
101 cmcc-スターバックス コン 2024-01-01 11:01:00 2024-01-01 11:02:00 最後の接続から 30 分以上経過している場合は、新しい接続動作とみなされます。


上記の問題はもう少し複雑で、動的連続アクティビティ統計 で導入された 最長アクティブ ストリークの変形とみなすことができます  。これは、動作シーケンス内の連続性のしきい値とコンテキスト情報を組み合わせて、最長の部分間隔を分割する 問題として説明できます 

SQLの実装

コア ロジック: ユーザーと WIFI をグループ化し、連続性のしきい値と動作シーケンスのコンテキスト情報を組み合わせて動作間隔を分割します。

詳細な手順:

1) データをユーザーおよび WIFI ごとにグループ化し、グループ ウィンドウ内でデータを時系列順に並べ替える; 2) グループ ウィンドウ内の 2 つの隣接するレコードを順番に走査する 2 つのレコード間の時間差が 30 分を超える場合、または時間が2 つのレコード間の差異 動作状態 (スキャン状態、接続状態) が変化すると、動作間隔が臨界点で分割されます。すべてのレコードを走査するまで。

3) 最終出力結果: ユーザー、WIFI、動作ステータス (スキャン状態、接続状態)、動作開始時刻、動作終了時刻。

-- 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 Wi-Fi 状態 始まる時間 終了時間
101 cmcc-スターバックス スキャン 2024-01-01 10:01:00 2024-01-01 10:03:00
101 cmcc-スターバックス コン 2024-01-01 10:04:00 2024-01-01 10:06:00
101 cmcc-スターバックス コン 2024-01-01 11:01:00 2024-01-01 11:03:00


この場合の連続性判別条件は、日付差、時間差、列挙型、距離差などを連続性判別条件とするデータシナリオなど、より多くのシナリオに拡張することができる。

結論

柔軟で拡散的なデータ処理の考え方を通じて、基本的な構文を使用して複雑なデータ シナリオを解決できます。これが この記事全体の考え方です。この記事では、シーケンスの生成、区間変換、並べ替えと組み合わせ、連続判別などの一般的なシナリオに対する比較的一般的なソリューションを提供し、例を示して実際のアプリケーションを説明します。

この記事は、ユニークなアプローチを模索し、柔軟なデータ処理の考え方を強調するもので、読者の気持ちが明るくなり、読者の役に立つことを願っています。同時に、結局のところ、個人の能力には限界があり、アイデアが必ずしも最適であるとは限らず、間違いが発生することもあります。

著者 | リゴ

クリックして今すぐクラウド製品を無料で試し、クラウドでの実践的な取り組みを始めましょう!

元のリンク

この記事は Alibaba Cloud のオリジナル コンテンツであり、許可なく複製することはできません。

阿里云严重故障,全线产品受影响(已恢复) 汤不热 (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}}

おすすめ

転載: my.oschina.net/yunqi/blog/10139796