SQL字段解析

一、背景

项目中存在着一个需求,创建数据表时需要根据SQL进行创建,这里定义的SQL为查询语句。所以需要将查询语句中的结果字段摘取出来作为建表所使用到的字段(简单/复杂查询都涉及)
在这里插入图片描述

二、相关SQL解析库的实践

一开始在网上找了java中sql解析的库,重点研究了下jsqlparser,效果如下

<dependency>
    <groupId>com.github.jsqlparser</groupId>
    <artifactId>jsqlparser</artifactId>
    <version>4.9</version>
</dependency>
String dataSql = "SELECT \n" +
    "    u.biz_line AS biz_group, \n" +
    "    u.realname AS user_name, \n" +
    "    DATE_FORMAT(s.reviewedPlanDate, '%Y-%m') AS month, \n" +
    "    COUNT(s.id) AS story_count, \n" +
    "    SUM(estimate) AS PD_estimated_hours,\n" +
    "    SUM(s.prdEstimate) AS prd_estimated_hours, \n" +
    "    SUM(s.prdConsumed) AS prd_actual_hours\n" +
    "FROM \n" +
    "    zt_story s\n" +
    "JOIN \n" +
    "    zt_user u ON s.productOwner = u.account\n" +
    "WHERE \n" +
    "    DATE_FORMAT(s.reviewedPlanDate, '%Y-%m') IN ('2024-05', '2024-06', '2024-07')\n" +
    "GROUP BY \n" +
    "    u.biz_line, \n" +
    "    u.realname, \n" +
    "    DATE_FORMAT(s.reviewedPlanDate, '%Y-%m')\n" +
    "ORDER BY \n" +
    "    u.biz_line, \n" +
    "    u.realname, \n" +
    "    month;";

Statement statement = CCJSqlParserUtil.parse(dataSql);
if(statement instanceof Select){
    Select selectStatement = (PlainSelect) statement;
    System.out.println("==> JsqlParser SQL: "+ selectStatement.toString());
    PlainSelect plainSelect = selectStatement.getPlainSelect();
    System.out.println("==> FromItem: "+ plainSelect.getFromItem());
    List<SelectItem<?>> selectItems = plainSelect.getSelectItems();
    List<Alias> collect = selectItems.stream().map(SelectItem::getAlias).collect(Collectors.toList());
    System.out.println("==> SelectItem: "+ selectItems);
    System.out.println("==> select items: "+collect);
    List<String> columns = new ArrayList<>();
    for (Alias alias:collect){
        String name = alias.getName();
        columns.add(name);
    }
    System.out.println("==> columns: "+columns);
    System.out.println("==> Where: "+plainSelect.getWhere());
}

结果如下
在这里插入图片描述
上图中看到,针对于这种简单的select查询语句来说,字段确实有效解析出了

WITH PlannedTasks AS (
    SELECT
        task_res_user_name,
        DATE_FORMAT(finish_plan_time, '%Y-%m') AS month,
        COUNT(*) AS planned_count
    FROM
        power_dpm_associated_task
    WHERE
        finish_plan_time BETWEEN '2024-01-01' AND '2024-07-31'
    GROUP BY
        task_res_user_name,
        DATE_FORMAT(finish_plan_time, '%Y-%m')
),
CompletedTasks AS (
    SELECT
        t.task_res_user_name,
        DATE_FORMAT(o.create_time, '%Y-%m') AS month,
        COUNT(*) AS completed_count
    FROM
        power_dpm_associated_task t
    JOIN
        power_dpm_approver_opinion o ON t.id = o.task_id
    WHERE
        o.check_result = 'PASS'
        AND o.create_time BETWEEN '2024-01-01' AND '2024-07-31'
        AND o.create_time = (
            SELECT MAX(o2.create_time)
            FROM power_dpm_approver_opinion o2
            WHERE o2.task_id = o.task_id
            AND o2.check_result = 'PASS'
        )
    GROUP BY
        t.task_res_user_name,
        DATE_FORMAT(o.create_time, '%Y-%m')
)
SELECT
    COALESCE(p.task_res_user_name, c.task_res_user_name) AS task_res_user_name,
    COALESCE(p.month, c.month) AS month,
    COALESCE(p.planned_count, 0) AS planned_count,
    COALESCE(c.completed_count, 0) AS completed_count
FROM
    PlannedTasks p
LEFT JOIN
    CompletedTasks c ON p.task_res_user_name = c.task_res_user_name AND p.month = c.month
UNION
SELECT
    COALESCE(p.task_res_user_name, c.task_res_user_name) AS task_res_user_name,
    COALESCE(p.month, c.month) AS month,
    COALESCE(p.planned_count, 0) AS planned_count,
    COALESCE(c.completed_count, 0) AS completed_count
FROM
    PlannedTasks p
RIGHT JOIN
    CompletedTasks c ON p.task_res_user_name = c.task_res_user_name AND p.month = c.month
ORDER BY
    task_res_user_name,
    month;

但是下面的这种with关联的复杂sql查询语句,使用上述方法就得不到想要的结果了。因此,经过了一系列尝试后,选择了jdbi库中的方法实现。

三、JDBI实现SQL解析(重点)

pom依赖及Java代码实现如下

<dependency>
    <groupId>org.jdbi</groupId>
    <artifactId>jdbi3-sqlobject</artifactId>
    <version>3.45.0</version>
</dependency>
<!-- gradle依赖在此 implementation 'org.jdbi:jdbi3-sqlobject:3.45.0' ->
public List<String> getQueryColumns(Jdbi jdbi, String sql) {
    log.info("执行查询的sql语句: {}", sql);
    return jdbi.withHandle((Handle h) -> {
        // 执行查询并获取列名
        return h.createQuery(sql).map(new ColumnNameMapper()).first();
    });
}

/**
  * 内部类
  */
private static class ColumnNameMapper implements RowMapper<List<String>> {
    @Override
    public List<String> map(ResultSet rs, StatementContext ctx) throws SQLException {
        List<String> columnNames = new ArrayList<>();
        ResultSetMetaData metaData = rs.getMetaData();
        int columnCount = metaData.getColumnCount();
        for (int i = 1; i <= columnCount; i++) {
            columnNames.add(metaData.getColumnName(i));
        }
        return columnNames;
    }
}
效果展示

1.select *
在这里插入图片描述
2.select具体字段
在这里插入图片描述
3.with的复杂查询
在这里插入图片描述
效果见上图,项目中所用到的SQL查询场景这三种情况基本能覆盖,字段也都正常解析出,所以需求能满足。

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值