如何提取SQL语句中有查询意义的数字ID字符串-part2


=Start=

缘由:

去年的时候记录过一篇《如何提取SQL语句中有查询意义的数字ID字符串?》的文章,近期在写一个类似功能的ETL的时候又想到一些问题,在此记录一下,作为上一次思考的补充和扩展。

正文:

参考解答:

先说目标,提取出SQL语句中有查询意义的数字ID字符串主要是为了做异常分析——这个人查这个数字ID的次数、操作时间、时间跨度是否正常?是否符合正常工作需求?这个数字ID是否为一个有效的用户ID或是其他公司内部在用的数字ID?

明确目标,要聚焦,要精确——多不如精。


一些正常和不正常的SQL代码片段样例:

id=123
id='123'
id="123"

id = 123
id = '123'
id = "123"

id in (123)
id in ('123')
id in ("123")
id in ('123','456')

id in (select item_id from v2_base)
id in ( 'qw_24_1_76891', 'qw_24_1_74891')

Hive SQL中的一些要注意的问题:字符串字面量包含多种引号,不符合要求

处理建议:
检查 SQL 语句中的字符串字面量,确保只使用一种引号(单引号或双引号)。例如,将 'It's a test' 改为 "It's a test"'It\'s a test'


下面直接说当前的解法吧,简单直接点。

1. 先做统一的格式化操作——移除注释,将连续的多个空白符号替换成一个空格符号,使用的是 regexp_replace 函数;
2. 然后用正则表达式把一些大概符合条件的SQL语句筛选出来;
3. 再用正则表达式对目标内容进行提取,这个提取是分别进行的,字符串等于的就直接拿数字ID,使用in语法的就先把括号里的内容都提取出来,再在外层进行切分并逐个提取;
4. 对提取出来的数字ID尝试进行left join匹配扩充相关字段方便后面分析;
5. 最后把那些针对特定数字ID有长期、大量查询的行为拿出来深入分析调查以确认是否正常。
-- SQL语句格式化处理+初步筛选
and lower(regexp_replace(regexp_replace(query_sql,'--.+?\\n{1,}',''),'\\s{1,}',' ')) rlike 'id\\s?(=|in)\\s?[^a-z ]'
WITH t1 AS (
    SELECT 'select * from tbl where userid = 123' AS sql_text UNION ALL
    SELECT 'select * from tbl where userid = ''456''' UNION ALL
    SELECT 'select * from tbl where userid in (123, 456, ''789'')' UNION ALL
    SELECT 'select * from tbl where userid in (211, 985, "110")' UNION ALL
    SELECT 'select * from tbl where user_id = 111'
)
SELECT 
    sql_text
    ,if(sql_text rlike 'id\\s?=', regexp_extract(sql_text,'id\\s?=\\s?[\']?(\\d{3,})[\']?',1), NULL) as id_equal
    ,if(sql_text rlike 'id\\s?=', regexp_extract(sql_text,"id\\s?=\\s?[']?(\\d{3,})[']?",1), NULL) as id_equal2
    ,if(sql_text rlike 'id\\s?in',regexp_extract(sql_text,'id\\s?in\\s?\\((.+?)\\)',1), NULL) as id_in
    ,split(regexp_replace(if(sql_text rlike 'id\\s?in',regexp_extract(sql_text,'id\\s?in\\s?\\((.+?)\\)',1), NULL),'[\' ]+',''),',|"') as id_list
FROM t1

如果想使用原生 Hive SQL 实现一些较为复杂的功能,最好的办法就是分成多个步骤进行处理(大概率涉及到一个或多个临时表),更为稳定可靠。

-- 先建立一个临时表缓存初步过滤和格式化处理之后的内容
with query_sql_tmp as (
select
    user_name,
    query_time,
    query_id,
    -- 格式化处理
    lower(regexp_replace(regexp_replace(query_sql,'--.+?\\n{1,}',''),'\\s{1,}',' ')) as query_sql
    -- 其他字段在此处先省略
from
data_platform.sql_query_detail
where
date = '${date}'
-- 初步过滤
and lower(regexp_replace(regexp_replace(query_sql,'--.+?\\n{1,}',''),'\\s{1,}',' ')) rlike 'id\\s?(=|in)\\s?[^a-z ]'
)


-- 单个id查询
select
    user_name,
    query_time,
    query_id,
    query_sql,
    1 as qid_cnt,
    regexp_replace(regexp_extract(query_sql, '(user_id|userid|uid)\\s+=\\s+[\']*(\\d+)', 2), '\'', '') as qid
from
query_sql_tmp
where
query_sql rlike '(user_id|userid|uid)\\s+='

union all

-- 多个id查询
select
    user_name,
    query_time,
    query_id,
    query_sql,
    size(in_list) as qid_cnt,
    qid
from (
        select
            user_name,
            query_time,
            query_id,
            query_sql,
            split(regexp_replace(regexp_extract(query_sql, '(user_id|userid|uid)\\s+in\\s+\\(([^\\)]+)\\)', 2), '\'| |"', ''),',') as in_list
        from
        query_sql_tmp
        where
        query_sql rlike '(user_id|userid|uid)\\s+in\\s+\\('
) t1
lateral view explode(in_list) tmp as qid
参考链接:

如何提取SQL语句中有查询意义的数字ID字符串?
https://ixyzero.com/blog/archives/5826.html

=END=


《“如何提取SQL语句中有查询意义的数字ID字符串-part2”》 有 1 条评论

  1. question
    Hive SQL语句中的字符串字面量只能包含一种引号吗?如果我当前有一个需求是将字符串字段input_str中所有的单引号、双引号都替换为空的话,应该如何处理呢?

    answer
    在Hive SQL中,字符串字面量可以使用单引号(’)或双引号(”)来表示,但是这取决于具体的配置和环境设置。通常情况下,单引号是标准的字符串定界符,而双引号可能被用来表示标识符(如列名、表名等),不过有些SQL方言允许使用双引号作为字符串字面量的定界符。

    针对你的需求——将字符串字段`input_str`中所有的单引号(’)和双引号(”)都替换为空,你可以使用Hive的内置函数`REPLACE`来完成这项工作。由于你可能需要连续进行两次替换操作(一次针对单引号,另一次针对双引号),你可以嵌套使用`REPLACE`函数或者使用正则表达式相关的函数来实现这一点。

    【推荐方式】这里是一个使用嵌套`REPLACE`的例子:

    “`sql
    SELECT REPLACE(REPLACE(input_str, “‘”, “”), ‘”‘, “”) AS modified_input_str
    FROM your_table_name;
    “`

    这个查询首先会将`input_str`中的所有单引号替换为空,然后在外层的`REPLACE`函数中将所有双引号替换为空。最终结果是`input_str`字段中的所有单引号和双引号都被移除了。

    另外,如果你更倾向于使用正则表达式的方法,可以考虑使用`REGEXP_REPLACE`函数,这样只需执行一次替换即可:

    “`sql
    SELECT REGEXP_REPLACE(input_str, “[‘\”]”, “”) AS modified_input_str
    FROM your_table_name;
    “`

    在这个例子中,`REGEXP_REPLACE`函数利用了一个简单的正则表达式`[‘”]`来匹配单引号和双引号,并将它们替换为空字符串。这种方法更加简洁,尤其是在需要处理多种字符替换时显得尤为方便。

    根据近期我的大量测试,得出一个适合我的结论,在Hive SQL中使用正则表达式时,尤其是 regexp_replace/regexp_extract 函数,不建议在一个正则表达式中同时包含单引号和双引号,虽然从正则表达式的角度来看正则是没问题的,但是平台实际执行过程中可能有各种各样的问题,平台给出的建议也是——检查 SQL 语句中的字符串字面量,确保只使用一种引号(单引号或双引号)。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注