This is an automated email from the ASF dual-hosted git repository.

chenglei pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/phoenix.git


The following commit(s) were added to refs/heads/master by this push:
     new e2479cc541 PHOENIX-7397 Optimize ClientAggregatePlan/ClientScanPlan 
when inner q… (#1966)
e2479cc541 is described below

commit e2479cc54175b11e486845e30fbc258fa15f163f
Author: chenglei <[email protected]>
AuthorDate: Wed Sep 11 17:22:57 2024 +0800

    PHOENIX-7397 Optimize ClientAggregatePlan/ClientScanPlan when inner q… 
(#1966)
---
 .../org/apache/phoenix/compile/CompiledOffset.java |   2 +
 .../org/apache/phoenix/compile/OffsetCompiler.java |   4 +-
 .../apache/phoenix/compile/OrderByCompiler.java    |  18 +-
 .../org/apache/phoenix/compile/QueryCompiler.java  |  16 +-
 .../org/apache/phoenix/compile/UnionCompiler.java  | 122 ++++++-
 .../phoenix/execute/ClientAggregatePlan.java       |   5 +-
 .../java/org/apache/phoenix/execute/UnionPlan.java |  86 ++++-
 .../phoenix/expression/OrderByExpression.java      |   9 +-
 .../java/org/apache/phoenix/parse/OrderByNode.java |  13 +
 .../org/apache/phoenix/parse/SelectStatement.java  |  12 +
 .../org/apache/phoenix/util/ExpressionUtil.java    |   4 +-
 .../org/apache/phoenix/end2end/DerivedTableIT.java |   4 +-
 .../phoenix/end2end/SortMergeJoinMoreIT.java       |   4 +-
 .../org/apache/phoenix/end2end/UnionAllIT.java     | 327 ++++++++++++++++++
 .../apache/phoenix/compile/QueryCompilerTest.java  | 366 ++++++++++++++++++++-
 15 files changed, 957 insertions(+), 35 deletions(-)

diff --git 
a/phoenix-core-client/src/main/java/org/apache/phoenix/compile/CompiledOffset.java
 
b/phoenix-core-client/src/main/java/org/apache/phoenix/compile/CompiledOffset.java
index 027265ba64..aa97f0760a 100644
--- 
a/phoenix-core-client/src/main/java/org/apache/phoenix/compile/CompiledOffset.java
+++ 
b/phoenix-core-client/src/main/java/org/apache/phoenix/compile/CompiledOffset.java
@@ -23,6 +23,8 @@ import 
org.apache.phoenix.thirdparty.com.google.common.base.Optional;
  * CompiledOffset represents the result of the Compiler on the OFFSET clause.
  */
 public class CompiledOffset {
+    public static final CompiledOffset EMPTY_COMPILED_OFFSET =
+            new CompiledOffset(Optional.<Integer>absent(), 
Optional.<byte[]>absent());
     private final Optional<Integer> integerOffset;
     private final Optional<byte[]> byteOffset;
 
diff --git 
a/phoenix-core-client/src/main/java/org/apache/phoenix/compile/OffsetCompiler.java
 
b/phoenix-core-client/src/main/java/org/apache/phoenix/compile/OffsetCompiler.java
index ae522ca698..f83aa30d39 100644
--- 
a/phoenix-core-client/src/main/java/org/apache/phoenix/compile/OffsetCompiler.java
+++ 
b/phoenix-core-client/src/main/java/org/apache/phoenix/compile/OffsetCompiler.java
@@ -80,7 +80,9 @@ public class OffsetCompiler {
 
     public CompiledOffset compile(StatementContext context, 
FilterableStatement statement, boolean inJoin, boolean inUnion) throws 
SQLException {
         OffsetNode offsetNode = statement.getOffset();
-        if (offsetNode == null) { return new 
CompiledOffset(Optional.<Integer>absent(), Optional.<byte[]>absent()); }
+        if (offsetNode == null) {
+            return CompiledOffset.EMPTY_COMPILED_OFFSET;
+        }
         if (offsetNode.isIntegerOffset()) {
             OffsetParseNodeVisitor visitor = new 
OffsetParseNodeVisitor(context);
             offsetNode.getOffsetParseNode().accept(visitor);
diff --git 
a/phoenix-core-client/src/main/java/org/apache/phoenix/compile/OrderByCompiler.java
 
b/phoenix-core-client/src/main/java/org/apache/phoenix/compile/OrderByCompiler.java
index 1f8a819ba8..7faf1dbcbd 100644
--- 
a/phoenix-core-client/src/main/java/org/apache/phoenix/compile/OrderByCompiler.java
+++ 
b/phoenix-core-client/src/main/java/org/apache/phoenix/compile/OrderByCompiler.java
@@ -23,6 +23,7 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Objects;
 
 import org.apache.phoenix.compile.GroupByCompiler.GroupBy;
 import org.apache.phoenix.compile.OrderPreservingTracker.Ordering;
@@ -32,16 +33,13 @@ import org.apache.phoenix.expression.Expression;
 import org.apache.phoenix.expression.OrderByExpression;
 import org.apache.phoenix.iterate.OrderedResultIterator;
 import org.apache.phoenix.parse.HintNode.Hint;
-import org.apache.phoenix.parse.LiteralParseNode;
 import org.apache.phoenix.parse.OrderByNode;
-import org.apache.phoenix.parse.ParseNode;
 import org.apache.phoenix.parse.SelectStatement;
 import org.apache.phoenix.query.QueryServices;
 import org.apache.phoenix.query.QueryServicesOptions;
 import org.apache.phoenix.schema.PTableType;
 import 
org.apache.phoenix.schema.RowValueConstructorOffsetNotAllowedInQueryException;
 import 
org.apache.phoenix.schema.RowValueConstructorOffsetNotCoercibleException;
-import org.apache.phoenix.schema.types.PInteger;
 
 import org.apache.phoenix.thirdparty.com.google.common.collect.ImmutableList;
 import org.apache.phoenix.thirdparty.com.google.common.collect.Lists;
@@ -105,6 +103,10 @@ public class OrderByCompiler {
             }
             return new OrderBy(newOrderByExpressions);
         }
+
+        public static boolean equalsForOutputOrderBy(OrderBy orderBy1, OrderBy 
orderBy2) {
+            return Objects.equals(orderBy1.orderByExpressions, 
orderBy2.orderByExpressions);
+        }
     }
     /**
      * Gets a list of columns in the ORDER BY clause
@@ -150,10 +152,14 @@ public class OrderByCompiler {
 
         LinkedHashSet<OrderByExpression> orderByExpressions = 
Sets.newLinkedHashSetWithExpectedSize(orderByNodes.size());
         for (OrderByNode node : orderByNodes) {
-            ParseNode parseNode = node.getNode();
             Expression expression = null;
-            if (parseNode instanceof LiteralParseNode && 
((LiteralParseNode)parseNode).getType() == PInteger.INSTANCE){
-                Integer index = 
(Integer)((LiteralParseNode)parseNode).getValue();
+            if (node.isIntegerLiteral()) {
+                if (rowProjector == null) {
+                    throw new IllegalStateException(
+                            "rowProjector is null when there is 
LiteralParseNode in orderByNodes");
+                }
+                Integer index = node.getValueIfIntegerLiteral();
+                assert index != null;
                 int size = rowProjector.getColumnProjectors().size();
                 if (index > size || index <= 0 ) {
                     throw new 
SQLExceptionInfo.Builder(SQLExceptionCode.PARAM_INDEX_OUT_OF_BOUND)
diff --git 
a/phoenix-core-client/src/main/java/org/apache/phoenix/compile/QueryCompiler.java
 
b/phoenix-core-client/src/main/java/org/apache/phoenix/compile/QueryCompiler.java
index 9444241931..0c40b2ca5d 100644
--- 
a/phoenix-core-client/src/main/java/org/apache/phoenix/compile/QueryCompiler.java
+++ 
b/phoenix-core-client/src/main/java/org/apache/phoenix/compile/QueryCompiler.java
@@ -42,7 +42,6 @@ import org.apache.phoenix.compile.JoinCompiler.JoinSpec;
 import org.apache.phoenix.compile.JoinCompiler.JoinTable;
 import org.apache.phoenix.compile.JoinCompiler.Table;
 import org.apache.phoenix.compile.OrderByCompiler.OrderBy;
-import org.apache.phoenix.coprocessorclient.BaseScannerRegionObserverConstants;
 import org.apache.phoenix.exception.SQLExceptionCode;
 import org.apache.phoenix.exception.SQLExceptionInfo;
 import org.apache.phoenix.execute.AggregatePlan;
@@ -237,10 +236,12 @@ public class QueryCompiler {
             QueryPlan subPlan = compileSubquery(subSelect, true);
             plans.add(subPlan);
         }
+
         TableRef tableRef = UnionCompiler.contructSchemaTable(statement, plans,
             select.hasWildcard() ? null : select.getSelect());
         ColumnResolver resolver = FromCompiler.getResolver(tableRef);
         StatementContext context = new StatementContext(statement, resolver, 
bindManager, scan, sequenceManager);
+        plans = UnionCompiler.convertToTupleProjectionPlan(plans, tableRef, 
context);
         QueryPlan plan = compileSingleFlatQuery(
                 context,
                 select,
@@ -267,7 +268,7 @@ public class QueryCompiler {
     }
 
     public QueryPlan compileSelect(SelectStatement select) throws SQLException{
-        StatementContext context = new StatementContext(statement, resolver, 
bindManager, scan, sequenceManager);
+        StatementContext context = createStatementContext();
         QueryPlan dataPlanForCDC = getExistingDataPlanForCDC();
         if (dataPlanForCDC != null) {
             TableRef cdcTableRef = dataPlanForCDC.getTableRef();
@@ -298,6 +299,10 @@ public class QueryCompiler {
         }
     }
 
+    private StatementContext createStatementContext() {
+        return new StatementContext(statement, resolver, bindManager, scan, 
sequenceManager);
+    }
+
     /**
      * Call compileJoinQuery() for join queries recursively down to the leaf 
JoinTable nodes.
      * If it is a leaf node, call compileSingleFlatQuery() or 
compileSubquery(), otherwise:
@@ -721,6 +726,13 @@ public class QueryCompiler {
         }
 
         QueryPlan innerPlan = compileSubquery(innerSelect, false);
+        if (innerPlan instanceof UnionPlan) {
+            UnionCompiler.optimizeUnionOrderByIfPossible(
+                    (UnionPlan) innerPlan,
+                    select,
+                    this::createStatementContext);
+        }
+
         RowProjector innerQueryPlanRowProjector = innerPlan.getProjector();
         TupleProjector tupleProjector = new 
TupleProjector(innerQueryPlanRowProjector);
 
diff --git 
a/phoenix-core-client/src/main/java/org/apache/phoenix/compile/UnionCompiler.java
 
b/phoenix-core-client/src/main/java/org/apache/phoenix/compile/UnionCompiler.java
index 6277affcda..97597aaf8b 100644
--- 
a/phoenix-core-client/src/main/java/org/apache/phoenix/compile/UnionCompiler.java
+++ 
b/phoenix-core-client/src/main/java/org/apache/phoenix/compile/UnionCompiler.java
@@ -23,17 +23,23 @@ import java.sql.SQLException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.function.Supplier;
 
 import org.apache.phoenix.thirdparty.com.google.common.collect.ImmutableList;
 import org.apache.hadoop.hbase.HConstants;
+import org.apache.phoenix.compile.GroupByCompiler.GroupBy;
+import org.apache.phoenix.compile.OrderByCompiler.OrderBy;
 import org.apache.phoenix.exception.SQLExceptionCode;
 import org.apache.phoenix.exception.SQLExceptionInfo;
 import org.apache.phoenix.execute.TupleProjectionPlan;
 import org.apache.phoenix.execute.TupleProjector;
+import org.apache.phoenix.execute.UnionPlan;
 import org.apache.phoenix.expression.CoerceExpression;
 import org.apache.phoenix.expression.Expression;
 import org.apache.phoenix.jdbc.PhoenixStatement;
 import org.apache.phoenix.parse.AliasedNode;
+import org.apache.phoenix.parse.OrderByNode;
+import org.apache.phoenix.parse.SelectStatement;
 import org.apache.phoenix.schema.PColumn;
 import org.apache.phoenix.schema.PColumnImpl;
 import org.apache.phoenix.schema.PName;
@@ -80,12 +86,6 @@ public class UnionCompiler {
     public static TableRef contructSchemaTable(PhoenixStatement statement, 
List<QueryPlan> plans,
             List<AliasedNode> selectNodes) throws SQLException {
         List<TargetDataExpression> targetTypes = 
checkProjectionNumAndExpressions(plans);
-        for (int i = 0; i < plans.size(); i++) {
-            QueryPlan subPlan = plans.get(i);
-            TupleProjector projector = 
getTupleProjector(subPlan.getProjector(), targetTypes);
-            subPlan = new TupleProjectionPlan(subPlan, projector, null, null);
-            plans.set(i, subPlan);
-        }
         QueryPlan plan = plans.get(0);
         List<PColumn> projectedColumns = new ArrayList<PColumn>();
         for (int i = 0; i < plan.getProjector().getColumnCount(); i++) {
@@ -161,13 +161,13 @@ public class UnionCompiler {
     }
 
     private static TupleProjector getTupleProjector(RowProjector rowProj,
-            List<TargetDataExpression> targetTypes) throws SQLException {
-        Expression[] exprs = new Expression[targetTypes.size()];
+            List<PColumn> columns) throws SQLException {
+        Expression[] exprs = new Expression[columns.size()];
         int i = 0;
         for (ColumnProjector colProj : rowProj.getColumnProjectors()) {
             exprs[i] = CoerceExpression.create(colProj.getExpression(),
-                targetTypes.get(i).getType(), 
targetTypes.get(i).getSortOrder(),
-                targetTypes.get(i).getMaxLength());
+                    columns.get(i).getDataType(), 
columns.get(i).getSortOrder(),
+                    columns.get(i).getMaxLength());
             i++;
         }
         return new TupleProjector(exprs);
@@ -218,4 +218,106 @@ public class UnionCompiler {
             this.sortOrder = sortOrder;
         }
     }
+
+    static List<QueryPlan> convertToTupleProjectionPlan(
+            List<QueryPlan> plans,
+            TableRef tableRef,
+            StatementContext statementContext) throws SQLException {
+        List<PColumn> columns =  tableRef.getTable().getColumns();
+        for (int i = 0; i < plans.size(); i++) {
+            QueryPlan subPlan = plans.get(i);
+            TupleProjector projector = 
getTupleProjector(subPlan.getProjector(), columns);
+            subPlan = new TupleProjectionPlan(subPlan, projector, 
statementContext, null);
+            plans.set(i, subPlan);
+        }
+        return plans;
+    }
+
+    /**
+     * If every subquery in {@link UnionPlan} is ordered, and {@link 
QueryPlan#getOutputOrderBys}
+     * of each subquery are equal(absolute equality or the same column name is 
unnecessary, just
+     * column types are compatible and columns count is same), and at the same 
time the outer
+     * query of {@link UnionPlan} has group by or order by, we would further 
examine whether
+     * maintaining this order for the entire {@link UnionPlan} can compile out 
the outer query's
+     * group by or order by. If it is sure, then {@link UnionPlan} just to 
perform a simple
+     * merge on the output of each subquery to ensure the overall order of the 
union all;
+     * otherwise, {@link UnionPlan} would not perform any special processing 
on the output
+     * of the subqueries.
+     */
+    static void optimizeUnionOrderByIfPossible(
+            UnionPlan innerUnionPlan,
+            SelectStatement outerSelectStatement,
+            Supplier<StatementContext> statementContextCreator) throws 
SQLException {
+        innerUnionPlan.enableCheckSupportOrderByOptimize();
+        if (!innerUnionPlan.isSupportOrderByOptimize()) {
+            return;
+        }
+
+        if (!isOptimizeUnionOrderByDeserved(
+                innerUnionPlan, outerSelectStatement, 
statementContextCreator)) {
+            // If maintain the order for the entire UnionPlan(by merge on the 
output of each
+            // subquery) could not compile out the outer query's group by or 
order by, we would
+            // not perform any special processing on the output of the 
subqueries.
+            innerUnionPlan.disableSupportOrderByOptimize();
+        }
+    }
+
+    /**
+     * If group by or order by in outerSelectStatement could be compiled out,
+     * this optimization is deserved.
+     */
+    private static boolean isOptimizeUnionOrderByDeserved(
+            UnionPlan innerUnionPlan,
+            SelectStatement outerSelectStatement,
+            Supplier<StatementContext> statementContextCreator) throws 
SQLException {
+        if (!outerSelectStatement.haveGroupBy() && 
!outerSelectStatement.haveOrderBy()) {
+            return false;
+        }
+
+        // Just to avoid additional ProjectionCompiler.compile, make the 
compilation of order by
+        // as simple as possible.
+        if (!outerSelectStatement.haveGroupBy()
+                && 
outerSelectStatement.getOrderBy().stream().anyMatch(OrderByNode::isIntegerLiteral))
 {
+            return false;
+        }
+        StatementContext statementContext = statementContextCreator.get();
+        ColumnResolver columResover = 
innerUnionPlan.getContext().getResolver();
+        TableRef tableRef = innerUnionPlan.getTableRef();
+        statementContext.setResolver(columResover);
+        statementContext.setCurrentTable(tableRef);
+
+        if (outerSelectStatement.haveGroupBy()) {
+            // For outer query has group by, we check whether 
groupBy.isOrderPreserving is true.
+            GroupBy groupBy = GroupByCompiler.compile(statementContext, 
outerSelectStatement);
+            outerSelectStatement =
+                    HavingCompiler.rewrite(statementContext, 
outerSelectStatement, groupBy);
+            Expression where = WhereCompiler.compile(
+                    statementContext,
+                    outerSelectStatement,
+                    null,
+                    null,
+                    CompiledOffset.EMPTY_COMPILED_OFFSET.getByteOffset());
+            groupBy = groupBy.compile(statementContext, innerUnionPlan, where);
+            return groupBy.isOrderPreserving();
+        }
+
+        assert outerSelectStatement.haveOrderBy();
+        Expression where = WhereCompiler.compile(
+                statementContext,
+                outerSelectStatement,
+                null,
+                null,
+                CompiledOffset.EMPTY_COMPILED_OFFSET.getByteOffset());
+        // For outer query has order by, we check whether orderBy is 
OrderBy.FWD_ROW_KEY_ORDER_BY.
+        OrderBy orderBy = OrderByCompiler.compile(
+                statementContext,
+                outerSelectStatement,
+                GroupBy.EMPTY_GROUP_BY,
+                null,
+                CompiledOffset.EMPTY_COMPILED_OFFSET,
+                null,
+                innerUnionPlan,
+                where);
+        return orderBy == OrderBy.FWD_ROW_KEY_ORDER_BY;
+    }
 }
diff --git 
a/phoenix-core-client/src/main/java/org/apache/phoenix/execute/ClientAggregatePlan.java
 
b/phoenix-core-client/src/main/java/org/apache/phoenix/execute/ClientAggregatePlan.java
index 1b472a0fe3..b9a34c217e 100644
--- 
a/phoenix-core-client/src/main/java/org/apache/phoenix/execute/ClientAggregatePlan.java
+++ 
b/phoenix-core-client/src/main/java/org/apache/phoenix/execute/ClientAggregatePlan.java
@@ -236,8 +236,9 @@ public class ClientAggregatePlan extends 
ClientProcessingPlan {
             planSteps.add("CLIENT AGGREGATE INTO SINGLE ROW");
             newBuilder.setClientAggregate("CLIENT AGGREGATE INTO SINGLE ROW");
         } else if (groupBy.isOrderPreserving()) {
-            planSteps.add("CLIENT AGGREGATE INTO DISTINCT ROWS BY " + 
groupBy.getExpressions().toString());
-            newBuilder.setClientAggregate("CLIENT AGGREGATE INTO DISTINCT ROWS 
BY "
+            planSteps.add("CLIENT AGGREGATE INTO ORDERED DISTINCT ROWS BY "
+                + groupBy.getExpressions().toString());
+            newBuilder.setClientAggregate("CLIENT AGGREGATE INTO ORDERED 
DISTINCT ROWS BY "
                 + groupBy.getExpressions().toString());
         } else if (useHashAgg) {
             planSteps.add("CLIENT HASH AGGREGATE INTO DISTINCT ROWS BY " + 
groupBy.getExpressions().toString());
diff --git 
a/phoenix-core-client/src/main/java/org/apache/phoenix/execute/UnionPlan.java 
b/phoenix-core-client/src/main/java/org/apache/phoenix/execute/UnionPlan.java
index 649fd766ae..e1f0885886 100644
--- 
a/phoenix-core-client/src/main/java/org/apache/phoenix/execute/UnionPlan.java
+++ 
b/phoenix-core-client/src/main/java/org/apache/phoenix/execute/UnionPlan.java
@@ -26,6 +26,7 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 import org.apache.hadoop.hbase.client.Scan;
 import org.apache.phoenix.compile.ExplainPlan;
@@ -38,6 +39,7 @@ import org.apache.phoenix.compile.RowProjector;
 import org.apache.phoenix.compile.ScanRanges;
 import org.apache.phoenix.compile.StatementContext;
 import org.apache.phoenix.execute.visitor.QueryPlanVisitor;
+import org.apache.phoenix.expression.OrderByExpression;
 import org.apache.phoenix.iterate.ConcatResultIterator;
 import org.apache.phoenix.iterate.DefaultParallelScanGrouper;
 import org.apache.phoenix.iterate.LimitingResultIterator;
@@ -51,7 +53,6 @@ import org.apache.phoenix.optimize.Cost;
 import org.apache.phoenix.parse.FilterableStatement;
 import org.apache.phoenix.query.KeyRange;
 import org.apache.phoenix.schema.TableRef;
-import org.apache.phoenix.util.ExpressionUtil;
 
 import org.apache.phoenix.thirdparty.com.google.common.collect.Sets;
 
@@ -75,6 +76,8 @@ public class UnionPlan implements QueryPlan {
     private Long estimatedBytes;
     private Long estimateInfoTs;
     private boolean getEstimatesCalled;
+    private boolean supportOrderByOptimize = false;
+    private List<OrderBy> outputOrderBys = null;
 
     public UnionPlan(StatementContext context, FilterableStatement statement, 
TableRef table, RowProjector projector,
             Integer limit, Integer offset, OrderBy orderBy, GroupBy groupBy, 
List<QueryPlan> plans, ParameterMetaData paramMetaData) throws SQLException {
@@ -95,7 +98,53 @@ public class UnionPlan implements QueryPlan {
                 break;
             }
         }
-        this.isDegenerate = isDegen;     
+        this.isDegenerate = isDegen;
+    }
+
+    /**
+     * If every subquery in {@link UnionPlan} is ordered, and {@link 
QueryPlan#getOutputOrderBys}
+     * of each subquery are equal(absolute equality or the same column name is 
unnecessary, just
+     * column types are compatible and columns count is same), then it just 
needs to perform a
+     * simple merge on the subquery results to ensure the overall order of the 
union all, see
+     * comments on {@link QueryCompiler#optimizeUnionOrderByIfPossible}.
+     */
+    private boolean checkIfSupportOrderByOptimize() {
+        if (!this.orderBy.isEmpty()) {
+            return false;
+        }
+        if (plans.isEmpty()) {
+            return false;
+        }
+        OrderBy prevOrderBy = null;
+        for (QueryPlan queryPlan : plans) {
+            List<OrderBy> orderBys = queryPlan.getOutputOrderBys();
+            if (orderBys.isEmpty() || orderBys.size() > 1) {
+                return false;
+            }
+            OrderBy orderBy = orderBys.get(0);
+            if (prevOrderBy != null && 
!OrderBy.equalsForOutputOrderBy(prevOrderBy, orderBy)) {
+                return false;
+            }
+            prevOrderBy = orderBy;
+        }
+        return true;
+    }
+
+    public boolean isSupportOrderByOptimize() {
+        return this.supportOrderByOptimize;
+    }
+
+    public void enableCheckSupportOrderByOptimize() {
+        this.supportOrderByOptimize = checkIfSupportOrderByOptimize();
+        this.outputOrderBys = null;
+    }
+
+    public void disableSupportOrderByOptimize() {
+        if (!this.supportOrderByOptimize) {
+            return;
+        }
+        this.outputOrderBys = null;
+        this.supportOrderByOptimize = false;
     }
 
     @Override
@@ -165,10 +214,13 @@ public class UnionPlan implements QueryPlan {
     public final ResultIterator iterator(ParallelScanGrouper scanGrouper, Scan 
scan) throws SQLException {
         this.iterators = new UnionResultIterators(plans, parentContext);
         ResultIterator scanner;      
-        boolean isOrdered = !orderBy.getOrderByExpressions().isEmpty();
 
-        if (isOrdered) { // TopN
+        if (!orderBy.isEmpty()) { // TopN
             scanner = new MergeSortTopNResultIterator(iterators, limit, 
offset, orderBy.getOrderByExpressions());
+        } else if (this.supportOrderByOptimize) {
+            //Every subquery is ordered
+            scanner = new MergeSortTopNResultIterator(
+                    iterators, limit, offset, 
getOrderByExpressionsWhenSupportOrderByOptimize());
         } else {
             scanner = new ConcatResultIterator(iterators);
             if (offset != null) {
@@ -308,17 +360,43 @@ public class UnionPlan implements QueryPlan {
         }
     }
 
+    @edu.umd.cs.findbugs.annotations.SuppressWarnings(
+            value = "EI_EXPOSE_REP",
+            justification = "getOutputOrderBys designed to work this way.")
     @Override
     public List<OrderBy> getOutputOrderBys() {
+        if (this.outputOrderBys != null) {
+            return this.outputOrderBys;
+        }
+        return this.outputOrderBys = convertToOutputOrderBys();
+    }
+
+    private List<OrderBy> convertToOutputOrderBys() {
         assert this.groupBy == GroupBy.EMPTY_GROUP_BY;
         assert this.orderBy != OrderBy.FWD_ROW_KEY_ORDER_BY && this.orderBy != 
OrderBy.REV_ROW_KEY_ORDER_BY;
         if(!this.orderBy.isEmpty()) {
             return Collections.<OrderBy> singletonList(
                     
OrderBy.convertCompiledOrderByToOutputOrderBy(this.orderBy));
         }
+        if (this.supportOrderByOptimize) {
+            assert this.plans.size() > 0;
+            return this.plans.get(0).getOutputOrderBys();
+        }
         return Collections.<OrderBy> emptyList();
     }
 
+    private List<OrderByExpression> 
getOrderByExpressionsWhenSupportOrderByOptimize() {
+        assert this.supportOrderByOptimize;
+        assert this.plans.size() > 0;
+        assert this.orderBy.isEmpty();
+        List<OrderBy> outputOrderBys = this.plans.get(0).getOutputOrderBys();
+        assert outputOrderBys != null && outputOrderBys.size() == 1;
+        List<OrderByExpression> orderByExpressions = 
outputOrderBys.get(0).getOrderByExpressions();
+        assert orderByExpressions != null && orderByExpressions.size() > 0;
+        return 
orderByExpressions.stream().map(OrderByExpression::convertIfExpressionSortOrderDesc)
+            .collect(Collectors.toList());
+    }
+
     @Override
     public boolean isApplicable() {
         return true;
diff --git 
a/phoenix-core-client/src/main/java/org/apache/phoenix/expression/OrderByExpression.java
 
b/phoenix-core-client/src/main/java/org/apache/phoenix/expression/OrderByExpression.java
index 8f71de9c4f..0ea8ef4eb0 100644
--- 
a/phoenix-core-client/src/main/java/org/apache/phoenix/expression/OrderByExpression.java
+++ 
b/phoenix-core-client/src/main/java/org/apache/phoenix/expression/OrderByExpression.java
@@ -50,15 +50,18 @@ public class OrderByExpression implements Writable {
     }
 
     /**
-     * If {@link Expression#getSortOrder()} is {@link SortOrder#DESC},the 
isAscending of returned new is reversed,but isNullsLast is untouched.
+     * If {@link Expression#getSortOrder()} is {@link SortOrder#DESC}, reverse 
the isAscending, but isNullsLast is untouched.
      * @param orderByExpression
      * @return
      */
     public static OrderByExpression 
convertIfExpressionSortOrderDesc(OrderByExpression orderByExpression) {
-        return createByCheckIfExpressionSortOrderDesc(
+        if (orderByExpression.getExpression().getSortOrder() != 
SortOrder.DESC) {
+            return orderByExpression;
+        }
+        return new OrderByExpression(
                 orderByExpression.getExpression(),
                 orderByExpression.isNullsLast(),
-                orderByExpression.isAscending());
+                !orderByExpression.isAscending());
     }
 
     /**
diff --git 
a/phoenix-core-client/src/main/java/org/apache/phoenix/parse/OrderByNode.java 
b/phoenix-core-client/src/main/java/org/apache/phoenix/parse/OrderByNode.java
index 2451c4b4c9..39ddc8dbe1 100644
--- 
a/phoenix-core-client/src/main/java/org/apache/phoenix/parse/OrderByNode.java
+++ 
b/phoenix-core-client/src/main/java/org/apache/phoenix/parse/OrderByNode.java
@@ -18,6 +18,7 @@
 package org.apache.phoenix.parse;
 
 import org.apache.phoenix.compile.ColumnResolver;
+import org.apache.phoenix.schema.types.PInteger;
 
 
 /**
@@ -84,4 +85,16 @@ public final class OrderByNode {
         if (!orderAscending) buf.append(" DESC");
         if (nullsLast) buf.append(" NULLS LAST ");
     }
+
+    public boolean isIntegerLiteral() {
+        return child instanceof LiteralParseNode
+                && ((LiteralParseNode) child).getType() == PInteger.INSTANCE;
+    }
+
+    public Integer getValueIfIntegerLiteral() {
+        if (!isIntegerLiteral()) {
+            return null;
+        }
+        return (Integer) ((LiteralParseNode) child).getValue();
+    }
 }
diff --git 
a/phoenix-core-client/src/main/java/org/apache/phoenix/parse/SelectStatement.java
 
b/phoenix-core-client/src/main/java/org/apache/phoenix/parse/SelectStatement.java
index 53e82639f8..3446eb73bc 100644
--- 
a/phoenix-core-client/src/main/java/org/apache/phoenix/parse/SelectStatement.java
+++ 
b/phoenix-core-client/src/main/java/org/apache/phoenix/parse/SelectStatement.java
@@ -384,4 +384,16 @@ public class SelectStatement implements 
FilterableStatement {
         return offset;
     }
 
+    public boolean haveGroupBy() {
+        return this.getGroupBy() != null && this.getGroupBy().size() > 0
+                || !this.isAggregate()
+                        && this.isDistinct()
+                        && this.getSelect() != null
+                        && this.getSelect().size() > 0;
+    }
+
+    public boolean haveOrderBy() {
+        return this.getOrderBy() != null && this.getOrderBy().size() > 0;
+    }
+
 }
diff --git 
a/phoenix-core-client/src/main/java/org/apache/phoenix/util/ExpressionUtil.java 
b/phoenix-core-client/src/main/java/org/apache/phoenix/util/ExpressionUtil.java
index 64e6885c0a..bf84a4c413 100644
--- 
a/phoenix-core-client/src/main/java/org/apache/phoenix/util/ExpressionUtil.java
+++ 
b/phoenix-core-client/src/main/java/org/apache/phoenix/util/ExpressionUtil.java
@@ -244,7 +244,7 @@ public class ExpressionUtil {
 
     /**
      * <pre>
-     * Infer OrderBys from the rowkey columns of {@link PTable},for projected 
table may be there is no rowkey columns,
+     * Infer OrderBys from the rowkey columns of {@link PTable}, for projected 
table may be no rowkey columns,
      * so we should move forward to inspect {@link ProjectedColumn} by {@link 
#getOrderByFromProjectedTable}.
      * The second part of the return pair is the rowkey column offset we must 
skip when we create OrderBys, because for table with 
salted/multiTenant/viewIndexId,
      * some leading rowkey columns should be skipped.
@@ -302,7 +302,7 @@ public class ExpressionUtil {
     }
 
     /**
-     * For projected table may be there is no rowkey columns,
+     * For projected table may be no rowkey columns,
      * so we should move forward to inspect {@link ProjectedColumn} to check 
if the source column is rowkey column.
      * The second part of the return pair is the rowkey column offset we must 
skip when we create OrderBys, because for table with 
salted/multiTenant/viewIndexId,
      * some leading rowkey columns should be skipped.
diff --git 
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/DerivedTableIT.java 
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/DerivedTableIT.java
index 597e0fc6cb..b86b75cee1 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/DerivedTableIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/DerivedTableIT.java
@@ -129,7 +129,7 @@ public class DerivedTableIT extends ParallelStatsDisabledIT 
{
                 "CLIENT PARALLEL 1-WAY FULL SCAN OVER 
"+dynamicTableName+"_DERIVED_IDX \n" +
                         "    SERVER AGGREGATE INTO DISTINCT ROWS BY 
[\"A_STRING\", \"B_STRING\"]\n" +
                         "CLIENT MERGE SORT\n" +
-                        "CLIENT AGGREGATE INTO DISTINCT ROWS BY [A]\n" +
+                        "CLIENT AGGREGATE INTO ORDERED DISTINCT ROWS BY [A]\n" 
+
                         "CLIENT DISTINCT ON [COLLECTDISTINCT(B)]\n" +
                         "CLIENT SORTED BY [A DESC]"}});
         testCases.add(new String[][] {
@@ -145,7 +145,7 @@ public class DerivedTableIT extends ParallelStatsDisabledIT 
{
                 "CLIENT PARALLEL 4-WAY FULL SCAN OVER "+dynamicTableName+" \n" 
+
                         "    SERVER AGGREGATE INTO DISTINCT ROWS BY [A_STRING, 
B_STRING]\n" +
                         "CLIENT MERGE SORT\n" +
-                        "CLIENT AGGREGATE INTO DISTINCT ROWS BY [A]\n" +
+                        "CLIENT AGGREGATE INTO ORDERED DISTINCT ROWS BY [A]\n" 
+
                         "CLIENT DISTINCT ON [COLLECTDISTINCT(B)]\n" +
                         "CLIENT SORTED BY [A DESC]"}});
         return testCases;
diff --git 
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/SortMergeJoinMoreIT.java 
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SortMergeJoinMoreIT.java
index b7e52859bb..fe64d7dfc6 100644
--- 
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/SortMergeJoinMoreIT.java
+++ 
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SortMergeJoinMoreIT.java
@@ -443,7 +443,7 @@ public class SortMergeJoinMoreIT extends 
ParallelStatsDisabledIT {
                         "        SERVER AGGREGATE INTO ORDERED DISTINCT ROWS 
BY [BUCKET, \"TIMESTAMP\", SRC_LOCATION, DST_LOCATION]\n" +
                         "    CLIENT MERGE SORT\n" +
                         "    CLIENT SORTED BY [BUCKET, \"TIMESTAMP\"]\n" +
-                        "CLIENT AGGREGATE INTO DISTINCT ROWS BY [E.BUCKET, 
E.TIMESTAMP]"
+                        "CLIENT AGGREGATE INTO ORDERED DISTINCT ROWS BY 
[E.BUCKET, E.TIMESTAMP]"
                         :
                         "SORT-MERGE-JOIN (INNER) TABLES\n" +
                         "    CLIENT PARALLEL 2-WAY SKIP SCAN ON 2 RANGES OVER 
" + eventCountTableName + " [X'00','5SEC',~1462993520000000000,'Tr/Bal'] - 
[X'01','5SEC',~1462993420000000000,'Tr/Bal']\n" +
@@ -458,7 +458,7 @@ public class SortMergeJoinMoreIT extends 
ParallelStatsDisabledIT {
                         "        SERVER DISTINCT PREFIX FILTER OVER [BUCKET, 
\"TIMESTAMP\", SRC_LOCATION, DST_LOCATION]\n" +
                         "        SERVER AGGREGATE INTO ORDERED DISTINCT ROWS 
BY [BUCKET, \"TIMESTAMP\", SRC_LOCATION, DST_LOCATION]\n" +
                         "    CLIENT MERGE SORT\n" +
-                        "CLIENT AGGREGATE INTO DISTINCT ROWS BY [E.BUCKET, 
E.TIMESTAMP]";
+                        "CLIENT AGGREGATE INTO ORDERED DISTINCT ROWS BY 
[E.BUCKET, E.TIMESTAMP]";
                 
                 ResultSet rs = conn.createStatement().executeQuery("explain " 
+ q);
                 assertEquals(p, QueryUtil.getExplainPlan(rs));
diff --git 
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/UnionAllIT.java 
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/UnionAllIT.java
index 9b76b319db..b15f8c66c2 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/UnionAllIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/UnionAllIT.java
@@ -37,8 +37,10 @@ import org.apache.phoenix.compile.ExplainPlan;
 import org.apache.phoenix.compile.ExplainPlanAttributes;
 import org.apache.phoenix.exception.SQLExceptionCode;
 import org.apache.phoenix.jdbc.PhoenixPreparedStatement;
+import org.apache.phoenix.query.QueryServices;
 import org.apache.phoenix.util.PropertiesUtil;
 import org.apache.phoenix.util.QueryUtil;
+import org.apache.phoenix.util.TestUtil;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
 
@@ -1134,4 +1136,329 @@ public class UnionAllIT extends ParallelStatsDisabledIT 
{
             conn.close();
         }
     }
+
+    @Test
+    public void testOrderByOptimizeBug7397() throws Exception {
+        String tableName1 = generateUniqueName();
+        String tableName2 = generateUniqueName();
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        props.setProperty(QueryServices.FORCE_ROW_KEY_ORDER_ATTRIB, 
Boolean.toString(false));
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+
+        try {
+            String ddl = "create table " + tableName1 + "( "+
+                    " fuid UNSIGNED_LONG not null , " +
+                    " fstatsdate UNSIGNED_LONG not null, " +
+                    " fversion UNSIGNED_LONG not null," +
+                    " faid_1 UNSIGNED_LONG not null," +
+                    " clk_pv_1 UNSIGNED_LONG, " +
+                    " activation_pv_1 UNSIGNED_LONG, " +
+                    " CONSTRAINT TEST_PK PRIMARY KEY ( " +
+                    " fuid , " +
+                    " fstatsdate, " +
+                    " fversion, " +
+                    " faid_1 " +
+                    " ))";
+            createTestTable(getUrl(), ddl);
+            String dml = "UPSERT INTO " + tableName1 + " VALUES(?,?,?,?,?,?)";
+            PreparedStatement stmt = conn.prepareStatement(dml);
+            setValues(stmt, 1, 20240711, 1, 11, 1, 1);
+            stmt.execute();
+            setValues(stmt, 1, 20240712, 1, 22, 3, 3);
+            stmt.execute();
+            setValues(stmt, 1, 20240713, 1, 33, 7, 7);
+            stmt.execute();
+            conn.commit();
+
+            ddl = "create table " + tableName2 + "( "+
+                    " fuid UNSIGNED_LONG not null, " +
+                    " fstatsdate UNSIGNED_LONG not null, " +
+                    " fversion UNSIGNED_LONG not null," +
+                    " faid_2 UNSIGNED_LONG not null," +
+                    " clk_pv_2 UNSIGNED_LONG, " +
+                    " activation_pv_2 UNSIGNED_LONG, " +
+                    " CONSTRAINT TEST_PK PRIMARY KEY ( " +
+                    " fuid , " +
+                    " fstatsdate, " +
+                    " fversion," +
+                    " faid_2 " +
+                    " ))";
+            createTestTable(getUrl(), ddl);
+            dml = "UPSERT INTO " + tableName2 + " VALUES(?,?,?,?,?,?)";
+            stmt = conn.prepareStatement(dml);
+            setValues(stmt, 1, 20240711, 1, 11, 6, 6);
+            stmt.execute();
+            setValues(stmt, 1, 20240712, 1, 22, 2, 2);
+            stmt.execute();
+            setValues(stmt, 1, 20240713, 1, 33, 4, 4);
+            stmt.execute();
+            setValues(stmt, 1, 20240710, 1, 22, 5, 5);
+            stmt.execute();
+            conn.commit();
+
+            String orderedUnionSql = "(SELECT FUId AS advertiser_id,"
+                    + "   FAId_1 AS adgroup_id,"
+                    + "   FStatsDate AS date,"
+                    + "   SUM(clk_pv_1) AS valid_click_count,"
+                    + "   SUM(activation_pv_1) AS activated_count"
+                    + "  FROM " + tableName1
+                    + "  WHERE (FVersion = 1) AND (FUId IN (1)) AND (FAId_1 IN 
(11, 22, 33, 10))"
+                    + "  AND (FStatsDate >= 20240710) AND (FStatsDate <= 
20240718)"
+                    + "  GROUP BY FUId, FAId_1, FStatsDate"
+                    + "  UNION ALL "
+                    + "  SELECT "
+                    + "  FUId AS advertiser_id,"
+                    + "  FAId_2 AS adgroup_id,"
+                    + "  FStatsDate AS date,"
+                    + "  SUM(clk_pv_2) AS valid_click_count,"
+                    + "  SUM(activation_pv_2) AS activated_count"
+                    + "  FROM " + tableName2
+                    + "  WHERE (FVersion = 1) AND (FUId IN (1)) AND (FAId_2 IN 
(11, 22, 33, 10))"
+                    + "  AND (FStatsDate >= 20240710) AND (FStatsDate <= 
20240718)"
+                    + "  GROUP BY FUId, FAId_2, FStatsDate"
+                    + ")";
+
+            //Test group by orderPreserving
+            String sql = "SELECT ADVERTISER_ID AS advertiser_id,"
+                    + "ADGROUP_ID AS adgroup_id,"
+                    + "DATE AS i_date,"
+                    + "SUM(VALID_CLICK_COUNT) AS valid_click_count,"
+                    + "SUM(ACTIVATED_COUNT) AS activated_count "
+                    + "FROM "
+                    + orderedUnionSql
+                    + "GROUP BY ADVERTISER_ID, ADGROUP_ID, I_DATE "
+                    + "ORDER BY advertiser_id, adgroup_id, i_date "
+                    + "limit 10";
+            ResultSet rs = conn.createStatement().executeQuery(sql);
+            TestUtil.assertResultSet(rs, new Long[][]{
+                {1L, 11L, 20240711L, 7L, 7L},
+                {1L, 22L, 20240710L, 5L, 5L},
+                {1L, 22L, 20240712L, 5L, 5L},
+                {1L, 33L, 20240713L, 11L, 11L}});
+
+            //Test group by not orderPreserving
+            sql = "SELECT ADVERTISER_ID AS i_advertiser_id,"
+                    + "ADGROUP_ID AS i_adgroup_id,"
+                    + "SUM(VALID_CLICK_COUNT) AS valid_click_count,"
+                    + "SUM(ACTIVATED_COUNT) AS activated_count "
+                    + "FROM "
+                    + orderedUnionSql
+                    + "GROUP BY I_ADVERTISER_ID, ADGROUP_ID "
+                    + "ORDER BY i_adgroup_id "
+                    + "limit 10";
+            rs = conn.createStatement().executeQuery(sql);
+            TestUtil.assertResultSet(rs, new Long[][]{
+                {1L, 11L, 7L, 7L},
+                {1L, 22L, 10L, 10L},
+                {1L, 33L, 11L, 11L}});
+
+            //Test group by not orderPreserving
+            sql = "SELECT ADGROUP_ID AS adgroup_id,"
+                    + "DATE AS i_date,"
+                    + "SUM(VALID_CLICK_COUNT) AS valid_click_count,"
+                    + "SUM(ACTIVATED_COUNT) AS activated_count "
+                    + "FROM "
+                    + orderedUnionSql
+                    + "GROUP BY ADGROUP_ID, I_DATE "
+                    + "ORDER BY adgroup_id, i_date "
+                    + "limit 10";
+            rs = conn.createStatement().executeQuery(sql);
+            TestUtil.assertResultSet(rs, new Long[][]{
+                {11L, 20240711L, 7L, 7L},
+                {22L, 20240710L, 5L, 5L},
+                {22L, 20240712L, 5L, 5L},
+                {33L, 20240713L, 11L, 11L}});
+
+            //Test group by orderPreserving with where
+            sql = "SELECT ADGROUP_ID AS adgroup_id,"
+                    + "DATE AS i_date,"
+                    + "SUM(VALID_CLICK_COUNT) AS valid_click_count,"
+                    + "SUM(ACTIVATED_COUNT) AS activated_count "
+                    + "FROM "
+                    + orderedUnionSql
+                    + " where advertiser_id = 1 "
+                    + "GROUP BY ADGROUP_ID, I_DATE "
+                    + "ORDER BY adgroup_id, i_date "
+                    + "limit 10";
+            rs = conn.createStatement().executeQuery(sql);
+            TestUtil.assertResultSet(rs, new Long[][]{
+                {11L, 20240711L, 7L, 7L},
+                {22L, 20240710L, 5L, 5L},
+                {22L, 20240712L, 5L, 5L},
+                {33L, 20240713L, 11L, 11L}});
+
+            //Test order by orderPreserving
+            sql ="SELECT ADVERTISER_ID AS advertiser_id,"
+                    + "ADGROUP_ID AS adgroup_id,"
+                    + "DATE AS i_date,"
+                    + "VALID_CLICK_COUNT AS valid_click_count,"
+                    + "ACTIVATED_COUNT AS activated_count "
+                    + "FROM "
+                    + orderedUnionSql
+                    + " where valid_click_count in (1, 5, 2, 4) "
+                    + "ORDER BY advertiser_id, i_date, adgroup_id "
+                    + "limit 10";
+            rs = conn.createStatement().executeQuery(sql);
+            TestUtil.assertResultSet(rs, new Long[][]{
+                {1L, 22L, 20240710L, 5L, 5L},
+                {1L, 11L, 20240711L, 1L, 1L},
+                {1L, 22L, 20240712L, 2L, 2L},
+                {1L, 33L, 20240713L, 4L, 4L}});
+
+            //Test order by not orderPreserving
+            sql ="SELECT ADVERTISER_ID AS advertiser_id,"
+                    + "ADGROUP_ID AS i_adgroup_id,"
+                    + "DATE AS date,"
+                    + "VALID_CLICK_COUNT AS valid_click_count,"
+                    + "ACTIVATED_COUNT AS activated_count "
+                    + "FROM "
+                    + orderedUnionSql
+                    + "ORDER BY advertiser_id, i_adgroup_id, date, 
valid_click_count "
+                    + "limit 10";
+            rs = conn.createStatement().executeQuery(sql);
+            TestUtil.assertResultSet(rs, new Long[][]{
+                {1L, 11L, 20240711L, 1L, 1L},
+                {1L, 11L, 20240711L, 6L, 6L},
+                {1L, 22L, 20240710L, 5L, 5L},
+                {1L, 22L, 20240712L, 2L, 2L},
+                {1L, 22L, 20240712L, 3L, 3L},
+                {1L, 33L, 20240713L, 4L, 4L},
+                {1L, 33L, 20240713L, 7L, 7L}});
+
+            //Test there is no order in union
+            sql ="SELECT ADVERTISER_ID AS advertiser_id,"
+                    + "ADGROUP_ID AS adgroup_id,"
+                    + "DATE AS i_date,"
+                    + "SUM(VALID_CLICK_COUNT) AS valid_click_count,"
+                    + "SUM(ACTIVATED_COUNT) AS activated_count "
+                    + "FROM "
+                    + "(SELECT FUId AS advertiser_id,"
+                    + "   FAId_1 AS adgroup_id,"
+                    + "   FStatsDate AS date,"
+                    + "   clk_pv_1 AS valid_click_count,"
+                    + "   activation_pv_1 AS activated_count"
+                    + "  FROM " + tableName1
+                    + "  WHERE (FVersion = 1) AND (FUId IN (1)) AND (FAId_1 IN 
(11, 22, 33, 10))"
+                    + "  AND (FStatsDate >= 20240710) AND (FStatsDate <= 
20240718)"
+                    + "  UNION ALL "
+                    + "  SELECT "
+                    + "  FUId AS advertiser_id,"
+                    + "  FAId_2 AS adgroup_id,"
+                    + "  FStatsDate AS date,"
+                    + "  clk_pv_2 AS valid_click_count,"
+                    + "  activation_pv_2 AS activated_count"
+                    + "  FROM " + tableName2
+                    + "  WHERE (FVersion = 1) AND (FUId IN (1)) AND (FAId_2 IN 
(11, 22, 33, 10))"
+                    + "  AND (FStatsDate >= 20240710) AND (FStatsDate <= 
20240718)"
+                    + ")"
+                    + "GROUP BY ADVERTISER_ID, ADGROUP_ID, I_DATE "
+                    + "ORDER BY advertiser_id, adgroup_id, i_date "
+                    + "limit 10";
+            rs = conn.createStatement().executeQuery(sql);
+            TestUtil.assertResultSet(rs, new Long[][]{
+                {1L, 11L, 20240711L, 7L, 7L},
+                {1L, 22L, 20240710L, 5L, 5L},
+                {1L, 22L, 20240712L, 5L, 5L},
+                {1L, 33L, 20240713L, 11L, 11L}});
+
+            //Test alias not inconsistent in union
+            sql ="SELECT ADVERTISER_ID AS advertiser_id,"
+                    + "ADGROUP_ID_1 AS adgroup_id,"
+                    + "DATE AS i_date,"
+                    + "SUM(VALID_CLICK_COUNT) AS valid_click_count,"
+                    + "SUM(ACTIVATED_COUNT) AS activated_count "
+                    + "FROM "
+                    + "(SELECT FUId AS advertiser_id,"
+                    + "   FAId_1 AS adgroup_id_1,"
+                    + "   FStatsDate AS date,"
+                    + "   SUM(clk_pv_1) AS valid_click_count,"
+                    + "   SUM(activation_pv_1) AS activated_count"
+                    + "  FROM " + tableName1
+                    + "  WHERE (FVersion = 1) AND (FUId IN (1)) AND (FAId_1 IN 
(11, 22, 33, 10))"
+                    + "  AND (FStatsDate >= 20240710) AND (FStatsDate <= 
20240718)"
+                    + "  GROUP BY FUId, FAId_1, FStatsDate"
+                    + "  UNION ALL "
+                    + "  SELECT "
+                    + "  FUId AS advertiser_id,"
+                    + "  FAId_2,"
+                    + "  FStatsDate AS date,"
+                    + "  SUM(clk_pv_2),"
+                    + "  SUM(activation_pv_2)"
+                    + "  FROM " + tableName2
+                    + "  WHERE (FVersion = 1) AND (FUId IN (1)) AND (FAId_2 IN 
(11, 22, 33, 10))"
+                    + "  AND (FStatsDate >= 20240710) AND (FStatsDate <= 
20240718)"
+                    + "  GROUP BY FUId, FAId_2, FStatsDate"
+                    + ")"
+                    + "GROUP BY ADVERTISER_ID, ADGROUP_ID_1, I_DATE "
+                    + "ORDER BY advertiser_id, adgroup_id, i_date "
+                    + "limit 10";
+            rs = conn.createStatement().executeQuery(sql);
+            TestUtil.assertResultSet(rs, new Long[][]{
+                {1L, 11L, 20240711L, 7L, 7L},
+                {1L, 22L, 20240710L, 5L, 5L},
+                {1L, 22L, 20240712L, 5L, 5L},
+                {1L, 33L, 20240713L, 11L, 11L}});
+
+            //Test order by column not equals in union
+            sql = "SELECT ADVERTISER_ID AS advertiser_id,"
+                    + "ADGROUP_ID AS adgroup_id,"
+                    + "DATE AS i_date,"
+                    + "SUM(VALID_CLICK_COUNT) AS valid_click_count,"
+                    + "SUM(ACTIVATED_COUNT) AS activated_count "
+                    + "FROM "
+                    + "(SELECT FUId AS advertiser_id,"
+                    + "   FAId_1 AS adgroup_id,"
+                    + "   FStatsDate AS date,"
+                    + "   SUM(clk_pv_1) AS valid_click_count,"
+                    + "   SUM(activation_pv_1) AS activated_count"
+                    + "  FROM " + tableName1
+                    + "  WHERE (FVersion = 1) AND (FUId IN (1)) AND (FAId_1 IN 
(11, 22, 33, 10))"
+                    + "  AND (FStatsDate >= 20240710) AND (FStatsDate <= 
20240718)"
+                    + "  GROUP BY FUId, FAId_1, FStatsDate"
+                    + "  UNION ALL "
+                    + "  SELECT "
+                    + "  FUId AS advertiser_id,"
+                    + "  FAId_2 AS adgroup_id,"
+                    + "  cast (0 as UNSIGNED_LONG)  AS date,"
+                    + "  SUM(clk_pv_2) AS valid_click_count,"
+                    + "  SUM(activation_pv_2) AS activated_count"
+                    + "  FROM " + tableName2
+                    + "  WHERE (FVersion = 1) AND (FUId IN (1)) AND (FAId_2 IN 
(11, 22, 33, 10))"
+                    + "  AND (FStatsDate >= 20240710) AND (FStatsDate <= 
20240718)"
+                    + "  GROUP BY FUId, FAId_2"
+                    + ")"
+                    + "GROUP BY ADVERTISER_ID, ADGROUP_ID, I_DATE "
+                    + "ORDER BY advertiser_id, adgroup_id, i_date "
+                    + "limit 10";
+            rs = conn.createStatement().executeQuery(sql);
+            TestUtil.assertResultSet(rs, new Long[][]{
+                {1L, 11L, 0L, 6L, 6L},
+                {1L, 11L, 20240711L, 1L, 1L},
+                {1L, 22L, 0L, 7L, 7L},
+                {1L, 22L, 20240712L, 3L, 3L},
+                {1L, 33L, 0L, 4L, 4L},
+                {1L, 33L, 20240713L, 7L, 7L}});
+
+            //Test only union and order by not match
+            sql = orderedUnionSql.substring(1, orderedUnionSql.length()-1) +
+                    " order by advertiser_id, date, adgroup_id, 
valid_click_count";
+            rs = conn.createStatement().executeQuery(sql);
+            TestUtil.assertResultSet(rs, new Long[][]{
+                {1L, 22L, 20240710L, 5L, 5L},
+                {1L, 11L, 20240711L, 1L, 1L},
+                {1L, 11L, 20240711L, 6L, 6L},
+                {1L, 22L, 20240712L, 2L, 2L},
+                {1L, 22L, 20240712L, 3L, 3L},
+                {1L, 33L, 20240713L, 4L, 4L},
+                {1L, 33L, 20240713L, 7L, 7L}});
+        } finally {
+            conn.close();
+        }
+    }
+
+    private static void setValues(PreparedStatement stmt, int... values) 
throws SQLException {
+        for(int i = 0; i < values.length; i++) {
+            stmt.setLong(i + 1, values[i]);
+        }
+    }
 }
diff --git 
a/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryCompilerTest.java 
b/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryCompilerTest.java
index 0e32be630f..bb838b9956 100644
--- 
a/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryCompilerTest.java
+++ 
b/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryCompilerTest.java
@@ -80,6 +80,7 @@ import 
org.apache.phoenix.expression.aggregator.CountAggregator;
 import org.apache.phoenix.expression.aggregator.ServerAggregators;
 import org.apache.phoenix.expression.function.TimeUnit;
 import org.apache.phoenix.filter.EncodedQualifiersColumnProjectionFilter;
+import org.apache.phoenix.iterate.MergeSortTopNResultIterator;
 import org.apache.phoenix.jdbc.PhoenixConnection;
 import org.apache.phoenix.jdbc.PhoenixPreparedStatement;
 import org.apache.phoenix.jdbc.PhoenixStatement;
@@ -6863,7 +6864,6 @@ public class QueryCompilerTest extends 
BaseConnectionlessQueryTest {
                     "(SELECT 1 FROM " + orderTableName + " o  where o.price > 
8) ORDER BY name";
             queryPlan= TestUtil.getOptimizeQueryPlanNoIterator(conn, sql);
             assertTrue(queryPlan instanceof HashJoinPlan);
-            System.out.println(queryPlan.getStatement());
             TestUtil.assertSelectStatement(
                     queryPlan.getStatement(),
                     "SELECT ITEM_ID,NAME FROM ITEM_TABLE I  WHERE  EXISTS 
(SELECT 1 FROM ORDER_TABLE O  WHERE O.PRICE > 8 LIMIT 1) ORDER BY NAME");
@@ -7410,4 +7410,368 @@ public class QueryCompilerTest extends 
BaseConnectionlessQueryTest {
         assertEquals(isNullsLast, orderByExpression.isNullsLast());
         assertEquals(isAscending, orderByExpression.isAscending());
     }
+
+    @Test
+    public void testUnionAllOrderByOptimizeBug7397() throws Exception {
+        Properties props = new Properties();
+        props.setProperty(QueryServices.FORCE_ROW_KEY_ORDER_ATTRIB, 
Boolean.toString(false));
+        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
+            String tableName1 = generateUniqueName();
+            String sql1 = "create table " + tableName1 + "( "+
+                    " fuid UNSIGNED_LONG not null , " +
+                    " fstatsdate UNSIGNED_LONG not null, " +
+                    " fversion UNSIGNED_LONG not null," +
+                    " faid_1 UNSIGNED_LONG not null," +
+                    " clk_pv_1 UNSIGNED_LONG, " +
+                    " activation_pv_1 UNSIGNED_LONG, " +
+                    " CONSTRAINT TEST_PK PRIMARY KEY ( " +
+                    " fuid , " +
+                    " fstatsdate, " +
+                    " fversion, " +
+                    " faid_1 " +
+                    " ))";
+            conn.createStatement().execute(sql1);
+
+            String tableName2= generateUniqueName();
+            String sql2 = "create table " + tableName2 + "( "+
+                    " fuid UNSIGNED_LONG not null , " +
+                    " fstatsdate UNSIGNED_LONG not null, " +
+                    " fversion UNSIGNED_LONG not null," +
+                    " faid_2 UNSIGNED_LONG not null," +
+                    " clk_pv_2 UNSIGNED_LONG, " +
+                    " activation_pv_2 UNSIGNED_LONG, " +
+                    " CONSTRAINT TEST_PK PRIMARY KEY ( " +
+                    " fuid , " +
+                    " fstatsdate, " +
+                    " fversion," +
+                    " faid_2 " +
+                    " ))";
+            conn.createStatement().execute(sql2);
+
+            String orderedUnionSql =
+                    "(SELECT FUId AS advertiser_id,"
+                    + "   FAId_1 AS adgroup_id,"
+                    + "   FStatsDate AS date,"
+                    + "   SUM(clk_pv_1) AS valid_click_count,"
+                    + "   SUM(activation_pv_1) AS activated_count"
+                    + "  FROM " + tableName1
+                    + "  WHERE (FVersion = 1) AND (FUId IN (1)) AND (FAId_1 IN 
(11, 22, 33, 10))"
+                    + "  AND (FStatsDate >= 20240710) AND (FStatsDate <= 
20240718)"
+                    + "  GROUP BY FUId, FAId_1, FStatsDate"
+                    + "  UNION ALL "
+                    + "  SELECT "
+                    + "  FUId AS advertiser_id,"
+                    + "  FAId_2 AS adgroup_id,"
+                    + "  FStatsDate AS date,"
+                    + "  SUM(clk_pv_2) AS valid_click_count,"
+                    + "  SUM(activation_pv_2) AS activated_count"
+                    + "  FROM " + tableName2
+                    + "  WHERE (FVersion = 1) AND (FUId IN (1)) AND (FAId_2 IN 
(11, 22, 33, 10))"
+                    + "  AND (FStatsDate >= 20240710) AND (FStatsDate <= 
20240718)"
+                    + "  GROUP BY FUId, FAId_2, FStatsDate"
+                    + ")";
+
+            //Test group by orderPreserving
+            String sql = "SELECT ADVERTISER_ID AS advertiser_id,"
+                    + "ADGROUP_ID AS adgroup_id,"
+                    + "DATE AS i_date,"
+                    + "SUM(VALID_CLICK_COUNT) AS valid_click_count,"
+                    + "SUM(ACTIVATED_COUNT) AS activated_count "
+                    + "FROM "
+                    + orderedUnionSql
+                    + "GROUP BY ADVERTISER_ID, ADGROUP_ID, I_DATE "
+                    + "ORDER BY advertiser_id, adgroup_id, i_date "
+                    + "limit 10";
+            ClientAggregatePlan plan 
=(ClientAggregatePlan)TestUtil.getOptimizeQueryPlan(conn, sql);
+            assertTrue(plan.getGroupBy().isOrderPreserving());
+            UnionPlan unionPlan = 
(UnionPlan)((TupleProjectionPlan)(plan.getDelegate())).getDelegate();
+            assertTrue(unionPlan.isSupportOrderByOptimize());
+            assertTrue(unionPlan.iterator() instanceof 
MergeSortTopNResultIterator);
+            List<OrderBy> orderBys = unionPlan.getOutputOrderBys();
+            assertTrue(orderBys.size() == 1);
+            OrderBy orderBy = orderBys.get(0);
+            assertTrue(orderBy.getOrderByExpressions().size() == 3);
+            
assertTrue(orderBy.getOrderByExpressions().get(0).toString().equals("ADVERTISER_ID"));
+            
assertTrue(orderBy.getOrderByExpressions().get(1).toString().equals("DATE"));
+            
assertTrue(orderBy.getOrderByExpressions().get(2).toString().equals("ADGROUP_ID"));
+
+            //Test group by orderPreserving for distinct
+            sql = "SELECT distinct ADVERTISER_ID AS advertiser_id,"
+                    + "ADGROUP_ID AS adgroup_id,"
+                    + "DATE AS i_date "
+                    + "FROM "
+                    + orderedUnionSql
+                    + "ORDER BY advertiser_id, adgroup_id, i_date "
+                    + "limit 10";
+            plan =(ClientAggregatePlan)TestUtil.getOptimizeQueryPlan(conn, 
sql);
+            assertTrue(plan.getGroupBy().isOrderPreserving());
+            unionPlan = 
(UnionPlan)((TupleProjectionPlan)(plan.getDelegate())).getDelegate();
+            assertTrue(unionPlan.isSupportOrderByOptimize());
+            assertTrue(unionPlan.iterator() instanceof 
MergeSortTopNResultIterator);
+            orderBys = unionPlan.getOutputOrderBys();
+            assertTrue(orderBys.size() == 1);
+            orderBy = orderBys.get(0);
+            assertTrue(orderBy.getOrderByExpressions().size() == 3);
+            
assertTrue(orderBy.getOrderByExpressions().get(0).toString().equals("ADVERTISER_ID"));
+            
assertTrue(orderBy.getOrderByExpressions().get(1).toString().equals("DATE"));
+            
assertTrue(orderBy.getOrderByExpressions().get(2).toString().equals("ADGROUP_ID"));
+
+            //Test group by not orderPreserving
+            sql = "SELECT ADVERTISER_ID AS i_advertiser_id,"
+                    + "ADGROUP_ID AS i_adgroup_id,"
+                    + "SUM(VALID_CLICK_COUNT) AS valid_click_count,"
+                    + "SUM(ACTIVATED_COUNT) AS activated_count "
+                    + "FROM "
+                    + orderedUnionSql
+                    + "GROUP BY I_ADVERTISER_ID, ADGROUP_ID "
+                    + "ORDER BY i_adgroup_id "
+                    + "limit 10";
+            plan =(ClientAggregatePlan)TestUtil.getOptimizeQueryPlan(conn, 
sql);
+            assertTrue(!plan.getGroupBy().isOrderPreserving());
+            assertTrue(plan.getOrderBy() != OrderBy.FWD_ROW_KEY_ORDER_BY);
+            unionPlan = 
(UnionPlan)((TupleProjectionPlan)(plan.getDelegate())).getDelegate();
+            assertTrue(!unionPlan.isSupportOrderByOptimize());
+            assertTrue(!(unionPlan.iterator() instanceof 
MergeSortTopNResultIterator));
+            assertTrue(unionPlan.getOutputOrderBys().isEmpty());
+
+            //Test group by not orderPreserving
+            sql = "SELECT ADGROUP_ID AS adgroup_id,"
+                    + "DATE AS i_date,"
+                    + "SUM(VALID_CLICK_COUNT) AS valid_click_count,"
+                    + "SUM(ACTIVATED_COUNT) AS activated_count "
+                    + "FROM "
+                    + orderedUnionSql
+                    + "GROUP BY ADGROUP_ID, I_DATE "
+                    + "ORDER BY adgroup_id, i_date "
+                    + "limit 10";
+            plan =(ClientAggregatePlan)TestUtil.getOptimizeQueryPlan(conn, 
sql);
+            assertTrue(!plan.getGroupBy().isOrderPreserving());
+            assertTrue(plan.getOrderBy() == OrderBy.FWD_ROW_KEY_ORDER_BY);
+            unionPlan = 
(UnionPlan)((TupleProjectionPlan)(plan.getDelegate())).getDelegate();
+            assertTrue(!unionPlan.isSupportOrderByOptimize());
+            assertTrue(!(unionPlan.iterator() instanceof 
MergeSortTopNResultIterator));
+            assertTrue(unionPlan.getOutputOrderBys().isEmpty());
+
+            //Test group by orderPreserving with where
+            sql = "SELECT ADGROUP_ID AS adgroup_id,"
+                    + "DATE AS i_date,"
+                    + "SUM(VALID_CLICK_COUNT) AS valid_click_count,"
+                    + "SUM(ACTIVATED_COUNT) AS activated_count "
+                    + "FROM "
+                    + orderedUnionSql
+                    + " where advertiser_id = 1 "
+                    + "GROUP BY ADGROUP_ID, I_DATE "
+                    + "ORDER BY adgroup_id, i_date "
+                    + "limit 10";
+            plan =(ClientAggregatePlan)TestUtil.getOptimizeQueryPlan(conn, 
sql);
+            assertTrue(plan.getGroupBy().isOrderPreserving());
+            unionPlan = 
(UnionPlan)((TupleProjectionPlan)(plan.getDelegate())).getDelegate();
+            assertTrue(unionPlan.isSupportOrderByOptimize());
+            assertTrue(unionPlan.iterator() instanceof 
MergeSortTopNResultIterator);
+            orderBys = unionPlan.getOutputOrderBys();
+            assertTrue(orderBys.size() == 1);
+            orderBy = orderBys.get(0);
+            assertTrue(orderBy.getOrderByExpressions().size() == 3);
+            
assertTrue(orderBy.getOrderByExpressions().get(0).toString().equals("ADVERTISER_ID"));
+            
assertTrue(orderBy.getOrderByExpressions().get(1).toString().equals("DATE"));
+            
assertTrue(orderBy.getOrderByExpressions().get(2).toString().equals("ADGROUP_ID"));
+
+            //Test order by orderPreserving
+            sql ="SELECT ADVERTISER_ID AS advertiser_id,"
+                    + "ADGROUP_ID AS adgroup_id,"
+                    + "DATE AS i_date,"
+                    + "VALID_CLICK_COUNT AS valid_click_count,"
+                    + "ACTIVATED_COUNT AS activated_count "
+                    + "FROM "
+                    + orderedUnionSql
+                    + "ORDER BY advertiser_id, i_date, adgroup_id "
+                    + "limit 10";
+            ClientScanPlan scanPlan 
=(ClientScanPlan)TestUtil.getOptimizeQueryPlan(conn, sql);
+            assertTrue(scanPlan.getOrderBy() == OrderBy.FWD_ROW_KEY_ORDER_BY);
+            unionPlan = 
(UnionPlan)((TupleProjectionPlan)(scanPlan.getDelegate())).getDelegate();
+            assertTrue(unionPlan.isSupportOrderByOptimize());
+            assertTrue(unionPlan.iterator() instanceof 
MergeSortTopNResultIterator);
+            orderBys = unionPlan.getOutputOrderBys();
+            assertTrue(orderBys.size() == 1);
+            orderBy = orderBys.get(0);
+            assertTrue(orderBy.getOrderByExpressions().size() == 3);
+            
assertTrue(orderBy.getOrderByExpressions().get(0).toString().equals("ADVERTISER_ID"));
+            
assertTrue(orderBy.getOrderByExpressions().get(1).toString().equals("DATE"));
+            
assertTrue(orderBy.getOrderByExpressions().get(2).toString().equals("ADGROUP_ID"));
+
+            //Test order by not orderPreserving
+            sql ="SELECT ADVERTISER_ID AS advertiser_id,"
+                    + "ADGROUP_ID AS i_adgroup_id,"
+                    + "DATE AS date,"
+                    + "VALID_CLICK_COUNT AS valid_click_count,"
+                    + "ACTIVATED_COUNT AS activated_count "
+                    + "FROM "
+                    + orderedUnionSql
+                    + "ORDER BY advertiser_id, i_adgroup_id, date, 
valid_click_count "
+                    + "limit 10";
+            scanPlan =(ClientScanPlan)TestUtil.getOptimizeQueryPlan(conn, sql);
+            assertTrue(!scanPlan.getOrderBy().isEmpty());
+            unionPlan = 
(UnionPlan)((TupleProjectionPlan)(scanPlan.getDelegate())).getDelegate();
+            assertTrue(!unionPlan.isSupportOrderByOptimize());
+            assertTrue(!(unionPlan.iterator() instanceof 
MergeSortTopNResultIterator));
+            assertTrue(unionPlan.getOutputOrderBys().isEmpty());
+
+            //Test there is no order in union
+            sql ="SELECT ADVERTISER_ID AS advertiser_id,"
+                    + "ADGROUP_ID AS adgroup_id,"
+                    + "DATE AS i_date,"
+                    + "SUM(VALID_CLICK_COUNT) AS valid_click_count,"
+                    + "SUM(ACTIVATED_COUNT) AS activated_count "
+                    + "FROM "
+                    + "(SELECT FUId AS advertiser_id,"
+                    + "   FAId_1 AS adgroup_id,"
+                    + "   FStatsDate AS date,"
+                    + "   clk_pv_1 AS valid_click_count,"
+                    + "   activation_pv_1 AS activated_count"
+                    + "  FROM " + tableName1
+                    + "  WHERE (FVersion = 1) AND (FUId IN (1)) AND (FAId_1 IN 
(11, 22, 33, 10))"
+                    + "  AND (FStatsDate >= 20240710) AND (FStatsDate <= 
20240718)"
+                    + "  UNION ALL "
+                    + "  SELECT "
+                    + "  FUId AS advertiser_id,"
+                    + "  FAId_2 AS adgroup_id,"
+                    + "  FStatsDate AS date,"
+                    + "  clk_pv_2 AS valid_click_count,"
+                    + "  activation_pv_2 AS activated_count"
+                    + "  FROM " + tableName2
+                    + "  WHERE (FVersion = 1) AND (FUId IN (1)) AND (FAId_2 IN 
(11, 22, 33, 10))"
+                    + "  AND (FStatsDate >= 20240710) AND (FStatsDate <= 
20240718)"
+                    + ")"
+                    + "GROUP BY ADVERTISER_ID, ADGROUP_ID, I_DATE "
+                    + "ORDER BY advertiser_id, adgroup_id, i_date "
+                    + "limit 10";
+            plan =(ClientAggregatePlan)TestUtil.getOptimizeQueryPlan(conn, 
sql);
+            assertTrue(!plan.getGroupBy().isOrderPreserving());
+            unionPlan = 
(UnionPlan)((TupleProjectionPlan)(plan.getDelegate())).getDelegate();
+            assertTrue(!unionPlan.isSupportOrderByOptimize());
+            assertTrue(!(unionPlan.iterator() instanceof 
MergeSortTopNResultIterator));
+            assertTrue(unionPlan.getOutputOrderBys().isEmpty());
+
+            //Test alias not inconsistent in union
+            sql ="SELECT ADVERTISER_ID AS advertiser_id,"
+                    + "ADGROUP_ID_1 AS adgroup_id,"
+                    + "DATE AS i_date,"
+                    + "SUM(VALID_CLICK_COUNT) AS valid_click_count,"
+                    + "SUM(ACTIVATED_COUNT) AS activated_count "
+                    + "FROM "
+                    + "(SELECT FUId AS advertiser_id,"
+                    + "   FAId_1 AS adgroup_id_1,"
+                    + "   FStatsDate AS date,"
+                    + "   SUM(clk_pv_1) AS valid_click_count,"
+                    + "   SUM(activation_pv_1) AS activated_count"
+                    + "  FROM " + tableName1
+                    + "  WHERE (FVersion = 1) AND (FUId IN (1)) AND (FAId_1 IN 
(11, 22, 33, 10))"
+                    + "  AND (FStatsDate >= 20240710) AND (FStatsDate <= 
20240718)"
+                    + "  GROUP BY FUId, FAId_1, FStatsDate"
+                    + "  UNION ALL "
+                    + "  SELECT "
+                    + "  FUId AS advertiser_id,"
+                    + "  FAId_2,"
+                    + "  FStatsDate AS date,"
+                    + "  SUM(clk_pv_2),"
+                    + "  SUM(activation_pv_2)"
+                    + "  FROM " + tableName2
+                    + "  WHERE (FVersion = 1) AND (FUId IN (1)) AND (FAId_2 IN 
(11, 22, 33, 10))"
+                    + "  AND (FStatsDate >= 20240710) AND (FStatsDate <= 
20240718)"
+                    + "  GROUP BY FUId, FAId_2, FStatsDate"
+                    + ")"
+                    + "GROUP BY ADVERTISER_ID, ADGROUP_ID_1, I_DATE "
+                    + "ORDER BY advertiser_id, adgroup_id, i_date "
+                    + "limit 10";
+            plan =(ClientAggregatePlan)TestUtil.getOptimizeQueryPlan(conn, 
sql);
+            assertTrue(plan.getGroupBy().isOrderPreserving());
+            unionPlan = 
(UnionPlan)((TupleProjectionPlan)(plan.getDelegate())).getDelegate();
+            assertTrue(unionPlan.isSupportOrderByOptimize());
+            assertTrue(unionPlan.iterator() instanceof 
MergeSortTopNResultIterator);
+            orderBys = unionPlan.getOutputOrderBys();
+            assertTrue(orderBys.size() == 1);
+            orderBy = orderBys.get(0);
+            assertTrue(orderBy.getOrderByExpressions().size() == 3);
+            
assertTrue(orderBy.getOrderByExpressions().get(0).toString().equals("ADVERTISER_ID"));
+            
assertTrue(orderBy.getOrderByExpressions().get(1).toString().equals("DATE"));
+            
assertTrue(orderBy.getOrderByExpressions().get(2).toString().equals("ADGROUP_ID_1"));
+
+            //Test order by column not equals in union
+            sql = "SELECT ADVERTISER_ID AS advertiser_id,"
+                    + "ADGROUP_ID AS adgroup_id,"
+                    + "DATE AS i_date,"
+                    + "SUM(VALID_CLICK_COUNT) AS valid_click_count,"
+                    + "SUM(ACTIVATED_COUNT) AS activated_count "
+                    + "FROM "
+                    + "(SELECT FUId AS advertiser_id,"
+                    + "   FAId_1 AS adgroup_id,"
+                    + "   FStatsDate AS date,"
+                    + "   SUM(clk_pv_1) AS valid_click_count,"
+                    + "   SUM(activation_pv_1) AS activated_count"
+                    + "  FROM " + tableName1
+                    + "  WHERE (FVersion = 1) AND (FUId IN (1)) AND (FAId_1 IN 
(11, 22, 33, 10))"
+                    + "  AND (FStatsDate >= 20240710) AND (FStatsDate <= 
20240718)"
+                    + "  GROUP BY FUId, FAId_1, FStatsDate"
+                    + "  UNION ALL "
+                    + "  SELECT "
+                    + "  FUId AS advertiser_id,"
+                    + "  FAId_2 AS adgroup_id,"
+                    + "  cast (0 as UNSIGNED_LONG)  AS date,"
+                    + "  SUM(clk_pv_2) AS valid_click_count,"
+                    + "  SUM(activation_pv_2) AS activated_count"
+                    + "  FROM " + tableName2
+                    + "  WHERE (FVersion = 1) AND (FUId IN (1)) AND (FAId_2 IN 
(11, 22, 33, 10))"
+                    + "  AND (FStatsDate >= 20240710) AND (FStatsDate <= 
20240718)"
+                    + "  GROUP BY FUId, FAId_2"
+                    + ")"
+                    + "GROUP BY ADVERTISER_ID, ADGROUP_ID, I_DATE "
+                    + "ORDER BY advertiser_id, adgroup_id, i_date "
+                    + "limit 10";
+            plan =(ClientAggregatePlan)TestUtil.getOptimizeQueryPlan(conn, 
sql);
+            assertTrue(!plan.getGroupBy().isOrderPreserving());
+            unionPlan = 
(UnionPlan)((TupleProjectionPlan)(plan.getDelegate())).getDelegate();
+            assertTrue(!unionPlan.isSupportOrderByOptimize());
+            assertTrue(!(unionPlan.iterator() instanceof 
MergeSortTopNResultIterator));
+            assertTrue(unionPlan.getOutputOrderBys().isEmpty());
+
+            //Test only union
+            sql = orderedUnionSql.substring(1, orderedUnionSql.length()-1);
+            unionPlan =(UnionPlan)TestUtil.getOptimizeQueryPlan(conn, sql);
+            assertTrue(!unionPlan.isSupportOrderByOptimize());
+            assertTrue(!(unionPlan.iterator() instanceof 
MergeSortTopNResultIterator));
+            assertTrue(unionPlan.getOutputOrderBys().isEmpty());
+
+            //Test only union and order by match
+            sql = orderedUnionSql.substring(1, orderedUnionSql.length()-1) + " 
order by advertiser_id, date, adgroup_id";
+            unionPlan =(UnionPlan)TestUtil.getOptimizeQueryPlan(conn, sql);
+            assertTrue(!unionPlan.isSupportOrderByOptimize());
+            assertTrue(unionPlan.iterator() instanceof 
MergeSortTopNResultIterator);
+            assertTrue(unionPlan.getSubPlans().stream().allMatch(
+                    p -> p.getOrderBy() == OrderBy.FWD_ROW_KEY_ORDER_BY));
+            orderBys = unionPlan.getOutputOrderBys();
+            assertTrue(orderBys.size() == 1);
+            orderBy = orderBys.get(0);
+            assertTrue(orderBy.getOrderByExpressions().size() == 3);
+            
assertTrue(orderBy.getOrderByExpressions().get(0).toString().equals("ADVERTISER_ID"));
+            
assertTrue(orderBy.getOrderByExpressions().get(1).toString().equals("DATE"));
+            
assertTrue(orderBy.getOrderByExpressions().get(2).toString().equals("ADGROUP_ID"));
+
+            //Test only union and order by not match
+            sql = orderedUnionSql.substring(1, orderedUnionSql.length()-1) +
+                    " order by advertiser_id, date, adgroup_id, 
valid_click_count";
+            unionPlan =(UnionPlan)TestUtil.getOptimizeQueryPlan(conn, sql);
+            assertTrue(!unionPlan.isSupportOrderByOptimize());
+            assertTrue(unionPlan.iterator() instanceof 
MergeSortTopNResultIterator);
+            assertTrue(unionPlan.getSubPlans().stream().noneMatch(
+                    p -> p.getOrderBy() == OrderBy.FWD_ROW_KEY_ORDER_BY));
+            orderBys = unionPlan.getOutputOrderBys();
+            assertTrue(orderBys.size() == 1);
+            orderBy = orderBys.get(0);
+            assertTrue(orderBy.getOrderByExpressions().size() == 4);
+            
assertTrue(orderBy.getOrderByExpressions().get(0).toString().equals("ADVERTISER_ID"));
+            
assertTrue(orderBy.getOrderByExpressions().get(1).toString().equals("DATE"));
+            
assertTrue(orderBy.getOrderByExpressions().get(2).toString().equals("ADGROUP_ID"));
+            
assertTrue(orderBy.getOrderByExpressions().get(3).toString().equals("VALID_CLICK_COUNT"));
+        }
+    }
 }

Reply via email to