[improvement][headless] In the translator, adopt Calcite to support multiple WITH structures and optimize the Calcite code. (#1747)

This commit is contained in:
lexluo09
2024-09-30 18:53:27 +08:00
committed by GitHub
parent de2385cd2c
commit 8693e7f8a7
39 changed files with 234 additions and 126 deletions

View File

@@ -0,0 +1,177 @@
package com.tencent.supersonic.common.calcite;
import com.tencent.supersonic.common.pojo.enums.EngineType;
import org.apache.calcite.avatica.util.Casing;
import org.apache.calcite.avatica.util.Quoting;
import org.apache.calcite.config.CalciteConnectionConfig;
import org.apache.calcite.config.CalciteConnectionConfigImpl;
import org.apache.calcite.config.CalciteConnectionProperty;
import org.apache.calcite.config.Lex;
import org.apache.calcite.jdbc.CalciteSchema;
import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.plan.RelOptPlanner;
import org.apache.calcite.prepare.CalciteCatalogReader;
import org.apache.calcite.prepare.Prepare;
import org.apache.calcite.prepare.Prepare.CatalogReader;
import org.apache.calcite.rel.hint.HintStrategyTable;
import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.rel.type.RelDataTypeSystem;
import org.apache.calcite.rex.RexBuilder;
import org.apache.calcite.sql.SqlOperatorTable;
import org.apache.calcite.sql.SqlWriterConfig;
import org.apache.calcite.sql.advise.SqlAdvisor;
import org.apache.calcite.sql.advise.SqlAdvisorValidator;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import org.apache.calcite.sql.parser.SqlParser;
import org.apache.calcite.sql.parser.impl.SqlParserImpl;
import org.apache.calcite.sql.pretty.SqlPrettyWriter;
import org.apache.calcite.sql.util.ChainedSqlOperatorTable;
import org.apache.calcite.sql.validate.SqlValidator;
import org.apache.calcite.sql.validate.SqlValidatorScope;
import org.apache.calcite.sql.validate.SqlValidatorUtil;
import org.apache.calcite.sql.validate.SqlValidatorWithHints;
import org.apache.calcite.sql2rel.SqlToRelConverter;
import org.apache.calcite.tools.FrameworkConfig;
import org.apache.calcite.tools.Frameworks;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
/** global configuration of the calcite */
public class Configuration {
public static Properties configProperties = new Properties();
public static RelDataTypeFactory typeFactory =
new SemanticSqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
public static SqlOperatorTable operatorTable = SqlStdOperatorTable.instance();
public static CalciteConnectionConfig config =
new CalciteConnectionConfigImpl(configProperties);
public static SqlValidator.Config getValidatorConfig(EngineType engineType) {
SemanticSqlDialect sqlDialect = SqlDialectFactory.getSqlDialect(engineType);
return SqlValidator.Config.DEFAULT
.withConformance(sqlDialect.getConformance())
.withDefaultNullCollation(config.defaultNullCollation())
.withLenientOperatorLookup(true);
}
static {
configProperties.put(
CalciteConnectionProperty.CASE_SENSITIVE.camelName(), Boolean.TRUE.toString());
configProperties.put(
CalciteConnectionProperty.UNQUOTED_CASING.camelName(), Casing.UNCHANGED.toString());
configProperties.put(
CalciteConnectionProperty.QUOTED_CASING.camelName(), Casing.TO_LOWER.toString());
}
public static SqlParser.Config getParserConfig(EngineType engineType) {
CalciteConnectionConfig config = new CalciteConnectionConfigImpl(configProperties);
SemanticSqlDialect sqlDialect = SqlDialectFactory.getSqlDialect(engineType);
SqlParser.ConfigBuilder parserConfig = SqlParser.configBuilder();
parserConfig.setCaseSensitive(config.caseSensitive());
parserConfig.setUnquotedCasing(config.unquotedCasing());
parserConfig.setQuotedCasing(config.quotedCasing());
parserConfig.setConformance(config.conformance());
parserConfig.setLex(Lex.BIG_QUERY);
parserConfig
.setParserFactory(SqlParserImpl.FACTORY)
.setCaseSensitive(false)
.setIdentifierMaxLength(Integer.MAX_VALUE)
.setQuoting(Quoting.BACK_TICK)
.setQuoting(Quoting.SINGLE_QUOTE)
.setQuotedCasing(Casing.TO_UPPER)
.setUnquotedCasing(Casing.TO_UPPER)
.setConformance(sqlDialect.getConformance())
.setLex(Lex.BIG_QUERY);
parserConfig = parserConfig.setQuotedCasing(Casing.UNCHANGED);
parserConfig = parserConfig.setUnquotedCasing(Casing.UNCHANGED);
return parserConfig.build();
}
public static SqlValidator getSqlValidator(CalciteSchema rootSchema, EngineType engineType) {
List<SqlOperatorTable> tables = new ArrayList<>();
tables.add(SqlStdOperatorTable.instance());
SqlOperatorTable operatorTable = new ChainedSqlOperatorTable(tables);
// operatorTable.
Prepare.CatalogReader catalogReader =
new CalciteCatalogReader(
rootSchema,
Collections.singletonList(rootSchema.getName()),
typeFactory,
config);
return SqlValidatorUtil.newValidator(
operatorTable,
catalogReader,
typeFactory,
Configuration.getValidatorConfig(engineType));
}
public static SqlValidatorWithHints getSqlValidatorWithHints(
CalciteSchema rootSchema, EngineType engineTyp) {
return new SqlAdvisorValidator(
SqlStdOperatorTable.instance(),
new CalciteCatalogReader(
rootSchema,
Collections.singletonList(rootSchema.getName()),
typeFactory,
config),
typeFactory,
SqlValidator.Config.DEFAULT);
}
public static SqlToRelConverter.Config getConverterConfig() {
HintStrategyTable strategies = HintStrategyTable.builder().build();
return SqlToRelConverter.config()
.withHintStrategyTable(strategies)
.withTrimUnusedFields(true)
.withExpand(true)
.addRelBuilderConfigTransform(c -> c.withSimplify(false));
}
public static SqlToRelConverter getSqlToRelConverter(
SqlValidatorScope scope,
SqlValidator sqlValidator,
RelOptPlanner relOptPlanner,
EngineType engineType) {
RexBuilder rexBuilder = new RexBuilder(typeFactory);
RelOptCluster cluster = RelOptCluster.create(relOptPlanner, rexBuilder);
FrameworkConfig fromworkConfig =
Frameworks.newConfigBuilder()
.parserConfig(getParserConfig(engineType))
.defaultSchema(
scope.getValidator().getCatalogReader().getRootSchema().plus())
.build();
return new SqlToRelConverter(
new ViewExpanderImpl(),
sqlValidator,
(CatalogReader) scope.getValidator().getCatalogReader(),
cluster,
fromworkConfig.getConvertletTable(),
getConverterConfig());
}
public static SqlAdvisor getSqlAdvisor(SqlValidatorWithHints validator, EngineType engineType) {
return new SqlAdvisor(validator, getParserConfig(engineType));
}
public static SqlWriterConfig getSqlWriterConfig(EngineType engineType) {
SemanticSqlDialect sqlDialect = SqlDialectFactory.getSqlDialect(engineType);
SqlWriterConfig config =
SqlPrettyWriter.config()
.withDialect(sqlDialect)
.withKeywordsLowerCase(false)
.withClauseEndsLine(true)
.withAlwaysUseParentheses(false)
.withSelectListItemsOnSeparateLines(false)
.withUpdateSetListNewline(false)
.withIndentation(0);
if (EngineType.MYSQL.equals(engineType)) {
// no backticks around function name
config = config.withQuoteAllIdentifiers(false);
}
return config;
}
}

View File

@@ -0,0 +1,164 @@
package com.tencent.supersonic.common.calcite;
import org.apache.calcite.sql.fun.SqlLibrary;
import org.apache.calcite.sql.validate.SqlConformance;
import org.apache.calcite.sql.validate.SqlConformanceEnum;
/** customize the SqlConformance */
public class SemanticSqlConformance implements SqlConformance {
@Override
public boolean isLiberal() {
return SqlConformanceEnum.BIG_QUERY.isLiberal();
}
@Override
public boolean allowCharLiteralAlias() {
return SqlConformanceEnum.BIG_QUERY.allowCharLiteralAlias();
}
@Override
public boolean isGroupByAlias() {
return SqlConformanceEnum.BIG_QUERY.isGroupByAlias();
}
@Override
public boolean isGroupByOrdinal() {
return SqlConformanceEnum.BIG_QUERY.isGroupByOrdinal();
}
@Override
public boolean isHavingAlias() {
return false;
}
@Override
public boolean isSortByOrdinal() {
return SqlConformanceEnum.BIG_QUERY.isSortByOrdinal();
}
@Override
public boolean isSortByAlias() {
return SqlConformanceEnum.BIG_QUERY.isSortByAlias();
}
@Override
public boolean isSortByAliasObscures() {
return SqlConformanceEnum.BIG_QUERY.isSortByAliasObscures();
}
@Override
public boolean isFromRequired() {
return SqlConformanceEnum.BIG_QUERY.isFromRequired();
}
@Override
public boolean splitQuotedTableName() {
return SqlConformanceEnum.BIG_QUERY.splitQuotedTableName();
}
@Override
public boolean allowHyphenInUnquotedTableName() {
return SqlConformanceEnum.BIG_QUERY.allowHyphenInUnquotedTableName();
}
@Override
public boolean isBangEqualAllowed() {
return SqlConformanceEnum.BIG_QUERY.isBangEqualAllowed();
}
@Override
public boolean isPercentRemainderAllowed() {
return SqlConformanceEnum.BIG_QUERY.isPercentRemainderAllowed();
}
@Override
public boolean isMinusAllowed() {
return SqlConformanceEnum.BIG_QUERY.isMinusAllowed();
}
@Override
public boolean isRegexReplaceCaptureGroupDollarIndexed() {
return SqlConformanceEnum.BIG_QUERY.isRegexReplaceCaptureGroupDollarIndexed();
}
@Override
public boolean isApplyAllowed() {
return SqlConformanceEnum.BIG_QUERY.isApplyAllowed();
}
@Override
public boolean isInsertSubsetColumnsAllowed() {
return SqlConformanceEnum.BIG_QUERY.isInsertSubsetColumnsAllowed();
}
@Override
public boolean allowAliasUnnestItems() {
return SqlConformanceEnum.BIG_QUERY.allowAliasUnnestItems();
}
@Override
public boolean allowNiladicParentheses() {
return SqlConformanceEnum.BIG_QUERY.allowNiladicParentheses();
}
@Override
public boolean allowExplicitRowValueConstructor() {
return SqlConformanceEnum.BIG_QUERY.allowExplicitRowValueConstructor();
}
@Override
public boolean allowExtend() {
return SqlConformanceEnum.BIG_QUERY.allowExtend();
}
@Override
public boolean isLimitStartCountAllowed() {
return true;
}
@Override
public boolean isOffsetLimitAllowed() {
return false;
}
@Override
public boolean allowGeometry() {
return SqlConformanceEnum.BIG_QUERY.allowGeometry();
}
@Override
public boolean shouldConvertRaggedUnionTypesToVarying() {
return SqlConformanceEnum.BIG_QUERY.shouldConvertRaggedUnionTypesToVarying();
}
@Override
public boolean allowExtendedTrim() {
return SqlConformanceEnum.BIG_QUERY.allowExtendedTrim();
}
@Override
public boolean allowPluralTimeUnits() {
return SqlConformanceEnum.BIG_QUERY.allowPluralTimeUnits();
}
@Override
public boolean allowQualifyingCommonColumn() {
return SqlConformanceEnum.BIG_QUERY.allowQualifyingCommonColumn();
}
@Override
public boolean isValueAllowed() {
return false;
}
@Override
public SqlLibrary semantics() {
return SqlConformanceEnum.BIG_QUERY.semantics();
}
@Override
public boolean allowLenientCoercion() {
return false;
}
}

View File

@@ -0,0 +1,84 @@
package com.tencent.supersonic.common.calcite;
import com.google.common.base.Preconditions;
import org.apache.calcite.sql.SqlDialect;
import org.apache.calcite.sql.SqlIntervalLiteral;
import org.apache.calcite.sql.SqlNode;
import org.apache.calcite.sql.SqlWriter;
import org.apache.calcite.sql.validate.SqlConformance;
import org.checkerframework.checker.nullness.qual.Nullable;
/** customize the SqlDialect */
public class SemanticSqlDialect extends SqlDialect {
private static final SqlConformance tagTdwSqlConformance = new SemanticSqlConformance();
public SemanticSqlDialect(Context context) {
super(context);
}
public static void unparseFetchUsingAnsi(
SqlWriter writer, @Nullable SqlNode offset, @Nullable SqlNode fetch) {
Preconditions.checkArgument(fetch != null || offset != null);
SqlWriter.Frame fetchFrame;
writer.newlineAndIndent();
fetchFrame = writer.startList(SqlWriter.FrameTypeEnum.OFFSET);
writer.keyword("LIMIT");
boolean hasOffset = false;
if (offset != null) {
offset.unparse(writer, -1, -1);
hasOffset = true;
}
if (fetch != null) {
if (hasOffset) {
writer.keyword(",");
}
fetch.unparse(writer, -1, -1);
}
writer.endList(fetchFrame);
}
@Override
public void quoteStringLiteralUnicode(StringBuilder buf, String val) {
buf.append("'");
buf.append(val);
buf.append("'");
}
@Override
public void quoteStringLiteral(StringBuilder buf, String charsetName, String val) {
buf.append(literalQuoteString);
buf.append(val.replace(literalEndQuoteString, literalEscapedQuote));
buf.append(literalEndQuoteString);
}
@Override
public boolean supportsCharSet() {
return false;
}
@Override
public boolean requiresAliasForFromItems() {
return true;
}
@Override
public SqlConformance getConformance() {
// mysql_5
return tagTdwSqlConformance;
}
public boolean supportsGroupByWithCube() {
return true;
}
public void unparseSqlIntervalLiteral(
SqlWriter writer, SqlIntervalLiteral literal, int leftPrec, int rightPrec) {}
public void unparseOffsetFetch(
SqlWriter writer, @Nullable SqlNode offset, @Nullable SqlNode fetch) {
unparseFetchUsingAnsi(writer, offset, fetch);
}
}

View File

@@ -0,0 +1,19 @@
package com.tencent.supersonic.common.calcite;
import org.apache.calcite.rel.type.RelDataTypeSystem;
import org.apache.calcite.sql.type.SqlTypeFactoryImpl;
import java.nio.charset.Charset;
/** customize the SqlTypeFactoryImpl */
public class SemanticSqlTypeFactoryImpl extends SqlTypeFactoryImpl {
public SemanticSqlTypeFactoryImpl(RelDataTypeSystem typeSystem) {
super(typeSystem);
}
@Override
public Charset getDefaultCharset() {
return Charset.forName("UTF8");
}
}

View File

@@ -0,0 +1,49 @@
package com.tencent.supersonic.common.calcite;
import com.tencent.supersonic.common.pojo.enums.EngineType;
import org.apache.calcite.avatica.util.Casing;
import org.apache.calcite.sql.SqlDialect;
import org.apache.calcite.sql.SqlDialect.Context;
import org.apache.calcite.sql.SqlDialect.DatabaseProduct;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
public class SqlDialectFactory {
public static final Context DEFAULT_CONTEXT =
SqlDialect.EMPTY_CONTEXT
.withDatabaseProduct(DatabaseProduct.BIG_QUERY)
.withLiteralQuoteString("'")
.withLiteralEscapedQuoteString("''")
.withIdentifierQuoteString("`")
.withUnquotedCasing(Casing.UNCHANGED)
.withQuotedCasing(Casing.UNCHANGED)
.withCaseSensitive(false);
public static final Context POSTGRESQL_CONTEXT =
SqlDialect.EMPTY_CONTEXT
.withDatabaseProduct(DatabaseProduct.BIG_QUERY)
.withLiteralQuoteString("'")
.withLiteralEscapedQuoteString("''")
.withUnquotedCasing(Casing.UNCHANGED)
.withQuotedCasing(Casing.UNCHANGED)
.withCaseSensitive(false);
private static Map<EngineType, SemanticSqlDialect> sqlDialectMap;
static {
sqlDialectMap = new HashMap<>();
sqlDialectMap.put(EngineType.CLICKHOUSE, new SemanticSqlDialect(DEFAULT_CONTEXT));
sqlDialectMap.put(EngineType.MYSQL, new SemanticSqlDialect(DEFAULT_CONTEXT));
sqlDialectMap.put(EngineType.H2, new SemanticSqlDialect(DEFAULT_CONTEXT));
sqlDialectMap.put(EngineType.POSTGRESQL, new SemanticSqlDialect(POSTGRESQL_CONTEXT));
}
public static SemanticSqlDialect getSqlDialect(EngineType engineType) {
SemanticSqlDialect semanticSqlDialect = sqlDialectMap.get(engineType);
if (Objects.isNull(semanticSqlDialect)) {
return new SemanticSqlDialect(DEFAULT_CONTEXT);
}
return semanticSqlDialect;
}
}

View File

@@ -0,0 +1,80 @@
package com.tencent.supersonic.common.calcite;
import com.tencent.supersonic.common.pojo.enums.EngineType;
import lombok.extern.slf4j.Slf4j;
import org.apache.calcite.sql.SqlIdentifier;
import org.apache.calcite.sql.SqlLiteral;
import org.apache.calcite.sql.SqlNode;
import org.apache.calcite.sql.SqlNodeList;
import org.apache.calcite.sql.SqlWith;
import org.apache.calcite.sql.SqlWithItem;
import org.apache.calcite.sql.SqlWriterConfig;
import org.apache.calcite.sql.parser.SqlParseException;
import org.apache.calcite.sql.parser.SqlParser;
import org.apache.calcite.sql.parser.SqlParserPos;
import org.apache.calcite.sql.pretty.SqlPrettyWriter;
import java.util.ArrayList;
import java.util.List;
@Slf4j
public class SqlMergeWithUtils {
public static String mergeWith(
EngineType engineType,
String sql,
List<String> parentSqlList,
List<String> parentWithNameList)
throws SqlParseException {
SqlParser.Config parserConfig = Configuration.getParserConfig(engineType);
// Parse the main SQL statement
SqlParser parser = SqlParser.create(sql, parserConfig);
SqlNode sqlNode1 = parser.parseQuery();
// List to hold all WITH items
List<SqlNode> withItemList = new ArrayList<>();
// Iterate over each parentSql and parentWithName pair
for (int i = 0; i < parentSqlList.size(); i++) {
String parentSql = parentSqlList.get(i);
String parentWithName = parentWithNameList.get(i);
// Parse the parent SQL statement
parser = SqlParser.create(parentSql, parserConfig);
SqlNode sqlNode2 = parser.parseQuery();
// Create a new WITH item for parentWithName without quotes
SqlWithItem withItem =
new SqlWithItem(
SqlParserPos.ZERO,
new SqlIdentifier(
parentWithName, SqlParserPos.ZERO), // false to avoid quotes
null,
sqlNode2,
SqlLiteral.createBoolean(false, SqlParserPos.ZERO));
// Add the new WITH item to the list
withItemList.add(withItem);
}
// Extract existing WITH items from sqlNode1 if it is a SqlWith
if (sqlNode1 instanceof SqlWith) {
SqlWith sqlWith = (SqlWith) sqlNode1;
withItemList.addAll(sqlWith.withList.getList());
sqlNode1 = sqlWith.body;
}
// Create a new SqlWith node
SqlWith finalSqlNode =
new SqlWith(
SqlParserPos.ZERO,
new SqlNodeList(withItemList, SqlParserPos.ZERO),
sqlNode1);
// Custom SqlPrettyWriter configuration to avoid quoting identifiers
SqlWriterConfig config = Configuration.getSqlWriterConfig(engineType);
// Pretty print the final SQL
SqlPrettyWriter writer = new SqlPrettyWriter(config);
return writer.format(finalSqlNode);
}
}

View File

@@ -0,0 +1,21 @@
package com.tencent.supersonic.common.calcite;
import org.apache.calcite.plan.RelOptTable;
import org.apache.calcite.rel.RelRoot;
import org.apache.calcite.rel.type.RelDataType;
import java.util.List;
/** customize the ViewExpander */
public class ViewExpanderImpl implements RelOptTable.ViewExpander {
public ViewExpanderImpl() {}
@Override
public RelRoot expandView(
RelDataType rowType,
String queryString,
List<String> schemaPath,
List<String> dataSetPath) {
return null;
}
}

View File

@@ -14,7 +14,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
/** Sql Parser Select Helper */
@Slf4j
public class SqlAsHelper {

View File

@@ -709,51 +709,4 @@ public class SqlReplaceHelper {
}
}
}
public static String dealAliasToOrderBy(String querySql) {
Select selectStatement = SqlSelectHelper.getSelect(querySql);
List<PlainSelect> plainSelectList = new ArrayList<>();
// List<PlainSelect> withPlainSelectList = SqlSelectHelper.getWithItem(selectStatement);
// if (!CollectionUtils.isEmpty(withPlainSelectList)) {
// plainSelectList.addAll(withPlainSelectList);
// }
if (selectStatement instanceof PlainSelect) {
plainSelectList.add((PlainSelect) selectStatement);
} else if (selectStatement instanceof SetOperationList) {
SetOperationList setOperationList = (SetOperationList) selectStatement;
if (!CollectionUtils.isEmpty(setOperationList.getSelects())) {
setOperationList
.getSelects()
.forEach(
subSelectBody -> {
PlainSelect subPlainSelect = (PlainSelect) subSelectBody;
plainSelectList.add(subPlainSelect);
});
}
}
for (PlainSelect plainSelect : plainSelectList) {
List<SelectItem<?>> selectItemList = plainSelect.getSelectItems();
List<OrderByElement> orderByElementList = plainSelect.getOrderByElements();
if (CollectionUtils.isEmpty(orderByElementList)) {
continue;
}
Map<String, Expression> map = new HashMap<>();
for (int i = 0; i < selectItemList.size(); i++) {
if (!Objects.isNull(selectItemList.get(i).getAlias())) {
map.put(
selectItemList.get(i).getAlias().getName(),
selectItemList.get(i).getExpression());
selectItemList.get(i).setAlias(null);
}
}
for (OrderByElement orderByElement : orderByElementList) {
if (map.containsKey(orderByElement.getExpression().toString())) {
orderByElement.setExpression(
map.get(orderByElement.getExpression().toString()));
}
}
plainSelect.setOrderByElements(orderByElementList);
}
return selectStatement.toString();
}
}

View File

@@ -0,0 +1,38 @@
package com.tencent.supersonic.common.pojo.enums;
public enum EngineType {
TDW(0, "tdw"),
MYSQL(1, "mysql"),
DORIS(2, "doris"),
CLICKHOUSE(3, "clickhouse"),
KAFKA(4, "kafka"),
H2(5, "h2"),
POSTGRESQL(6, "postgresql"),
OTHER(7, "other");
private Integer code;
private String name;
EngineType(Integer code, String name) {
this.code = code;
this.name = name;
}
public Integer getCode() {
return code;
}
public String getName() {
return name;
}
public static EngineType fromString(String value) {
for (EngineType engineType : EngineType.values()) {
if (engineType.name().equalsIgnoreCase(value)) {
return engineType;
}
}
throw new IllegalArgumentException("Invalid value: " + value);
}
}

View File

@@ -0,0 +1,64 @@
package com.tencent.supersonic.common.calcite;
import com.tencent.supersonic.common.pojo.enums.EngineType;
import lombok.extern.slf4j.Slf4j;
import org.apache.calcite.sql.parser.SqlParseException;
import org.junit.Assert;
import org.junit.jupiter.api.Test;
import java.util.Collections;
@Slf4j
class SqlWithMergerTest {
@Test
void testWithMerger() throws SqlParseException {
String sql1 =
"WITH DepartmentVisits AS (\n"
+ " SELECT department, SUM(pv) AS 总访问次数\n"
+ " FROM t_1\n"
+ " WHERE sys_imp_date >= '2024-09-01' AND sys_imp_date <= '2024-09-29'\n"
+ " GROUP BY department\n"
+ ")\n"
+ "SELECT COUNT(*) FROM DepartmentVisits WHERE 总访问次数 > 100";
String sql2 =
"SELECT `t3`.`sys_imp_date`, `t2`.`department`, `t3`.`s2_pv_uv_statis_pv` AS `pv`\n"
+ "FROM (SELECT `user_name`, `department` FROM `s2_user_department`) AS `t2`\n"
+ "LEFT JOIN (SELECT 1 AS `s2_pv_uv_statis_pv`, `imp_date` AS `sys_imp_date`, `user_name`\n"
+ "FROM `s2_pv_uv_statis`) AS `t3` ON `t2`.`user_name` = `t3`.`user_name`";
String mergeSql =
SqlMergeWithUtils.mergeWith(
EngineType.MYSQL,
sql1,
Collections.singletonList(sql2),
Collections.singletonList("t_1"));
System.out.println(mergeSql);
sql1 =
"WITH DepartmentVisits AS (SELECT department, SUM(pv) AS 总访问次数 FROM t_1 WHERE sys_imp_date >= '2024-08-28' "
+ "AND sys_imp_date <= '2024-09-28' GROUP BY department) SELECT COUNT(*) FROM DepartmentVisits WHERE 总访问次数 > 100 LIMIT 1000";
sql2 =
"SELECT `t3`.`sys_imp_date`, `t2`.`department`, `t3`.`s2_pv_uv_statis_pv` AS `pv`\n"
+ "FROM\n"
+ "(SELECT `user_name`, `department`\n"
+ "FROM\n"
+ "`s2_user_department`) AS `t2`\n"
+ "LEFT JOIN (SELECT 1 AS `s2_pv_uv_statis_pv`, `imp_date` AS `sys_imp_date`, `user_name`\n"
+ "FROM\n"
+ "`s2_pv_uv_statis`) AS `t3` ON `t2`.`user_name` = `t3`.`user_name`";
mergeSql =
SqlMergeWithUtils.mergeWith(
EngineType.H2,
sql1,
Collections.singletonList(sql2),
Collections.singletonList("t_1"));
System.out.println(mergeSql);
}
}

View File

@@ -8,7 +8,7 @@ import java.util.List;
class SqlAsHelperTest {
@Test
void testReplaceAggField() {
void getAsFields() {
String sql =
"WITH SalesData AS (\n"
+ " SELECT \n"