将包含WITH子句和UNION的SQL查询转换为QueryDsl。

huangapple go评论72阅读模式
英文:

Convert SQL query that contains WITH clause and union in QueryDsl

问题

以下是翻译好的代码部分:

private final QStudent table = QStudent.student;
private final QAggregationValues aggregationValues = QAggregationValues.aggregationValues;

public List<AggregationResultRow> getAggregations(AggregationRequest aggregationRequest) {
    SubQueryExpression<AggregationValues> filteredValues = new BlazeJPAQuery<AggregationValues>(entityManager, criteriaBuilderFactory)
        .select(Projections.constructor(AggregationValues.class, 
            this.table.age,
            this.table.gender,
            this.table.mainCity))
        .from(this.table)
        .where(this.getConditions(aggregationRequest));

    BlazeJPAQuery<AggregationResultRow> ageSelect = new BlazeJPAQuery<AggregationResultRow>(entityManager, criteriaBuilderFactory)
        .with(this.aggregationValues, filteredValues)
        .select(Projections.constructor(AggregationResultRow.class, this.aggregationValues.age.as("value"),
            this.aggregationValues.count().as("row_count"),
            Expressions.asString("age").as("column_name")))
        .from(this.aggregationValues)
        .groupBy(this.aggregationValues.age);

    BlazeJPAQuery<AggregationResultRow> genderSelect = new BlazeJPAQuery<AggregationResultRow>(entityManager, criteriaBuilderFactory)
        .with(this.aggregationValues, filteredValues)
        .select(Projections.constructor(AggregationResultRow.class, this.aggregationValues.gender.as("value"),
            this.aggregationValues.count().as("row_count"),
            Expressions.asString("gender").as("column_name")))
        .from(this.aggregationValues)
        .groupBy(this.aggregationValues.gender);

    BlazeJPAQuery<AggregationResultRow> mainCitySelect = new BlazeJPAQuery<AggregationResultRow>(entityManager, criteriaBuilderFactory)
        .with(this.aggregationValues, filteredValues)
        .select(Projections.constructor(AggregationResultRow.class, this.aggregationValues.mainCity.as("value"),
            this.aggregationValues.count().as("row_count"),
            Expressions.asString("main_city").as("column_name")))
        .from(this.aggregationValues)
        .groupBy(this.aggregationValues.mainCity);

    return ageSelect.unionAll(genderSelect, mainCitySelect).fetch();
}

请注意,这只是代码的翻译部分,不包括其他内容。如果您有任何其他问题,请随时提出。

英文:

I'm using Oracle DB and has Students table:

Students Table:

| COLUMN      | TYPE          |
| ----------- | ------------- |
|  ID         | VARCHAR2(200) |
|  NAME       | VARCHAR2(200) |
|  AGE        | NUMBER(5)     |
|  MAIN_CITY  | VARCHAR2(200) |
|  GENDER     | VARCHAR2(50)  |
|  SCORE      | FLOAT(5)      |

`

I'm trying to convert this SQL query to query-dsl code:

SELECT /*+ parallel(2)*/ *
FROM (WITH filtered_values(age, gender, main_city) AS
(
SELECT /*+ MATERIALIZE */ age, gender, main_city 
FROM Students
-- GET CONDITIONS FROM USER AND FILTER THE QUERY USING WHERE
)
SELECT * 
FROM (
SELECT age AS value, COUNT(*) AS row_count, &#39;age&#39; AS column_name
FROM filtered_values
GROUP BY age
ORDER BY row_count)
WHERE rownum &lt;= 5
UNION ALL (
SELECT * 
FROM (
SELECT gender AS value, COUNT(*) AS row_count, &#39;gender&#39; AS column_name
FROM filtered_values
GROUP BY gender
ORDER BY row_count)
WHERE rownum &lt;= 5)
UNION ALL (
SELECT * 
FROM (
SELECT main_city AS value, COUNT(*) AS row_count, &#39;main_city&#39; AS column_name
FROM filtered_values
GROUP BY main_city
ORDER BY row_count)
WHERE rownum &lt;= 5)
)

The filters that available from client is flexible and change the WITH query, for example:

where score greater than 20:
> WITH filtered_values(age, gender, main_city) AS
> (
> SELECT /*+ MATERIALIZE */ age, gender, main_city
> FROM Students
> WHERE score > 20
> )

where city is LA or NYC, and score is between 50-60:
> WITH filtered_values(age, gender, main_city) AS
> (
> SELECT /*+ MATERIALIZE */ age, gender, main_city
> FROM Students
> WHERE main_city IN ('LA', 'NYC') AND score >= 50 AND score <= 60
> )

I'm using QueryDsl version 5.0.0.

I've created Java classes:

Student:

@Data
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Table(schema = &quot;PROD&quot;, name = &quot;Students&quot;)
public class Student implements Serializable {
@Id
@Column(name = &quot;ID&quot;)
private String id;
@Column(name = &quot;NAME&quot;)
private String name;
@Column(name = &quot;AGE&quot;)
private int age;
@Column(name = &quot;MAIN_CITY&quot;)
private String mainCity;
@Column(name = &quot;SCORE&quot;)
private float score; 
}

AggregationValues:

@Data
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Table(schema = &quot;PROD&quot;, name = &quot;NOT_REAL_TABLE&quot;)
public class AggregationValues {
private int age;
private String gender;
private String mainCity;
}

AggregationResultRow:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class AggregationResultRow {
private String value;
private long rowCount;
private String columnName;
}

My code is:
> Note that this.getConditions(aggregationsRequest) return BooleanBuilder with all the filters from the client.

private final QStudent table = QStudent.student;
private final QAggregationValues aggregationValues = QAggregationValues.aggregationValues;
public List&lt;AggregationResultRow&gt; getAggergations(AggregationRequest aggregationRequest) {
SubQueryExpression&lt;AggregationValues&gt; filteredValues = new BlazeJPAQuery&lt;AggregationValues&gt;(entityManager, criteriaBuilderFactory)
.select(Projections.constructor(AggregationValues.class, 
this.table.age,
this.table.gender,
this.table.mainCity))
.from(this.table)
.where(this.getConditions(aggregationRequest));
BlazeJPAQuery&lt;AggregationResultRow&gt; ageSelect = new BlazeJPAQuery&lt;AggregationResultRow&gt;(entityManager, criteriaBuilderFactory)
.with(this.aggregationValues, filteredValues)
.select(Projections.constructor(AggregationResultRow.class, this.aggregationValues.age.as(&quot;value&quot;),
this.aggregationValues.count().as(&quot;row_count&quot;),
Expressions.asString(&quot;age&quot;).as(&quot;column_name&quot;)))
.from(this.aggregationValues)
.groupBy(this.aggregationValues.age);
BlazeJPAQuery&lt;AggregationResultRow&gt; genderSelect = new BlazeJPAQuery&lt;AggregationResultRow&gt;(entityManager, criteriaBuilderFactory)
.with(this.aggregationValues, filteredValues)
.select(Projections.constructor(AggregationResultRow.class, this.aggregationValues.gender.as(&quot;value&quot;),
this.aggregationValues.count().as(&quot;row_count&quot;),
Expressions.asString(&quot;gender&quot;).as(&quot;column_name&quot;)))
.from(this.aggregationValues)
.groupBy(this.aggregationValues.gender);
BlazeJPAQuery&lt;AggregationResultRow&gt; mainCitySelect = new BlazeJPAQuery&lt;AggregationResultRow&gt;(entityManager, criteriaBuilderFactory)
.with(this.aggregationValues, filteredValues)
.select(Projections.constructor(AggregationResultRow.class, this.aggregationValues.mainCity.as(&quot;value&quot;),
this.aggregationValues.count().as(&quot;row_count&quot;),
Expressions.asString(&quot;main_city&quot;).as(&quot;column_name&quot;)))
.from(this.aggregationValues)
.groupBy(this.aggregationValues.mainCity);
return ageSelect.unionAll(genderSelect, mainCitySelect).fetch();
}

But I got Exception - java.lang.IllegalArgumentException: Don&#39;t mix union and from

I've tried more code ideas but nothing works and this one looks the closest to my query.

Any ideas?

Thank you very much, I'll appreciate it a lot!

答案1

得分: 0

这是您提供的代码的中文翻译部分:

所以我找到了一个折中的解决方案!
我所做的是在类中添加了@Autowired参数:

@Autowired
private JdbcTemplate jdbcTemplate;

这是该函数:

public List<AggregationResultRow> getAggregations(AggregationRequest aggregationRequest) throw Exception {
Connection connection = this.jdbcTemplate.getDataSource().getConnection();
OracleQuery<AggregationResultRow> query = new OracleQuery<AggregationResultRow>(connection)
.with(this.aggregationValues, new OracleQuery<AggregationValues>(connection)
.select(Projections.constructor(AggregationValues.class, this.table.age.as(this.aggregationValues.age), this.table.gender.as(this.aggregationValues.gender),
this.table.mainCity.as(this.aggregationValues.mainCity)))
.from(this.table)
where(this.getConditions(aggregationRequest)));

query.unionAll(new OracleQuery<AggregationResultRow>(connection)
.select(Projections.constructor(AggregationResultRow.class, this.aggregationValues.age.as("value"),
this.aggregationValues.age.count().as("row_count"),
Expressions.asString("age").as("column_name")))
.from(this.aggregationValues)
.groupBy(this.aggregationValues.age),
new OracleQuery<AggregationResultRow>(connection)
.select(Projections.constructor(AggregationResultRow.class, this.aggregationValues.gender.as("value"),
this.aggregationValues.gender.count().as("row_count"),
Expressions.asString("gender").as("column_name")))
.from(this.aggregationValues)
.groupBy(this.aggregationValues.gender),
new OracleQuery<AggregationResultRow>(connection)
.select(Projections.constructor(AggregationResultRow.class, this.aggregationValues.mainCity.as("value"),
this.aggregationValues.mainCity.count().as("row_count"),
Expressions.asString("mainCity").as("column_name")))
.from(this.aggregationValues)
.groupBy(this.aggregationValues.mainCity));

return query.fetch();
}

现在,我只需要在WITH子句的select中添加Materialize,并并行执行所有函数。有任何想法吗?

英文:

So I found a half way solution!
What I’ve done is to add autowired parameter in class:

@Autowired
private JdbcTemplate jdbcTemplate;

And this is the function:

public List&lt;AggregationResultRow&gt; getAggregations(AggregationRequest aggregationRequest) throw Exception {
Connection connection = this.jdbcTemplate.getDataSource().getConnection();
OracleQuery&lt;AggregationResultRow&gt; query = new OracleQuery&lt;AggregationResultRow&gt;(connection)
.with(this.aggregationValues, new OracleQuery&lt;AggregationValues&gt;(connection)
.select(Projections.constructor(AggregationValues.class, this.table.age.as(this.aggregationValues.age), this.table.gender.as(this.aggregationValues.gender),
this.table.mainCity.as(this.aggregationValues.mainCity)))
.from(this.table)
where(this.getConditions(aggregationRequest)));
query.unionAll(new OracleQuery&lt;AggregationResultRow&gt;(connection)
.select(Projections.constructor(AggregationResultRow.class, this.aggregationValues.age.as(“value”),
this.aggregationValues.age.count().as(“row_count”),
Expressions.asString(“age”).as(“column_name”)))
.from(this.aggregationValues)
.groupBy(this.aggregationValues.age),
new OracleQuery&lt;AggregationResultRow&gt;(connection)
.select(Projections.constructor(AggregationResultRow.class, this.aggregationValues.gender.as(“value”),
this.aggregationValues.gender.count().as(“row_count”),
Expressions.asString(“gender”).as(“column_name”)))
.from(this.aggregationValues)
.groupBy(this.aggregationValues.gender),
new OracleQuery&lt;AggregationResultRow&gt;(connection)
.select(Projections.constructor(AggregationResultRow.class, this.aggregationValues.mainCity.as(“value”),
this.aggregationValues.mainCity.count().as(“row_count”),
Expressions.asString(“mainCity”).as(“column_name”)))
.from(this.aggregationValues)
.groupBy(this.aggregationValues.mainCity));
return query.fetch();
}

Now I only need to add Materialize to the select in the WITH clause and parallel to all the function. Any ideas?

huangapple
  • 本文由 发表于 2023年2月27日 17:38:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/75578795.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定