2

我正在尝试使用 JOOQ 来查询 Hive。Hive SQL 方言非常接近 MySQL 方言。现在我遇到了这些问题:

  • Hive 支持 LIMIT N,它不支持 LIMIT N OFFSET K。虚拟解决方案 - 覆盖select.limit(limit);

在 JOOQ 中解决此类问题的最佳实践是什么?

4

2 回答 2

3

不幸的是,扩展 jOOQ 以彻底支持新的 SQL 方言并不是很简单。jOOQ 的 API 随着时间的推移变得越来越广泛,支持一组很棒的标准和特定于供应商的 SQL 语法变体。虽然 Apache Hive 方言可能看起来与 MySQL 相似,但可能存在许多需要在 jOOQ 内部实现的细微差别。LIMIT .. OFFSET 子句的不同实现只是一个问题。也就是说,将 jOOQ 与“未知”或“不受支持”的方言一起使用通常不是一个好主意。

解决方案:短期内

在短期内,您可能需要修补 jOOQ 呈现的 SQL。最好的技术是使用此处记录的 ExecuteListener:

收到“renderEnd()”事件后,您将能够访问呈现的 SQL 并使用正则表达式或您可能喜欢的任何技术对其进行修改。

解决方案:从长远来看

从长远来看,如果/当#2337实施时可能会有更好的解决方案(但我们可能不会实施)

于 2013-03-15T19:08:41.790 回答
1

这是最肮脏的解决方案 :) 不幸的是,JOOQ 用户组没有回答 :(

public class CountRatingQueryBuilder {

    private static final String SCORING_TABLE_NAME = "web_resource_rating";

    private final Connection connection;
    private final ScoringMetadata scoringMetadata;

    private final SelectSelectStep select;
    private final Factory create;

    public CountRatingQueryBuilder(Connection connection, ScoringMetadata scoringMetadata){
        this.connection = connection;
        this.scoringMetadata = scoringMetadata;

        create = new Factory(this.connection, SQLDialect.MYSQL);
        select = create.select();

        withSelectFieldsClause();
    }

    public CountRatingQueryBuilder withLimit(int limit){
        select.limit(limit);
        return this;
    }

    public CountRatingQueryBuilder withRegionId(Integer regionId){
        select.where(REGION_ID.field().equal(regionId));
        return this;
    }

    public CountRatingQueryBuilder withResourceTypeId(int resourceTypeId){
        select.where(RESOURCE_TYPE_ID.field().equal(resourceTypeId));
        return this;
    }

    public CountRatingQueryBuilder withRequestTimeBetween(long beginTimestamp, long endTimestamp){
        select.where(REQUEST_TIME.field().between(beginTimestamp, endTimestamp));
        return this;
    }

    public CountRatingQueryBuilder withResourceId(int resourceId){
        select.where(RESOURCE_ID.field().equal(resourceId));
        return this;
    }



    protected void withGroupByClause(){
        select.groupBy(REGION_ID.field());
        select.groupBy(RESOURCE_TYPE_ID.field());
        select.groupBy(RESOURCE_ID.field());
        select.groupBy(CONTENT_ID.field());
    }

    protected void withSelectFieldsClause(){
        select.select(REGION_ID.field());
        select.select(RESOURCE_TYPE_ID.field());
        select.select(CONTENT_ID.field());
        select.select(RESOURCE_ID.field());
        select.select(Factory.count(HIT_COUNT.field()).as(SUM_HIT_COUNT.fieldName()));
    }

    protected void withFromClause(){
        select.from(SCORING_TABLE_NAME);
    }

    protected void withOrderByClause(){
        select.orderBy(SUM_HIT_COUNT.field().desc());
    }

    public String build(){
        withGroupByClause();
        withOrderByClause();
        withFromClause();
        return select.getSQL().replace("offset ?","");//dirty hack for MySQL dialect. TODO: we can try to implement our own SQL dialect for Hive :)

    }

    public List<ResultRow> buildAndFetch(){
        String sqlWithPlaceholders = build();

        List<ResultRow> scoringResults = new ArrayList<ResultRow>(100);
        List<Record> recordResults = create.fetch(sqlWithPlaceholders, ArrayUtils.subarray(select.getBindValues().toArray(new Object[select.getBindValues().size()]),0, select.getBindValues().size()-1));//select.fetch();
        for(Record record : recordResults){
            ResultRowBuilder resultRowBuilder = ResultRowBuilder.create();

            resultRowBuilder.withContentType(scoringMetadata.getResourceType(record.getValue(RESOURCE_TYPE_ID.fieldName(), Integer.class)));
            resultRowBuilder.withHitCount(record.getValue(SUM_HIT_COUNT.fieldName(), Long.class));
            resultRowBuilder.withUrl(record.getValue(CONTENT_ID.fieldName(), String.class));
            scoringResults.add(resultRowBuilder.build());
        }
        return scoringResults;
    }

}
于 2013-03-14T09:44:48.143 回答