private static boolean analyzeSelect(
      QueryStatement queryStatement,
      Analysis analysis,
      List<Pair<Expression, String>> outputExpressions,
      Template template) {
    LinkedHashSet<Expression> selectExpressions = new LinkedHashSet<>();
    selectExpressions.add(DEVICE_EXPRESSION);
    if (queryStatement.isOutputEndTime()) {
      return false;
    }

    analysis.setDeviceTemplate(template);

    ColumnPaginationController paginationController =
        new ColumnPaginationController(
            queryStatement.getSeriesLimit(), queryStatement.getSeriesOffset());

    Set<Expression> aggregationExpressions = new LinkedHashSet<>();
    for (ResultColumn resultColumn : queryStatement.getSelectComponent().getResultColumns()) {
      Expression selectExpression = resultColumn.getExpression();

      if (selectExpression instanceof FunctionExpression
          && COUNT_TIME.equalsIgnoreCase(
              ((FunctionExpression) selectExpression).getFunctionName())) {
        outputExpressions.add(new Pair<>(selectExpression, resultColumn.getAlias()));
        selectExpressions.add(selectExpression);
        aggregationExpressions.add(selectExpression);

        analysis.getExpressionTypes().put(NodeRef.of(selectExpression), TSDataType.INT64);
        ((FunctionExpression) selectExpression)
            .setExpressions(Collections.singletonList(new TimestampOperand()));
        continue;
      }

      List<Expression> subExpressions;
      if (selectExpression.getOutputSymbol().contains("*")) {
        // when exist wildcard, only support agg(*) and count_time(*)
        if (selectExpression instanceof FunctionExpression
            && selectExpression.getExpressions().size() == 1
            && "*".equalsIgnoreCase(selectExpression.getExpressions().get(0).getOutputSymbol())) {
          subExpressions = new ArrayList<>();
          FunctionExpression functionExpression = (FunctionExpression) selectExpression;
          for (Map.Entry<String, IMeasurementSchema> entry : template.getSchemaMap().entrySet()) {
            FunctionExpression subFunctionExpression =
                new FunctionExpression(
                    functionExpression.getFunctionName(),
                    functionExpression.getFunctionAttributes(),
                    Collections.singletonList(
                        new TimeSeriesOperand(
                            new PartialPath(new String[] {entry.getKey()}),
                            entry.getValue().getType())));
            subFunctionExpression.setFunctionType(functionExpression.getFunctionType());
            subExpressions.add(subFunctionExpression);
          }
        } else {
          return false;
        }
      } else {
        subExpressions = Collections.singletonList(selectExpression);
      }

      for (Expression expression : subExpressions) {
        if (paginationController.hasCurOffset()) {
          paginationController.consumeOffset();
        } else if (paginationController.hasCurLimit()) {
          outputExpressions.add(new Pair<>(expression, resultColumn.getAlias()));
          selectExpressions.add(expression);
          aggregationExpressions.add(expression);
          analyzeExpressionType(analysis, expression);
        } else {
          break;
        }
      }
    }

    List<String> measurementList = new ArrayList<>();
    List<IMeasurementSchema> measurementSchemaList = new ArrayList<>();
    Set<String> measurementSet = new HashSet<>();

    if (queryStatement.isCountTimeAggregation()) {
      measurementList = new ArrayList<>(template.getSchemaMap().keySet());
      measurementSchemaList = new ArrayList<>(template.getSchemaMap().values());
    } else {
      int idx = 0;
      for (Expression selectExpression : selectExpressions) {
        idx++;
        if (idx == 1
            || (idx == 2 && ENDTIME.equalsIgnoreCase(selectExpression.getOutputSymbol()))) {
          continue;
        }

        String measurement = selectExpression.getExpressions().get(0).getOutputSymbol();
        //  not support agg(*), agg(s1+1) now
        if (!template.getSchemaMap().containsKey(measurement)) {
          return false;
        }

        // for agg1(s1) + agg2(s1), only record s1 for one time
        if (!measurementSet.contains(measurement)) {
          measurementSet.add(measurement);
          measurementList.add(measurement);
          measurementSchemaList.add(template.getSchemaMap().get(measurement));
        }
      }
    }

    analysis.setMeasurementList(measurementList);
    analysis.setMeasurementSchemaList(measurementSchemaList);
    analysis.setAggregationExpressions(aggregationExpressions);
    analysis.setOutputExpressions(outputExpressions);
    analysis.setSelectExpressions(selectExpressions);
    return true;
  }
Methods in this class are used for aggregation, templated with align by device situation.
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/TemplatedAggregationAnalyze.java