private List<Pair<Expression, String>> analyzeSelect(
      Analysis analysis,
      QueryStatement queryStatement,
      ISchemaTree schemaTree,
      List<PartialPath> deviceList,
      MPPQueryContext queryContext) {
    List<Pair<Expression, String>> outputExpressions = new ArrayList<>();
    Map<IDeviceID, Set<Expression>> deviceToSelectExpressions = new HashMap<>();
    ColumnPaginationController paginationController =
        new ColumnPaginationController(
            queryStatement.getSeriesLimit(), queryStatement.getSeriesOffset());

    for (ResultColumn resultColumn : queryStatement.getSelectComponent().getResultColumns()) {
      Expression selectExpression = resultColumn.getExpression();

      // select expression after removing wildcard
      // use LinkedHashMap for order-preserving
      Map<Expression, Map<IDeviceID, Expression>> measurementToDeviceSelectExpressions =
          new LinkedHashMap<>();
      for (PartialPath device : deviceList) {
        List<Expression> selectExpressionsOfOneDevice =
            concatDeviceAndBindSchemaForExpression(
                selectExpression, device, schemaTree, queryContext);
        if (selectExpressionsOfOneDevice.isEmpty()) {
          continue;
        }

        updateMeasurementToDeviceSelectExpressions(
            analysis,
            measurementToDeviceSelectExpressions,
            device.getIDeviceIDAsFullDevice(),
            selectExpressionsOfOneDevice);
      }

      checkAliasUniqueness(resultColumn.getAlias(), measurementToDeviceSelectExpressions);

      for (Map.Entry<Expression, Map<IDeviceID, Expression>> entry :
          measurementToDeviceSelectExpressions.entrySet()) {
        Expression measurementExpression = entry.getKey();
        Map<IDeviceID, Expression> deviceToSelectExpressionsOfOneMeasurement = entry.getValue();

        if (paginationController.hasCurOffset()) {
          paginationController.consumeOffset();
        } else if (paginationController.hasCurLimit()) {
          deviceToSelectExpressionsOfOneMeasurement
              .values()
              .forEach(expression -> analyzeExpressionType(analysis, expression));
          // check whether the datatype of paths which has the same measurement name are
          // consistent; if not, throw a SemanticException
          checkDataTypeConsistencyInAlignByDevice(
              analysis, new ArrayList<>(deviceToSelectExpressionsOfOneMeasurement.values()));

          // add outputExpressions
          Expression lowerCaseMeasurementExpression = toLowerCaseExpression(measurementExpression);
          analyzeExpressionType(analysis, lowerCaseMeasurementExpression);

          outputExpressions.add(
              new Pair<>(
                  lowerCaseMeasurementExpression,
                  analyzeAlias(
                      resultColumn.getAlias(),
                      measurementExpression,
                      lowerCaseMeasurementExpression,
                      queryStatement)));

          // add deviceToSelectExpressions
          updateDeviceToSelectExpressions(
              analysis, deviceToSelectExpressions, deviceToSelectExpressionsOfOneMeasurement);

          paginationController.consumeLimit();
        } else {
          break;
        }
      }
    }

    // remove devices without measurements to compute
    Set<PartialPath> noMeasurementDevices = new HashSet<>();
    for (PartialPath device : deviceList) {
      if (!deviceToSelectExpressions.containsKey(device.getIDeviceIDAsFullDevice())) {
        noMeasurementDevices.add(device);
      }
    }
    deviceList.removeAll(noMeasurementDevices);

    // when the select expression of any device is empty,
    // the where expression map also need remove this device
    if (analysis.getDeviceToWhereExpression() != null) {
      noMeasurementDevices.forEach(
          devicePath ->
              analysis.getDeviceToWhereExpression().remove(devicePath.getIDeviceIDAsFullDevice()));
    }

    Set<Expression> selectExpressions = new LinkedHashSet<>();
    selectExpressions.add(DEVICE_EXPRESSION);
    if (queryStatement.isOutputEndTime()) {
      selectExpressions.add(END_TIME_EXPRESSION);
    }
    outputExpressions.forEach(pair -> selectExpressions.add(pair.getLeft()));
    analysis.setSelectExpressions(selectExpressions);

    analysis.setDeviceToSelectExpressions(deviceToSelectExpressions);

    return outputExpressions;
  }
process select component for align by device.
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeVisitor.java