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

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


The following commit(s) were added to refs/heads/master by this push:
     new 81f1fbe4831 [nereids] group by key elimination (#30774)
81f1fbe4831 is described below

commit 81f1fbe48318e2c8aabb99bdfce9d410d407af80
Author: xzj7019 <[email protected]>
AuthorDate: Tue Feb 6 13:53:50 2024 +0800

    [nereids] group by key elimination (#30774)
---
 .../doris/nereids/jobs/executor/Rewriter.java      |   6 +
 .../ExprFdItem.java}                               |  24 ++-
 .../apache/doris/nereids/properties/FdFactory.java |  43 ++++
 .../apache/doris/nereids/properties/FdItem.java    |  64 ++++++
 .../nereids/properties/FunctionalDependencies.java |  23 ++-
 .../nereids/properties/LogicalProperties.java      |   6 +-
 .../doris/nereids/properties/TableFdItem.java      |  93 +++++++++
 .../org/apache/doris/nereids/rules/RuleType.java   |   1 +
 .../nereids/rules/rewrite/EliminateGroupByKey.java | 217 +++++++++++++++++++++
 .../trees/plans/BlockFuncDepsPropagation.java      |   8 +
 .../nereids/trees/plans/PropagateFuncDeps.java     |  19 ++
 .../trees/plans/logical/LogicalAggregate.java      |  28 +++
 .../plans/logical/LogicalCatalogRelation.java      |  53 +++++
 .../plans/logical/LogicalDeferMaterializeTopN.java |  23 +++
 .../nereids/trees/plans/logical/LogicalExcept.java |  30 +++
 .../nereids/trees/plans/logical/LogicalFilter.java |  13 ++
 .../trees/plans/logical/LogicalGenerate.java       |   9 +
 .../nereids/trees/plans/logical/LogicalHaving.java |  13 ++
 .../trees/plans/logical/LogicalIntersect.java      |  32 +++
 .../nereids/trees/plans/logical/LogicalJoin.java   | 165 +++++++++++++++-
 .../nereids/trees/plans/logical/LogicalLimit.java  |  24 +++
 .../trees/plans/logical/LogicalOneRowRelation.java |  22 +++
 .../nereids/trees/plans/logical/LogicalPlan.java   |   4 +
 .../trees/plans/logical/LogicalProject.java        |  13 ++
 .../nereids/trees/plans/logical/LogicalRepeat.java |  14 ++
 .../trees/plans/logical/LogicalSubQueryAlias.java  |  10 +
 .../nereids/trees/plans/logical/LogicalTopN.java   |  24 +++
 .../nereids/trees/plans/logical/LogicalUnion.java  |  24 +++
 .../nereids/trees/plans/logical/LogicalWindow.java |  35 +++-
 .../org/apache/doris/nereids/util/PlanUtils.java   |  25 +++
 .../constraints/query23.out                        |  47 +++--
 .../constraints/query23.groovy                     |   2 +
 32 files changed, 1071 insertions(+), 43 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/Rewriter.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/Rewriter.java
index 34f7afe4995..8f60fbddfdf 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/Rewriter.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/Rewriter.java
@@ -56,6 +56,7 @@ import 
org.apache.doris.nereids.rules.rewrite.EliminateDedupJoinCondition;
 import org.apache.doris.nereids.rules.rewrite.EliminateEmptyRelation;
 import org.apache.doris.nereids.rules.rewrite.EliminateFilter;
 import org.apache.doris.nereids.rules.rewrite.EliminateGroupBy;
+import org.apache.doris.nereids.rules.rewrite.EliminateGroupByKey;
 import org.apache.doris.nereids.rules.rewrite.EliminateJoinByFK;
 import org.apache.doris.nereids.rules.rewrite.EliminateJoinByUnique;
 import org.apache.doris.nereids.rules.rewrite.EliminateJoinCondition;
@@ -314,6 +315,11 @@ public class Rewriter extends AbstractBatchJobExecutor {
                     custom(RuleType.ELIMINATE_UNNECESSARY_PROJECT, 
EliminateUnnecessaryProject::new)
             ),
 
+            // this rule should invoke after topic "Join pull up"
+            topic("eliminate group by keys according to fd items",
+                    topDown(new EliminateGroupByKey())
+            ),
+
             topic("Limit optimization",
                     // TODO: the logical plan should not contains any phase 
information,
                     //       we should refactor like AggregateStrategies, e.g. 
LimitStrategies,
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/BlockFuncDepsPropagation.java
 b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/ExprFdItem.java
similarity index 57%
copy from 
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/BlockFuncDepsPropagation.java
copy to 
fe/fe-core/src/main/java/org/apache/doris/nereids/properties/ExprFdItem.java
index 3f60727576d..9a36f6e0d97 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/BlockFuncDepsPropagation.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/ExprFdItem.java
@@ -15,21 +15,27 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.nereids.trees.plans;
+package org.apache.doris.nereids.properties;
 
-import org.apache.doris.nereids.properties.FunctionalDependencies;
-import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
 import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
 
-import java.util.List;
-import java.util.function.Supplier;
+import com.google.common.collect.ImmutableSet;
 
 /**
- * Block fd propagation, it always returns an empty fd
+ * Expression level function dependence items.
  */
-public interface BlockFuncDepsPropagation extends LogicalPlan {
+public class ExprFdItem extends FdItem {
+    private ImmutableSet<SlotReference> childExprs;
+
+    public ExprFdItem(ImmutableSet<SlotReference> parentExprs, boolean 
isUnique,
+            ImmutableSet<SlotReference> childExprs) {
+        super(parentExprs, isUnique, false);
+        this.childExprs = ImmutableSet.copyOf(childExprs);
+    }
+
     @Override
-    default FunctionalDependencies computeFuncDeps(Supplier<List<Slot>> 
outputSupplier) {
-        return FunctionalDependencies.EMPTY_FUNC_DEPS;
+    public boolean checkExprInChild(SlotReference slot, LogicalPlan childPlan) 
{
+        return childExprs.contains(slot);
     }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/FdFactory.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/FdFactory.java
new file mode 100644
index 00000000000..cd08ce50563
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/FdFactory.java
@@ -0,0 +1,43 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.properties;
+
+import org.apache.doris.catalog.TableIf;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * Function dependence building factory.
+ */
+public class FdFactory {
+
+    public static final FdFactory INSTANCE = new FdFactory();
+
+    public TableFdItem createTableFdItem(ImmutableSet<SlotReference> 
parentExprs, boolean isUnique,
+            boolean isCandidate, ImmutableSet<TableIf> tableIds) {
+        TableFdItem fdItem = new TableFdItem(parentExprs, isUnique, 
isCandidate, tableIds);
+        return fdItem;
+    }
+
+    public ExprFdItem createExprFdItem(ImmutableSet<SlotReference> 
parentExprs, boolean isUnique,
+            ImmutableSet<SlotReference> childExprs) {
+        ExprFdItem fdItem = new ExprFdItem(parentExprs, isUnique, childExprs);
+        return fdItem;
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/FdItem.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/FdItem.java
new file mode 100644
index 00000000000..4f8bf602695
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/FdItem.java
@@ -0,0 +1,64 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.properties;
+
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * Function dependence items.
+ */
+public class FdItem {
+    private ImmutableSet<SlotReference> parentExprs;
+
+    private boolean isUnique;
+
+    private boolean isCandidate;
+
+    public FdItem(ImmutableSet<SlotReference> parentExprs, boolean isUnique, 
boolean isCandidate) {
+        this.parentExprs = ImmutableSet.copyOf(parentExprs);
+        this.isUnique = isUnique;
+        this.isCandidate = isCandidate;
+    }
+
+    public boolean isCandidate() {
+        return isCandidate;
+    }
+
+    public void setCandidate(boolean isCandidate) {
+        this.isCandidate = isCandidate;
+    }
+
+    public boolean isUnique() {
+        return isUnique;
+    }
+
+    public void setUnique(boolean isUnique) {
+        this.isUnique = isUnique;
+    }
+
+    public ImmutableSet<SlotReference> getParentExprs() {
+        return parentExprs;
+    }
+
+    public boolean checkExprInChild(SlotReference slot, LogicalPlan childPlan) 
{
+        return false;
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/FunctionalDependencies.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/FunctionalDependencies.java
index b7395ecb8cf..30bcdaec3dd 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/FunctionalDependencies.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/FunctionalDependencies.java
@@ -34,13 +34,16 @@ import java.util.stream.Collectors;
 public class FunctionalDependencies {
 
     public static final FunctionalDependencies EMPTY_FUNC_DEPS
-            = new FunctionalDependencies(new NestedSet().toImmutable(), new 
NestedSet().toImmutable());
+            = new FunctionalDependencies(new NestedSet().toImmutable(),
+                    new NestedSet().toImmutable(), new 
ImmutableSet.Builder<FdItem>().build());
     private final NestedSet uniqueSet;
     private final NestedSet uniformSet;
+    private final ImmutableSet<FdItem> fdItems;
 
-    private FunctionalDependencies(NestedSet uniqueSet, NestedSet uniformSet) {
+    private FunctionalDependencies(NestedSet uniqueSet, NestedSet uniformSet, 
ImmutableSet<FdItem> fdItems) {
         this.uniqueSet = uniqueSet;
         this.uniformSet = uniformSet;
+        this.fdItems = fdItems;
     }
 
     public boolean isEmpty() {
@@ -83,9 +86,13 @@ public class FunctionalDependencies {
         return slotSet.stream().noneMatch(Slot::nullable) && 
isUniform(slotSet);
     }
 
+    public ImmutableSet<FdItem> getFdItems() {
+        return fdItems;
+    }
+
     @Override
     public String toString() {
-        return String.format("FuncDeps[uniform:%s,  unique:%s]", uniformSet, 
uniqueSet);
+        return String.format("FuncDeps[uniform:%s, unique:%s, fdItems:%s]", 
uniformSet, uniqueSet, fdItems);
     }
 
     /**
@@ -94,15 +101,18 @@ public class FunctionalDependencies {
     public static class Builder {
         private final NestedSet uniqueSet;
         private final NestedSet uniformSet;
+        private ImmutableSet<FdItem> fdItems;
 
         public Builder() {
             uniqueSet = new NestedSet();
             uniformSet = new NestedSet();
+            fdItems = new ImmutableSet.Builder<FdItem>().build();
         }
 
         public Builder(FunctionalDependencies other) {
             this.uniformSet = new NestedSet(other.uniformSet);
             this.uniqueSet = new NestedSet(other.uniqueSet);
+            this.fdItems = ImmutableSet.copyOf(other.fdItems);
         }
 
         public void addUniformSlot(Slot slot) {
@@ -125,13 +135,18 @@ public class FunctionalDependencies {
             uniqueSet.add(slotSet);
         }
 
+        public void addFdItems(ImmutableSet<FdItem> items) {
+            fdItems = ImmutableSet.copyOf(items);
+        }
+
         public void addFunctionalDependencies(FunctionalDependencies fd) {
             uniformSet.add(fd.uniformSet);
             uniqueSet.add(fd.uniqueSet);
         }
 
         public FunctionalDependencies build() {
-            return new FunctionalDependencies(uniqueSet.toImmutable(), 
uniformSet.toImmutable());
+            return new FunctionalDependencies(uniqueSet.toImmutable(), 
uniformSet.toImmutable(),
+                    ImmutableSet.copyOf(fdItems));
         }
 
         public void pruneSlots(Set<Slot> outputSlots) {
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/LogicalProperties.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/LogicalProperties.java
index f82b43eaea6..07d28828942 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/LogicalProperties.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/LogicalProperties.java
@@ -45,7 +45,8 @@ public class LogicalProperties {
     protected final Supplier<FunctionalDependencies> fdSupplier;
     private Integer hashCode = null;
 
-    public LogicalProperties(Supplier<List<Slot>> outputSupplier, 
Supplier<FunctionalDependencies> fdSupplier) {
+    public LogicalProperties(Supplier<List<Slot>> outputSupplier,
+            Supplier<FunctionalDependencies> fdSupplier) {
         this(outputSupplier, fdSupplier, ImmutableList::of);
     }
 
@@ -56,7 +57,8 @@ public class LogicalProperties {
      *                       throw exception for which children have 
UnboundRelation
      */
     public LogicalProperties(Supplier<List<Slot>> outputSupplier,
-            Supplier<FunctionalDependencies> fdSupplier, Supplier<List<Slot>> 
nonUserVisibleOutputSupplier) {
+            Supplier<FunctionalDependencies> fdSupplier,
+            Supplier<List<Slot>> nonUserVisibleOutputSupplier) {
         this.outputSupplier = Suppliers.memoize(
                 Objects.requireNonNull(outputSupplier, "outputSupplier can not 
be null")
         );
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/TableFdItem.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/TableFdItem.java
new file mode 100644
index 00000000000..e919ea2f05d
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/TableFdItem.java
@@ -0,0 +1,93 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.properties;
+
+import org.apache.doris.catalog.TableIf;
+import org.apache.doris.nereids.trees.expressions.Alias;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.plans.logical.LogicalCatalogRelation;
+import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+import org.apache.doris.nereids.util.PlanUtils;
+
+import com.google.common.collect.ImmutableSet;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Table level function dependence items.
+ */
+public class TableFdItem extends FdItem {
+
+    private ImmutableSet<TableIf> childTables;
+
+    public TableFdItem(ImmutableSet<SlotReference> parentExprs, boolean 
isUnique,
+            boolean isCandidate, ImmutableSet<TableIf> childTables) {
+        super(parentExprs, isUnique, isCandidate);
+        this.childTables = ImmutableSet.copyOf(childTables);
+    }
+
+    @Override
+    public boolean checkExprInChild(SlotReference slot, LogicalPlan childPlan) 
{
+        NamedExpression slotInChild = null;
+        // find in child project based on exprid matching
+        List<NamedExpression> exprList = ((LogicalProject) 
childPlan).getProjects();
+        for (NamedExpression expr : exprList) {
+            if (expr.getExprId().equals(slot.getExprId())) {
+                slotInChild = expr;
+                break;
+            }
+        }
+        if (slotInChild == null) {
+            return false;
+        } else {
+            Set<Slot> slotSet = new HashSet<>();
+            if (slotInChild instanceof Alias) {
+                slotSet = slotInChild.getInputSlots();
+            } else {
+                slotSet.add((Slot) slotInChild);
+            }
+            // get table list from slotSet
+            Set<TableIf> tableSets = getTableSets(slotSet, childPlan);
+            if (tableSets.isEmpty()) {
+                return false;
+            } else if (childTables.containsAll(tableSets)) {
+                return true;
+            } else {
+                return false;
+            }
+        }
+    }
+
+    private Set<TableIf> getTableSets(Set<Slot> slotSet, LogicalPlan plan) {
+        Set<LogicalCatalogRelation> tableSets = 
PlanUtils.getLogicalScanFromRootPlan(plan);
+        Set<TableIf> resultSet = new HashSet<>();
+        for (Slot slot : slotSet) {
+            for (LogicalCatalogRelation table : tableSets) {
+                if (table.getOutputExprIds().contains(slot.getExprId())) {
+                    resultSet.add(table.getTable());
+                }
+            }
+        }
+        return resultSet;
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java
index 594f49a3b70..89f4ce29eb5 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java
@@ -209,6 +209,7 @@ public enum RuleType {
     ELIMINATE_MARK_JOIN(RuleTypeClass.REWRITE),
     ELIMINATE_GROUP_BY(RuleTypeClass.REWRITE),
     ELIMINATE_JOIN_BY_UK(RuleTypeClass.REWRITE),
+    ELIMINATE_GROUP_BY_KEY(RuleTypeClass.REWRITE),
     ELIMINATE_DEDUP_JOIN_CONDITION(RuleTypeClass.REWRITE),
     ELIMINATE_NULL_AWARE_LEFT_ANTI_JOIN(RuleTypeClass.REWRITE),
     ELIMINATE_ASSERT_NUM_ROWS(RuleTypeClass.REWRITE),
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/EliminateGroupByKey.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/EliminateGroupByKey.java
new file mode 100644
index 00000000000..d922252bebc
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/EliminateGroupByKey.java
@@ -0,0 +1,217 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.rules.rewrite;
+
+import org.apache.doris.nereids.properties.FdItem;
+import org.apache.doris.nereids.rules.Rule;
+import org.apache.doris.nereids.rules.RuleType;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
+import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
+import org.apache.doris.qe.ConnectContext;
+
+import com.google.common.collect.ImmutableSet;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Eliminate group by key based on fd item information.
+ */
+public class EliminateGroupByKey extends OneRewriteRuleFactory {
+    @Override
+    public Rule build() {
+        return logicalAggregate(logicalProject()).then(agg -> {
+            Set<Integer> enableNereidsRules = 
ConnectContext.get().getSessionVariable().getEnableNereidsRules();
+            if 
(!enableNereidsRules.contains(RuleType.ELIMINATE_GROUP_BY_KEY.type())) {
+                return null;
+            }
+            LogicalPlan childPlan = agg.child();
+            List<FdItem> uniqueFdItems = new ArrayList<>();
+            List<FdItem> nonUniqueFdItems = new ArrayList<>();
+            if (agg.getGroupByExpressions().isEmpty()
+                    || 
agg.getGroupByExpressions().equals(agg.getOutputExpressions())
+                    || !agg.getGroupByExpressions().stream().allMatch(e -> e 
instanceof SlotReference)
+                    || agg.getGroupByExpressions().stream().allMatch(e ->
+                        ((SlotReference) e).getColumn().isPresent() && 
((SlotReference) e).getTable().isPresent())) {
+                return null;
+            }
+            ImmutableSet<FdItem> fdItems = 
childPlan.getLogicalProperties().getFunctionalDependencies().getFdItems();
+            if (fdItems.isEmpty()) {
+                return null;
+            }
+            List<SlotReference> candiExprs = 
agg.getGroupByExpressions().stream()
+                    
.map(SlotReference.class::cast).collect(Collectors.toList());
+
+            fdItems.stream().filter(e -> !e.isCandidate()).forEach(e -> {
+                        if (e.isUnique()) {
+                            uniqueFdItems.add(e);
+                        } else {
+                            nonUniqueFdItems.add(e);
+                        }
+                    }
+            );
+
+            int minParentExprCnt = -1;
+            ImmutableSet<SlotReference> minParentExprs = ImmutableSet.of();
+            // if unique fd items exists, try to find the one which has the
+            // smallest parent exprs
+            for (int i = 0; i < uniqueFdItems.size(); i++) {
+                FdItem fdItem = uniqueFdItems.get(i);
+                ImmutableSet<SlotReference> parentExprs = 
fdItem.getParentExprs();
+                if (minParentExprCnt == -1 || parentExprs.size() < 
minParentExprCnt) {
+                    boolean isContain = isExprsContainFdParent(candiExprs, 
fdItem);
+                    if (isContain) {
+                        minParentExprCnt = parentExprs.size();
+                        minParentExprs = ImmutableSet.copyOf(parentExprs);
+                    }
+                }
+            }
+
+            Set<Integer> rootExprsSet = new HashSet<>();
+            List<SlotReference> rootExprs = new ArrayList<>();
+            Set<Integer> eliminateSet = new HashSet<>();
+            if (minParentExprs.size() > 0) {
+                // if any unique fd item found, find the expr which matching 
parentExprs
+                // from candiExprs directly
+                for (int i = 0; i < minParentExprs.size(); i++) {
+                    int index = findEqualExpr(candiExprs, 
minParentExprs.asList().get(i));
+                    if (index != -1) {
+                        rootExprsSet.add(new Integer(index));
+                    } else {
+                        return null;
+                    }
+                }
+            } else {
+                // no unique fd item found, try to find the smallest root 
exprs set
+                // from non-unique fd items.
+                for (int i = 0; i < nonUniqueFdItems.size() && 
eliminateSet.size() < candiExprs.size(); i++) {
+                    FdItem fdItem = nonUniqueFdItems.get(i);
+                    ImmutableSet<SlotReference> parentExprs = 
fdItem.getParentExprs();
+                    boolean isContains = isExprsContainFdParent(candiExprs, 
fdItem);
+                    if (isContains) {
+                        List<SlotReference> leftDomain = new ArrayList<>();
+                        List<SlotReference> rightDomain = new ArrayList<>();
+                        // generate new root exprs
+                        for (int j = 0; j < rootExprs.size(); j++) {
+                            leftDomain.add(rootExprs.get(j));
+                            boolean isInChild = 
fdItem.checkExprInChild(rootExprs.get(j), childPlan);
+                            if (!isInChild) {
+                                rightDomain.add(rootExprs.get(j));
+                            }
+                        }
+                        for (int j = 0; j < parentExprs.size(); j++) {
+                            int index = findEqualExpr(candiExprs, 
parentExprs.asList().get(j));
+                            if (index != -1) {
+                                rightDomain.add(candiExprs.get(index));
+                                if (!eliminateSet.contains(index)) {
+                                    leftDomain.add(candiExprs.get(index));
+                                }
+                            }
+                        }
+                        // check fd can eliminate new candi expr
+                        for (int j = 0; j < candiExprs.size(); j++) {
+                            if (!eliminateSet.contains(j)) {
+                                boolean isInChild = 
fdItem.checkExprInChild(candiExprs.get(j), childPlan);
+                                if (isInChild) {
+                                    eliminateSet.add(j);
+                                }
+                            }
+                        }
+                        // if fd eliminate new candi exprs or new root exprs 
is smaller than the older,
+                        // than use new root expr to replace old ones
+                        List<SlotReference> newRootExprs = leftDomain.size() 
<= rightDomain.size()
+                                ? leftDomain : rightDomain;
+                        rootExprs.clear();
+                        rootExprs.addAll(newRootExprs);
+                    }
+                }
+            }
+            // find the root expr, add into root exprs set, indicate the index 
in
+            // candiExprs list
+            for (int i = 0; i < rootExprs.size(); i++) {
+                int index = findEqualExpr(candiExprs, rootExprs.get(i));
+                if (index != -1) {
+                    rootExprsSet.add(new Integer(index));
+                } else {
+                    return null;
+                }
+            }
+            // other can't be determined expr, add into root exprs directly
+            if (eliminateSet.size() < candiExprs.size()) {
+                for (int i = 0; i < candiExprs.size(); i++) {
+                    if (!eliminateSet.contains(i)) {
+                        rootExprsSet.add(i);
+                    }
+                }
+            }
+            rootExprs.clear();
+            for (int i = 0; i < candiExprs.size(); i++) {
+                if (rootExprsSet.contains(i)) {
+                    rootExprs.add(candiExprs.get(i));
+                }
+            }
+
+            // use the new rootExprs as new group by keys
+            List<Expression> resultExprs = new ArrayList<>();
+            for (int i = 0; i < rootExprs.size(); i++) {
+                resultExprs.add(rootExprs.get(i));
+            }
+
+            // eliminate outputs keys
+            // TODO: remove outputExprList computing
+            List<NamedExpression> outputExprList = new ArrayList<>();
+            for (int i = 0; i < agg.getOutputExpressions().size(); i++) {
+                if (rootExprsSet.contains(i)) {
+                    outputExprList.add(agg.getOutputExpressions().get(i));
+                }
+            }
+            // find the remained outputExprs list
+            List<NamedExpression> remainedOutputExprList = new ArrayList<>();
+            for (int i = 0; i < agg.getOutputExpressions().size(); i++) {
+                NamedExpression outputExpr = agg.getOutputExpressions().get(i);
+                if (!agg.getGroupByExpressions().contains(outputExpr)) {
+                    remainedOutputExprList.add(outputExpr);
+                }
+            }
+            outputExprList.addAll(remainedOutputExprList);
+            return new LogicalAggregate<>(resultExprs, 
agg.getOutputExpressions(), agg.child());
+        }).toRule(RuleType.ELIMINATE_GROUP_BY_KEY);
+    }
+
+    /**
+     * find the equal expr index from expr list.
+     */
+    public int findEqualExpr(List<SlotReference> exprList, SlotReference expr) 
{
+        for (int i = 0; i < exprList.size(); i++) {
+            if (exprList.get(i).equals(expr)) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    private boolean isExprsContainFdParent(List<SlotReference> candiExprs, 
FdItem fdItem) {
+        return fdItem.getParentExprs().stream().allMatch(e -> 
candiExprs.contains(e));
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/BlockFuncDepsPropagation.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/BlockFuncDepsPropagation.java
index 3f60727576d..07804587e3b 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/BlockFuncDepsPropagation.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/BlockFuncDepsPropagation.java
@@ -17,10 +17,13 @@
 
 package org.apache.doris.nereids.trees.plans;
 
+import org.apache.doris.nereids.properties.FdItem;
 import org.apache.doris.nereids.properties.FunctionalDependencies;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
 
+import com.google.common.collect.ImmutableSet;
+
 import java.util.List;
 import java.util.function.Supplier;
 
@@ -32,4 +35,9 @@ public interface BlockFuncDepsPropagation extends LogicalPlan 
{
     default FunctionalDependencies computeFuncDeps(Supplier<List<Slot>> 
outputSupplier) {
         return FunctionalDependencies.EMPTY_FUNC_DEPS;
     }
+
+    @Override
+    default ImmutableSet<FdItem> computeFdItems(Supplier<List<Slot>> 
outputSupplier) {
+        return ImmutableSet.of();
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PropagateFuncDeps.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PropagateFuncDeps.java
index 332a80c99ee..c344f22ca21 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PropagateFuncDeps.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PropagateFuncDeps.java
@@ -17,10 +17,13 @@
 
 package org.apache.doris.nereids.trees.plans;
 
+import org.apache.doris.nereids.properties.FdItem;
 import org.apache.doris.nereids.properties.FunctionalDependencies;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
 
+import com.google.common.collect.ImmutableSet;
+
 import java.util.List;
 import java.util.function.Supplier;
 
@@ -39,6 +42,22 @@ public interface PropagateFuncDeps extends LogicalPlan {
         children().stream()
                 .map(p -> p.getLogicalProperties().getFunctionalDependencies())
                 .forEach(builder::addFunctionalDependencies);
+        ImmutableSet<FdItem> fdItems = computeFdItems(outputSupplier);
+        builder.addFdItems(fdItems);
+        return builder.build();
+    }
+
+    @Override
+    default ImmutableSet<FdItem> computeFdItems(Supplier<List<Slot>> 
outputSupplier) {
+        if (children().size() == 1) {
+            // Note when changing function dependencies, we always clone it.
+            // So it's safe to return a reference
+            return 
child(0).getLogicalProperties().getFunctionalDependencies().getFdItems();
+        }
+        ImmutableSet.Builder<FdItem> builder = ImmutableSet.builder();
+        children().stream()
+                .map(p -> 
p.getLogicalProperties().getFunctionalDependencies().getFdItems())
+                .forEach(builder::addAll);
         return builder.build();
     }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalAggregate.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalAggregate.java
index edf962838d3..20647a3808e 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalAggregate.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalAggregate.java
@@ -18,9 +18,12 @@
 package org.apache.doris.nereids.trees.plans.logical;
 
 import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.properties.FdFactory;
+import org.apache.doris.nereids.properties.FdItem;
 import org.apache.doris.nereids.properties.FunctionalDependencies;
 import org.apache.doris.nereids.properties.FunctionalDependencies.Builder;
 import org.apache.doris.nereids.properties.LogicalProperties;
+import org.apache.doris.nereids.properties.TableFdItem;
 import org.apache.doris.nereids.trees.expressions.Alias;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.NamedExpression;
@@ -360,6 +363,31 @@ public class LogicalAggregate<CHILD_TYPE extends Plan>
         // group by keys is unique
         fdBuilder.addUniqueSlot(groupByKeys);
         fdBuilder.pruneSlots(outputSet);
+
+        ImmutableSet<FdItem> fdItems = computeFdItems(outputSupplier);
+        fdBuilder.addFdItems(fdItems);
+
         return fdBuilder.build();
     }
+
+    @Override
+    public ImmutableSet<FdItem> computeFdItems(Supplier<List<Slot>> 
outputSupplier) {
+        ImmutableSet.Builder<FdItem> builder = ImmutableSet.builder();
+
+        ImmutableSet<SlotReference> groupByExprs = 
getGroupByExpressions().stream()
+                .filter(SlotReference.class::isInstance)
+                .map(SlotReference.class::cast)
+                .collect(ImmutableSet.toImmutableSet());
+
+        // inherit from child
+        ImmutableSet<FdItem> childItems = 
child().getLogicalProperties().getFunctionalDependencies().getFdItems();
+        builder.addAll(childItems);
+
+        // todo: fill the table sets
+        TableFdItem fdItem = 
FdFactory.INSTANCE.createTableFdItem(groupByExprs, true,
+                false, ImmutableSet.of());
+        builder.add(fdItem);
+
+        return builder.build();
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalCatalogRelation.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalCatalogRelation.java
index f0ff94c4c60..4076e8348e2 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalCatalogRelation.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalCatalogRelation.java
@@ -25,9 +25,13 @@ import org.apache.doris.catalog.TableIf;
 import org.apache.doris.datasource.CatalogIf;
 import org.apache.doris.nereids.exceptions.AnalysisException;
 import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.properties.FdFactory;
+import org.apache.doris.nereids.properties.FdItem;
 import org.apache.doris.nereids.properties.FunctionalDependencies;
 import org.apache.doris.nereids.properties.FunctionalDependencies.Builder;
 import org.apache.doris.nereids.properties.LogicalProperties;
+import org.apache.doris.nereids.properties.TableFdItem;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.expressions.SlotReference;
 import org.apache.doris.nereids.trees.plans.PlanType;
@@ -154,6 +158,55 @@ public abstract class LogicalCatalogRelation extends 
LogicalRelation implements
                     .collect(ImmutableSet.toImmutableSet());
             fdBuilder.addUniqueSlot(slotSet);
         });
+        ImmutableSet<FdItem> fdItems = computeFdItems(outputSupplier);
+        fdBuilder.addFdItems(fdItems);
         return fdBuilder.build();
     }
+
+    @Override
+    public ImmutableSet<FdItem> computeFdItems(Supplier<List<Slot>> 
outputSupplier) {
+        Set<NamedExpression> output = 
ImmutableSet.copyOf(outputSupplier.get());
+        ImmutableSet.Builder<FdItem> builder = ImmutableSet.builder();
+        table.getPrimaryKeyConstraints().forEach(c -> {
+            Set<Column> columns = c.getPrimaryKeys(this.getTable());
+            ImmutableSet<SlotReference> slotSet = output.stream()
+                    .filter(SlotReference.class::isInstance)
+                    .map(SlotReference.class::cast)
+                    .filter(s -> s.getColumn().isPresent()
+                            && columns.contains(s.getColumn().get()))
+                    .collect(ImmutableSet.toImmutableSet());
+            TableFdItem tableFdItem = 
FdFactory.INSTANCE.createTableFdItem(slotSet, true,
+                    false, ImmutableSet.of(table));
+            builder.add(tableFdItem);
+        });
+        table.getUniqueConstraints().forEach(c -> {
+            Set<Column> columns = c.getUniqueKeys(this.getTable());
+            boolean allNotNull = columns.stream()
+                    .filter(SlotReference.class::isInstance)
+                    .map(SlotReference.class::cast)
+                    .allMatch(s -> !s.nullable());
+            if (allNotNull) {
+                ImmutableSet<SlotReference> slotSet = output.stream()
+                        .filter(SlotReference.class::isInstance)
+                        .map(SlotReference.class::cast)
+                        .filter(s -> s.getColumn().isPresent()
+                                && columns.contains(s.getColumn().get()))
+                        .collect(ImmutableSet.toImmutableSet());
+                TableFdItem tableFdItem = 
FdFactory.INSTANCE.createTableFdItem(slotSet,
+                        true, false, ImmutableSet.of(table));
+                builder.add(tableFdItem);
+            } else {
+                ImmutableSet<SlotReference> slotSet = output.stream()
+                        .filter(SlotReference.class::isInstance)
+                        .map(SlotReference.class::cast)
+                        .filter(s -> s.getColumn().isPresent()
+                                && columns.contains(s.getColumn().get()))
+                        .collect(ImmutableSet.toImmutableSet());
+                TableFdItem tableFdItem = 
FdFactory.INSTANCE.createTableFdItem(slotSet,
+                        true, true, ImmutableSet.of(table));
+                builder.add(tableFdItem);
+            }
+        });
+        return builder.build();
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalDeferMaterializeTopN.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalDeferMaterializeTopN.java
index 6b6f6d432eb..5bf0afe1fae 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalDeferMaterializeTopN.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalDeferMaterializeTopN.java
@@ -18,6 +18,9 @@
 package org.apache.doris.nereids.trees.plans.logical;
 
 import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.properties.ExprFdItem;
+import org.apache.doris.nereids.properties.FdFactory;
+import org.apache.doris.nereids.properties.FdItem;
 import org.apache.doris.nereids.properties.FunctionalDependencies;
 import org.apache.doris.nereids.properties.FunctionalDependencies.Builder;
 import org.apache.doris.nereids.properties.LogicalProperties;
@@ -34,6 +37,7 @@ import org.apache.doris.nereids.util.Utils;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 
 import java.util.List;
 import java.util.Objects;
@@ -123,11 +127,30 @@ public class LogicalDeferMaterializeTopN<CHILD_TYPE 
extends Plan> extends Logica
             List<Slot> output = outputSupplier.get();
             output.forEach(builder::addUniformSlot);
             output.forEach(builder::addUniqueSlot);
+            ImmutableSet<FdItem> fdItems = computeFdItems(outputSupplier);
+            builder.addFdItems(fdItems);
             fd = builder.build();
         }
         return fd;
     }
 
+    @Override
+    public ImmutableSet<FdItem> computeFdItems(Supplier<List<Slot>> 
outputSupplier) {
+        ImmutableSet<FdItem> fdItems = 
child(0).getLogicalProperties().getFunctionalDependencies().getFdItems();
+        if (getLimit() == 1) {
+            ImmutableSet.Builder<FdItem> builder = ImmutableSet.builder();
+            List<Slot> output = outputSupplier.get();
+            ImmutableSet<SlotReference> slotSet = output.stream()
+                    .filter(SlotReference.class::isInstance)
+                    .map(SlotReference.class::cast)
+                    .collect(ImmutableSet.toImmutableSet());
+            ExprFdItem fdItem = FdFactory.INSTANCE.createExprFdItem(slotSet, 
true, slotSet);
+            builder.add(fdItem);
+            fdItems = builder.build();
+        }
+        return fdItems;
+    }
+
     @Override
     public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
         return visitor.visitLogicalDeferMaterializeTopN(this, context);
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalExcept.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalExcept.java
index c1753f147e4..707da68fe8f 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalExcept.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalExcept.java
@@ -18,6 +18,9 @@
 package org.apache.doris.nereids.trees.plans.logical;
 
 import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.properties.ExprFdItem;
+import org.apache.doris.nereids.properties.FdFactory;
+import org.apache.doris.nereids.properties.FdItem;
 import org.apache.doris.nereids.properties.FunctionalDependencies;
 import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.trees.expressions.NamedExpression;
@@ -35,6 +38,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import java.util.Set;
 import java.util.function.Supplier;
 
 /**
@@ -120,6 +124,32 @@ public class LogicalExcept extends LogicalSetOperation {
         if (qualifier == Qualifier.DISTINCT) {
             builder.addUniqueSlot(ImmutableSet.copyOf(outputSupplier.get()));
         }
+        ImmutableSet<FdItem> fdItems = computeFdItems(outputSupplier);
+        builder.addFdItems(fdItems);
+        return builder.build();
+    }
+
+    @Override
+    public ImmutableSet<FdItem> computeFdItems(Supplier<List<Slot>> 
outputSupplier) {
+        Set<NamedExpression> output = 
ImmutableSet.copyOf(outputSupplier.get());
+        ImmutableSet.Builder<FdItem> builder = ImmutableSet.builder();
+
+        ImmutableSet<SlotReference> exprs = output.stream()
+                .filter(SlotReference.class::isInstance)
+                .map(SlotReference.class::cast)
+                .collect(ImmutableSet.toImmutableSet());
+
+        if (qualifier == Qualifier.DISTINCT) {
+            ExprFdItem fdItem = FdFactory.INSTANCE.createExprFdItem(exprs, 
true, exprs);
+            builder.add(fdItem);
+
+            // only inherit from left side
+            ImmutableSet<FdItem> leftFdItems = child(0).getLogicalProperties()
+                    .getFunctionalDependencies().getFdItems();
+
+            builder.addAll(leftFdItems);
+        }
+
         return builder.build();
     }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalFilter.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalFilter.java
index 0c4699fcbe2..67ccc672bf4 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalFilter.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalFilter.java
@@ -18,6 +18,7 @@
 package org.apache.doris.nereids.trees.plans.logical;
 
 import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.properties.FdItem;
 import org.apache.doris.nereids.properties.FunctionalDependencies;
 import org.apache.doris.nereids.properties.FunctionalDependencies.Builder;
 import org.apache.doris.nereids.properties.LogicalProperties;
@@ -146,6 +147,18 @@ public class LogicalFilter<CHILD_TYPE extends Plan> 
extends LogicalUnary<CHILD_T
         Builder fdBuilder = new Builder(
                 child().getLogicalProperties().getFunctionalDependencies());
         getConjuncts().forEach(e -> 
fdBuilder.addUniformSlot(ExpressionUtils.extractUniformSlot(e)));
+        ImmutableSet<FdItem> fdItems = computeFdItems(outputSupplier);
+        fdBuilder.addFdItems(fdItems);
         return fdBuilder.build();
     }
+
+    @Override
+    public ImmutableSet<FdItem> computeFdItems(Supplier<List<Slot>> 
outputSupplier) {
+        ImmutableSet.Builder<FdItem> builder = ImmutableSet.builder();
+
+        ImmutableSet<FdItem> childItems = 
child().getLogicalProperties().getFunctionalDependencies().getFdItems();
+        builder.addAll(childItems);
+
+        return builder.build();
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalGenerate.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalGenerate.java
index 0de6e46ce25..2bb5d79b48b 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalGenerate.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalGenerate.java
@@ -18,6 +18,7 @@
 package org.apache.doris.nereids.trees.plans.logical;
 
 import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.properties.FdItem;
 import org.apache.doris.nereids.properties.FunctionalDependencies;
 import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.trees.expressions.Expression;
@@ -31,6 +32,7 @@ import org.apache.doris.nereids.util.Utils;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 
 import java.util.List;
@@ -146,6 +148,13 @@ public class LogicalGenerate<CHILD_TYPE extends Plan> 
extends LogicalUnary<CHILD
     public FunctionalDependencies computeFuncDeps(Supplier<List<Slot>> 
outputSupplier) {
         FunctionalDependencies.Builder builder = new 
FunctionalDependencies.Builder();
         
builder.addUniformSlot(child(0).getLogicalProperties().getFunctionalDependencies());
+        ImmutableSet<FdItem> fdItems = computeFdItems(outputSupplier);
+        builder.addFdItems(fdItems);
         return builder.build();
     }
+
+    @Override
+    public ImmutableSet<FdItem> computeFdItems(Supplier<List<Slot>> 
outputSupplier) {
+        return ImmutableSet.of();
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalHaving.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalHaving.java
index 25600c56d26..e4d01f12748 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalHaving.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalHaving.java
@@ -18,6 +18,7 @@
 package org.apache.doris.nereids.trees.plans.logical;
 
 import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.properties.FdItem;
 import org.apache.doris.nereids.properties.FunctionalDependencies;
 import org.apache.doris.nereids.properties.FunctionalDependencies.Builder;
 import org.apache.doris.nereids.properties.LogicalProperties;
@@ -123,9 +124,21 @@ public class LogicalHaving<CHILD_TYPE extends Plan> 
extends LogicalUnary<CHILD_T
         Builder fdBuilder = new Builder(
                 child().getLogicalProperties().getFunctionalDependencies());
         getConjuncts().forEach(e -> 
fdBuilder.addUniformSlot(ExpressionUtils.extractUniformSlot(e)));
+        ImmutableSet<FdItem> fdItems = computeFdItems(outputSupplier);
+        fdBuilder.addFdItems(fdItems);
         return fdBuilder.build();
     }
 
+    @Override
+    public ImmutableSet<FdItem> computeFdItems(Supplier<List<Slot>> 
outputSupplier) {
+        ImmutableSet.Builder<FdItem> builder = ImmutableSet.builder();
+
+        ImmutableSet<FdItem> childItems = 
child().getLogicalProperties().getFunctionalDependencies().getFdItems();
+        builder.addAll(childItems);
+
+        return builder.build();
+    }
+
     @Override
     public String toString() {
         return Utils.toSqlString("LogicalHaving", "predicates", 
getPredicate());
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalIntersect.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalIntersect.java
index 2d3006bda59..2fbdbd24561 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalIntersect.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalIntersect.java
@@ -18,6 +18,9 @@
 package org.apache.doris.nereids.trees.plans.logical;
 
 import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.properties.ExprFdItem;
+import org.apache.doris.nereids.properties.FdFactory;
+import org.apache.doris.nereids.properties.FdItem;
 import org.apache.doris.nereids.properties.FunctionalDependencies;
 import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.trees.expressions.NamedExpression;
@@ -35,6 +38,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import java.util.Set;
 import java.util.function.Supplier;
 
 /**
@@ -125,6 +129,34 @@ public class LogicalIntersect extends LogicalSetOperation {
         if (qualifier == Qualifier.DISTINCT) {
             builder.addUniqueSlot(ImmutableSet.copyOf(outputSupplier.get()));
         }
+        ImmutableSet<FdItem> fdItems = computeFdItems(outputSupplier);
+        builder.addFdItems(fdItems);
+        return builder.build();
+    }
+
+    @Override
+    public ImmutableSet<FdItem> computeFdItems(Supplier<List<Slot>> 
outputSupplier) {
+        Set<NamedExpression> output = 
ImmutableSet.copyOf(outputSupplier.get());
+        ImmutableSet.Builder<FdItem> builder = ImmutableSet.builder();
+
+        ImmutableSet<SlotReference> exprs = output.stream()
+                .filter(SlotReference.class::isInstance)
+                .map(SlotReference.class::cast)
+                .collect(ImmutableSet.toImmutableSet());
+
+        if (qualifier == Qualifier.DISTINCT) {
+            ExprFdItem fdItem = FdFactory.INSTANCE.createExprFdItem(exprs, 
true, exprs);
+            builder.add(fdItem);
+            // inherit from both sides
+            ImmutableSet<FdItem> leftFdItems = child(0).getLogicalProperties()
+                    .getFunctionalDependencies().getFdItems();
+            ImmutableSet<FdItem> rightFdItems = child(1).getLogicalProperties()
+                    .getFunctionalDependencies().getFdItems();
+
+            builder.addAll(leftFdItems);
+            builder.addAll(rightFdItems);
+        }
+
         return builder.build();
     }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalJoin.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalJoin.java
index d6d183408a7..80a40024a88 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalJoin.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalJoin.java
@@ -17,13 +17,17 @@
 
 package org.apache.doris.nereids.trees.plans.logical;
 
+import org.apache.doris.catalog.TableIf;
 import org.apache.doris.common.Pair;
 import org.apache.doris.nereids.hint.DistributeHint;
 import org.apache.doris.nereids.jobs.joinorder.hypergraph.bitmap.LongBitmap;
 import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.properties.FdFactory;
+import org.apache.doris.nereids.properties.FdItem;
 import org.apache.doris.nereids.properties.FunctionalDependencies;
 import org.apache.doris.nereids.properties.FunctionalDependencies.Builder;
 import org.apache.doris.nereids.properties.LogicalProperties;
+import org.apache.doris.nereids.properties.TableFdItem;
 import org.apache.doris.nereids.rules.exploration.join.JoinReorderContext;
 import org.apache.doris.nereids.trees.expressions.EqualPredicate;
 import org.apache.doris.nereids.trees.expressions.EqualTo;
@@ -40,6 +44,7 @@ import 
org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
 import org.apache.doris.nereids.util.ExpressionUtils;
 import org.apache.doris.nereids.util.ImmutableEqualSet;
 import org.apache.doris.nereids.util.JoinUtils;
+import org.apache.doris.nereids.util.PlanUtils;
 import org.apache.doris.nereids.util.Utils;
 
 import com.google.common.base.Preconditions;
@@ -374,14 +379,16 @@ public class LogicalJoin<LEFT_CHILD_TYPE extends Plan, 
RIGHT_CHILD_TYPE extends
     public LogicalJoin<Plan, Plan> withJoinConjuncts(List<Expression> 
hashJoinConjuncts,
             List<Expression> otherJoinConjuncts) {
         return new LogicalJoin<>(joinType, hashJoinConjuncts, 
otherJoinConjuncts, markJoinConjuncts,
-                hint, markJoinSlotReference, Optional.empty(), 
Optional.empty(), children, null);
+                hint, markJoinSlotReference, Optional.empty(), 
Optional.empty(),
+                children, null);
     }
 
     public LogicalJoin<Plan, Plan> withJoinConjuncts(List<Expression> 
hashJoinConjuncts,
                                                      List<Expression> 
otherJoinConjuncts,
                                                      List<Expression> 
markJoinConjuncts) {
         return new LogicalJoin<>(joinType, hashJoinConjuncts, 
otherJoinConjuncts, markJoinConjuncts,
-                hint, markJoinSlotReference, Optional.empty(), 
Optional.empty(), children, null);
+                hint, markJoinSlotReference, Optional.empty(), 
Optional.empty(),
+                children, null);
     }
 
     public LogicalJoin<Plan, Plan> withHashJoinConjunctsAndChildren(
@@ -409,7 +416,8 @@ public class LogicalJoin<LEFT_CHILD_TYPE extends Plan, 
RIGHT_CHILD_TYPE extends
 
     public LogicalJoin<Plan, Plan> withJoinType(JoinType joinType) {
         return new LogicalJoin<>(joinType, hashJoinConjuncts, 
otherJoinConjuncts, markJoinConjuncts,
-                hint, markJoinSlotReference, Optional.empty(), 
Optional.empty(), children, null);
+                hint, markJoinSlotReference, Optional.empty(), 
Optional.empty(),
+                children, null);
     }
 
     public LogicalJoin<Plan, Plan> withTypeChildren(JoinType joinType, Plan 
left, Plan right) {
@@ -510,9 +518,160 @@ public class LogicalJoin<LEFT_CHILD_TYPE extends Plan, 
RIGHT_CHILD_TYPE extends
             }
             
fdBuilder.addUniformSlot(left().getLogicalProperties().getFunctionalDependencies());
         }
+        ImmutableSet<FdItem> fdItems = computeFdItems(outputSupplier);
+        fdBuilder.addFdItems(fdItems);
         return fdBuilder.build();
     }
 
+    @Override
+    public ImmutableSet<FdItem> computeFdItems(Supplier<List<Slot>> 
outputSupplier) {
+        ImmutableSet.Builder<FdItem> builder = ImmutableSet.builder();
+        if (isMarkJoin() || joinType.isNullAwareLeftAntiJoin()
+                || joinType.isFullOuterJoin()
+                || !otherJoinConjuncts.isEmpty()) {
+            return ImmutableSet.of();
+        } else if (joinType.isLeftAntiJoin() || joinType.isLefSemiJoin()) {
+            return 
left().getLogicalProperties().getFunctionalDependencies().getFdItems();
+        } else if (joinType.isRightSemiJoin() || joinType.isRightAntiJoin()) {
+            return 
right().getLogicalProperties().getFunctionalDependencies().getFdItems();
+        } else if (joinType.isInnerJoin()) {
+            Pair<Set<Slot>, Set<Slot>> keys = extractNullRejectHashKeys();
+            if (keys == null) {
+                return ImmutableSet.of();
+            }
+            Set<Slot> leftSlotSet = keys.first;
+            Set<Slot> rightSlotSet = keys.second;
+
+            // enhance the fd from candidate to formal
+            ImmutableSet<FdItem> leftItems = 
left().getLogicalProperties().getFunctionalDependencies().getFdItems();
+            leftItems.stream().filter(e -> e.isCandidate()).forEach(f -> {
+                        if (leftSlotSet.containsAll(f.getParentExprs())) {
+                            f.setCandidate(false);
+                        }
+                    }
+            );
+            boolean isLeftUnique = leftItems.stream().filter(e -> 
e.isCandidate())
+                    .anyMatch(f -> 
leftSlotSet.containsAll(f.getParentExprs()));
+
+            ImmutableSet<FdItem> rightItems = 
right().getLogicalProperties().getFunctionalDependencies().getFdItems();
+            rightItems.stream().filter(e -> e.isCandidate()).forEach(f -> {
+                        if (rightSlotSet.containsAll(f.getParentExprs())) {
+                            f.setCandidate(false);
+                        }
+                    }
+            );
+            boolean isRightUnique = rightItems.stream().filter(e -> 
e.isCandidate())
+                    .anyMatch(f -> 
rightSlotSet.containsAll(f.getParentExprs()));
+
+            if (isRightUnique) {
+                // n to 1 unique
+                ImmutableSet<TableIf> rightTableSet = 
PlanUtils.getTableSet((LogicalPlan) right());
+                leftItems.stream().filter(e -> e.isUnique()).forEach(f -> {
+                    TableFdItem tableFdItem = 
FdFactory.INSTANCE.createTableFdItem(f.getParentExprs(),
+                            f.isUnique(), false, rightTableSet);
+                            builder.add(tableFdItem);
+                        }
+                );
+            } else if (isLeftUnique) {
+                // n to 1 unique
+                ImmutableSet<TableIf> leftTableSet = 
PlanUtils.getTableSet((LogicalPlan) left());
+                rightItems.stream().filter(e -> e.isUnique()).forEach(f -> {
+                            TableFdItem tableFdItem = 
FdFactory.INSTANCE.createTableFdItem(f.getParentExprs(),
+                                    f.isUnique(), false, leftTableSet);
+                            builder.add(tableFdItem);
+                        }
+                );
+            } else {
+                // n to n, set the unique false
+                leftItems.stream().forEach(e ->
+                        e.setUnique(false)
+                );
+                rightItems.stream().forEach(e ->
+                        e.setUnique(false)
+                );
+            }
+            builder.addAll(leftItems);
+            builder.addAll(rightItems);
+            return builder.build();
+        } else if (joinType.isLeftOuterJoin()) {
+            Pair<Set<Slot>, Set<Slot>> keys = extractNullRejectHashKeys();
+            if (keys == null) {
+                return ImmutableSet.of();
+            }
+            Set<Slot> leftSlotSet = keys.first;
+            Set<Slot> rightSlotSet = keys.second;
+
+            // enhance the fd from candidate to formal
+            ImmutableSet<FdItem> leftItems = 
left().getLogicalProperties().getFunctionalDependencies().getFdItems();
+            leftItems.stream().filter(e -> e.isCandidate()).forEach(f -> {
+                        if (leftSlotSet.containsAll(f.getParentExprs())) {
+                            f.setCandidate(false);
+                        }
+                    }
+            );
+
+            ImmutableSet<FdItem> rightItems = 
right().getLogicalProperties().getFunctionalDependencies().getFdItems();
+            boolean isRightUnique = rightItems.stream().filter(e -> 
e.isCandidate())
+                    .anyMatch(f -> 
rightSlotSet.containsAll(f.getParentExprs()));
+            if (isRightUnique) {
+                // n to 1 unique
+                ImmutableSet<TableIf> rightTableSet = 
PlanUtils.getTableSet((LogicalPlan) right());
+                leftItems.stream().filter(e -> e.isUnique()).forEach(f -> {
+                            TableFdItem tableFdItem = 
FdFactory.INSTANCE.createTableFdItem(f.getParentExprs(),
+                                    f.isUnique(), false, rightTableSet);
+                            builder.add(tableFdItem);
+                        }
+                );
+            } else {
+                // n to n, set the unique false
+                leftItems.stream().forEach(e ->
+                        e.setUnique(false)
+                );
+            }
+            builder.addAll(leftItems);
+            return builder.build();
+        } else if (joinType.isRightOuterJoin()) {
+            Pair<Set<Slot>, Set<Slot>> keys = extractNullRejectHashKeys();
+            if (keys == null) {
+                return ImmutableSet.of();
+            }
+            Set<Slot> leftSlotSet = keys.first;
+            Set<Slot> rightSlotSet = keys.second;
+
+            // enhance the fd from candidate to formal
+            ImmutableSet<FdItem> leftItems = 
left().getLogicalProperties().getFunctionalDependencies().getFdItems();
+            boolean isLeftUnique = leftItems.stream().filter(e -> 
e.isCandidate())
+                    .anyMatch(f -> 
leftSlotSet.containsAll(f.getParentExprs()));
+
+            ImmutableSet<FdItem> rightItems = 
right().getLogicalProperties().getFunctionalDependencies().getFdItems();
+            rightItems.stream().filter(e -> e.isCandidate()).forEach(f -> {
+                        if (rightSlotSet.containsAll(f.getParentExprs())) {
+                            f.setCandidate(false);
+                        }
+                    }
+            );
+            if (isLeftUnique) {
+                // n to 1 unique
+                ImmutableSet<TableIf> leftTableSet = 
PlanUtils.getTableSet((LogicalPlan) left());
+                rightItems.stream().filter(e -> e.isUnique()).forEach(f -> {
+                            TableFdItem tableFdItem = 
FdFactory.INSTANCE.createTableFdItem(f.getParentExprs(),
+                                    f.isUnique(), false, leftTableSet);
+                            builder.add(tableFdItem);
+                        }
+                );
+            } else {
+                // n to n, set the unique false
+                rightItems.stream().forEach(e ->
+                        e.setUnique(false)
+                );
+            }
+            builder.addAll(rightItems);
+            return builder.build();
+        } else {
+            return ImmutableSet.of();
+        }
+    }
+
     /**
      * get Equal slot from join
      */
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalLimit.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalLimit.java
index df18b134f10..482c7e26058 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalLimit.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalLimit.java
@@ -18,11 +18,15 @@
 package org.apache.doris.nereids.trees.plans.logical;
 
 import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.properties.ExprFdItem;
+import org.apache.doris.nereids.properties.FdFactory;
+import org.apache.doris.nereids.properties.FdItem;
 import org.apache.doris.nereids.properties.FunctionalDependencies;
 import org.apache.doris.nereids.properties.FunctionalDependencies.Builder;
 import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
 import org.apache.doris.nereids.trees.plans.LimitPhase;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.PlanType;
@@ -32,6 +36,7 @@ import org.apache.doris.nereids.util.Utils;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 
 import java.util.List;
 import java.util.Objects;
@@ -151,8 +156,27 @@ public class LogicalLimit<CHILD_TYPE extends Plan> extends 
LogicalUnary<CHILD_TY
             Builder builder = new Builder();
             outputSupplier.get().forEach(builder::addUniformSlot);
             outputSupplier.get().forEach(builder::addUniqueSlot);
+            ImmutableSet<FdItem> fdItems = computeFdItems(outputSupplier);
+            builder.addFdItems(fdItems);
             fd = builder.build();
         }
         return fd;
     }
+
+    @Override
+    public ImmutableSet<FdItem> computeFdItems(Supplier<List<Slot>> 
outputSupplier) {
+        ImmutableSet<FdItem> fdItems = 
child(0).getLogicalProperties().getFunctionalDependencies().getFdItems();
+        if (getLimit() == 1 && !phase.isLocal()) {
+            ImmutableSet.Builder<FdItem> builder = ImmutableSet.builder();
+            List<Slot> output = outputSupplier.get();
+            ImmutableSet<SlotReference> slotSet = output.stream()
+                    .filter(SlotReference.class::isInstance)
+                    .map(SlotReference.class::cast)
+                    .collect(ImmutableSet.toImmutableSet());
+            ExprFdItem fdItem = FdFactory.INSTANCE.createExprFdItem(slotSet, 
true, slotSet);
+            builder.add(fdItem);
+            fdItems = builder.build();
+        }
+        return fdItems;
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalOneRowRelation.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalOneRowRelation.java
index c0c25f79693..d338be5cc88 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalOneRowRelation.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalOneRowRelation.java
@@ -18,11 +18,15 @@
 package org.apache.doris.nereids.trees.plans.logical;
 
 import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.properties.ExprFdItem;
+import org.apache.doris.nereids.properties.FdFactory;
+import org.apache.doris.nereids.properties.FdItem;
 import org.apache.doris.nereids.properties.FunctionalDependencies;
 import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.NamedExpression;
 import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.PlanType;
 import org.apache.doris.nereids.trees.plans.RelationId;
@@ -31,10 +35,12 @@ import 
org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
 import org.apache.doris.nereids.util.Utils;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
+import java.util.Set;
 import java.util.function.Supplier;
 
 /**
@@ -136,6 +142,22 @@ public class LogicalOneRowRelation extends LogicalRelation 
implements OneRowRela
             builder.addUniformSlot(s);
             builder.addUniqueSlot(s);
         });
+        ImmutableSet<FdItem> fdItems = computeFdItems(outputSupplier);
+        builder.addFdItems(fdItems);
+        return builder.build();
+    }
+
+    @Override
+    public ImmutableSet<FdItem> computeFdItems(Supplier<List<Slot>> 
outputSupplier) {
+        Set<NamedExpression> output = 
ImmutableSet.copyOf(outputSupplier.get());
+        ImmutableSet.Builder<FdItem> builder = ImmutableSet.builder();
+        ImmutableSet<SlotReference> slotSet = output.stream()
+                .filter(SlotReference.class::isInstance)
+                .map(SlotReference.class::cast)
+                .collect(ImmutableSet.toImmutableSet());
+        ExprFdItem fdItem = FdFactory.INSTANCE.createExprFdItem(slotSet, true, 
slotSet);
+        builder.add(fdItem);
+
         return builder.build();
     }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalPlan.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalPlan.java
index 3c058c6e4d7..860bd263acb 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalPlan.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalPlan.java
@@ -17,11 +17,13 @@
 
 package org.apache.doris.nereids.trees.plans.logical;
 
+import org.apache.doris.nereids.properties.FdItem;
 import org.apache.doris.nereids.properties.FunctionalDependencies;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.plans.Plan;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 
 import java.util.List;
 import java.util.Optional;
@@ -60,4 +62,6 @@ public interface LogicalPlan extends Plan {
      *   - PropagateFD: propagate the fd
      */
     FunctionalDependencies computeFuncDeps(Supplier<List<Slot>> 
outputSupplier);
+
+    ImmutableSet<FdItem> computeFdItems(Supplier<List<Slot>> outputSupplier);
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalProject.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalProject.java
index cdacfe95b66..5dda7bb56a9 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalProject.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalProject.java
@@ -20,6 +20,7 @@ package org.apache.doris.nereids.trees.plans.logical;
 import org.apache.doris.nereids.analyzer.Unbound;
 import org.apache.doris.nereids.analyzer.UnboundStar;
 import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.properties.FdItem;
 import org.apache.doris.nereids.properties.FunctionalDependencies;
 import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.trees.expressions.Alias;
@@ -246,6 +247,18 @@ public class LogicalProject<CHILD_TYPE extends Plan> 
extends LogicalUnary<CHILD_
                 }
             }
         });
+        ImmutableSet<FdItem> fdItems = computeFdItems(outputSupplier);
+        builder.addFdItems(fdItems);
+        return builder.build();
+    }
+
+    @Override
+    public ImmutableSet<FdItem> computeFdItems(Supplier<List<Slot>> 
outputSupplier) {
+        ImmutableSet.Builder<FdItem> builder = ImmutableSet.builder();
+
+        ImmutableSet<FdItem> childItems = 
child().getLogicalProperties().getFunctionalDependencies().getFdItems();
+        builder.addAll(childItems);
+
         return builder.build();
     }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalRepeat.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalRepeat.java
index 8ee9e465a29..1c8a468b44b 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalRepeat.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalRepeat.java
@@ -18,6 +18,7 @@
 package org.apache.doris.nereids.trees.plans.logical;
 
 import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.properties.FdItem;
 import org.apache.doris.nereids.properties.FunctionalDependencies;
 import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.trees.expressions.Expression;
@@ -33,6 +34,7 @@ import org.apache.doris.nereids.util.Utils;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 
 import java.util.List;
 import java.util.Objects;
@@ -187,6 +189,18 @@ public class LogicalRepeat<CHILD_TYPE extends Plan> 
extends LogicalUnary<CHILD_T
         outputSupplier.get().stream()
                 
.filter(child(0).getLogicalProperties().getFunctionalDependencies()::isUniform)
                 .forEach(builder::addUniformSlot);
+        ImmutableSet<FdItem> fdItems = computeFdItems(outputSupplier);
+        builder.addFdItems(fdItems);
+        return builder.build();
+    }
+
+    @Override
+    public ImmutableSet<FdItem> computeFdItems(Supplier<List<Slot>> 
outputSupplier) {
+        ImmutableSet.Builder<FdItem> builder = ImmutableSet.builder();
+
+        ImmutableSet<FdItem> childItems = 
child().getLogicalProperties().getFunctionalDependencies().getFdItems();
+        builder.addAll(childItems);
+
         return builder.build();
     }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalSubQueryAlias.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalSubQueryAlias.java
index 73ae70e62bc..68a451f1df1 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalSubQueryAlias.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalSubQueryAlias.java
@@ -18,6 +18,7 @@
 package org.apache.doris.nereids.trees.plans.logical;
 
 import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.properties.FdItem;
 import org.apache.doris.nereids.properties.FunctionalDependencies;
 import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.trees.expressions.Expression;
@@ -30,6 +31,7 @@ import org.apache.doris.nereids.util.Utils;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
 import org.apache.commons.lang3.StringUtils;
 
@@ -172,9 +174,17 @@ public class LogicalSubQueryAlias<CHILD_TYPE extends Plan> 
extends LogicalUnary<
             replaceMap.put(child(0).getOutput().get(i), outputs.get(i));
         }
         builder.replace(replaceMap);
+        ImmutableSet<FdItem> fdItems = computeFdItems(outputSupplier);
+        builder.addFdItems(fdItems);
         return builder.build();
     }
 
+    @Override
+    public ImmutableSet<FdItem> computeFdItems(Supplier<List<Slot>> 
outputSupplier) {
+        // TODO: inherit from child with replaceMap
+        return ImmutableSet.of();
+    }
+
     public void setRelationId(RelationId relationId) {
         this.relationId = relationId;
     }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalTopN.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalTopN.java
index a38778d8d50..5d2656eee59 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalTopN.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalTopN.java
@@ -18,12 +18,16 @@
 package org.apache.doris.nereids.trees.plans.logical;
 
 import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.properties.ExprFdItem;
+import org.apache.doris.nereids.properties.FdFactory;
+import org.apache.doris.nereids.properties.FdItem;
 import org.apache.doris.nereids.properties.FunctionalDependencies;
 import org.apache.doris.nereids.properties.FunctionalDependencies.Builder;
 import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.properties.OrderKey;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.PlanType;
 import org.apache.doris.nereids.trees.plans.algebra.TopN;
@@ -32,6 +36,7 @@ import org.apache.doris.nereids.util.Utils;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 
 import java.util.List;
 import java.util.Objects;
@@ -160,8 +165,27 @@ public class LogicalTopN<CHILD_TYPE extends Plan> extends 
LogicalUnary<CHILD_TYP
             List<Slot> output = outputSupplier.get();
             output.forEach(builder::addUniformSlot);
             output.forEach(builder::addUniqueSlot);
+            ImmutableSet<FdItem> fdItems = computeFdItems(outputSupplier);
+            builder.addFdItems(fdItems);
             fd = builder.build();
         }
         return fd;
     }
+
+    @Override
+    public ImmutableSet<FdItem> computeFdItems(Supplier<List<Slot>> 
outputSupplier) {
+        ImmutableSet<FdItem> fdItems = 
child(0).getLogicalProperties().getFunctionalDependencies().getFdItems();
+        if (getLimit() == 1) {
+            ImmutableSet.Builder<FdItem> builder = ImmutableSet.builder();
+            List<Slot> output = outputSupplier.get();
+            ImmutableSet<SlotReference> slotSet = output.stream()
+                    .filter(SlotReference.class::isInstance)
+                    .map(SlotReference.class::cast)
+                    .collect(ImmutableSet.toImmutableSet());
+            ExprFdItem fdItem = FdFactory.INSTANCE.createExprFdItem(slotSet, 
true, slotSet);
+            builder.add(fdItem);
+            fdItems = builder.build();
+        }
+        return fdItems;
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalUnion.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalUnion.java
index 48d3fdf12ef..a7120cd2709 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalUnion.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalUnion.java
@@ -18,6 +18,9 @@
 package org.apache.doris.nereids.trees.plans.logical;
 
 import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.properties.ExprFdItem;
+import org.apache.doris.nereids.properties.FdFactory;
+import org.apache.doris.nereids.properties.FdItem;
 import org.apache.doris.nereids.properties.FunctionalDependencies;
 import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.trees.expressions.Expression;
@@ -37,6 +40,7 @@ import com.google.common.collect.ImmutableSet;
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
+import java.util.Set;
 import java.util.function.Supplier;
 
 /**
@@ -188,6 +192,26 @@ public class LogicalUnion extends LogicalSetOperation 
implements Union, OutputPr
         }
         FunctionalDependencies.Builder builder = new 
FunctionalDependencies.Builder();
         builder.addUniqueSlot(ImmutableSet.copyOf(outputSupplier.get()));
+        ImmutableSet<FdItem> fdItems = computeFdItems(outputSupplier);
+        builder.addFdItems(fdItems);
+        return builder.build();
+    }
+
+    @Override
+    public ImmutableSet<FdItem> computeFdItems(Supplier<List<Slot>> 
outputSupplier) {
+        Set<NamedExpression> output = 
ImmutableSet.copyOf(outputSupplier.get());
+        ImmutableSet.Builder<FdItem> builder = ImmutableSet.builder();
+
+        ImmutableSet<SlotReference> exprs = output.stream()
+                .filter(SlotReference.class::isInstance)
+                .map(SlotReference.class::cast)
+                .collect(ImmutableSet.toImmutableSet());
+
+        if (qualifier == Qualifier.DISTINCT) {
+            ExprFdItem fdItem = FdFactory.INSTANCE.createExprFdItem(exprs, 
true, exprs);
+            builder.add(fdItem);
+        }
+
         return builder.build();
     }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalWindow.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalWindow.java
index e8c16b49026..086643e9f8b 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalWindow.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalWindow.java
@@ -18,6 +18,7 @@
 package org.apache.doris.nereids.trees.plans.logical;
 
 import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.properties.FdItem;
 import org.apache.doris.nereids.properties.FunctionalDependencies;
 import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.trees.expressions.Expression;
@@ -234,7 +235,6 @@ public class LogicalWindow<CHILD_TYPE extends Plan> extends 
LogicalUnary<CHILD_T
         }
         WindowExpression windowExpr = (WindowExpression) 
namedExpression.child(0);
         List<Expression> partitionKeys = windowExpr.getPartitionKeys();
-
         // Now we only support slot type keys
         if (!partitionKeys.stream().allMatch(Slot.class::isInstance)) {
             return;
@@ -260,6 +260,24 @@ public class LogicalWindow<CHILD_TYPE extends Plan> 
extends LogicalUnary<CHILD_T
         }
     }
 
+    private void updateFuncDepsByWindowExpr(NamedExpression namedExpression, 
ImmutableSet.Builder<FdItem> builder) {
+        if (namedExpression.children().size() != 1 || 
!(namedExpression.child(0) instanceof WindowExpression)) {
+            return;
+        }
+        WindowExpression windowExpr = (WindowExpression) 
namedExpression.child(0);
+        List<Expression> partitionKeys = windowExpr.getPartitionKeys();
+
+        // Now we only support slot type keys
+        if (!partitionKeys.stream().allMatch(Slot.class::isInstance)) {
+            return;
+        }
+        //ImmutableSet<Slot> slotSet = partitionKeys.stream()
+        //        .map(s -> (Slot) s)
+        //        .collect(ImmutableSet.toImmutableSet());
+        // TODO: if partition by keys are unique, output is uniform
+        // TODO: if partition by keys are uniform, output is unique
+    }
+
     @Override
     public FunctionalDependencies computeFuncDeps(Supplier<List<Slot>> 
outputSupplier) {
         FunctionalDependencies.Builder builder = new 
FunctionalDependencies.Builder(
@@ -267,6 +285,21 @@ public class LogicalWindow<CHILD_TYPE extends Plan> 
extends LogicalUnary<CHILD_T
         for (NamedExpression namedExpression : windowExpressions) {
             updateFuncDepsByWindowExpr(namedExpression, builder);
         }
+        ImmutableSet<FdItem> fdItems = computeFdItems(outputSupplier);
+        builder.addFdItems(fdItems);
+        return builder.build();
+    }
+
+    @Override
+    public ImmutableSet<FdItem> computeFdItems(Supplier<List<Slot>> 
outputSupplier) {
+        ImmutableSet.Builder<FdItem> builder = ImmutableSet.builder();
+        ImmutableSet<FdItem> childItems = 
child().getLogicalProperties().getFunctionalDependencies().getFdItems();
+        builder.addAll(childItems);
+
+        for (NamedExpression namedExpression : windowExpressions) {
+            updateFuncDepsByWindowExpr(namedExpression, builder);
+        }
+
         return builder.build();
     }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/PlanUtils.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/PlanUtils.java
index 405d0282d8c..45dbd5b8e82 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/PlanUtils.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/PlanUtils.java
@@ -17,19 +17,25 @@
 
 package org.apache.doris.nereids.util;
 
+import org.apache.doris.catalog.TableIf;
 import org.apache.doris.nereids.trees.expressions.ComparisonPredicate;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.NamedExpression;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
+import org.apache.doris.nereids.trees.plans.logical.LogicalCatalogRelation;
 import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
 import org.apache.doris.nereids.trees.plans.logical.LogicalLimit;
+import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
 
+import java.util.Collection;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
@@ -96,4 +102,23 @@ public class PlanUtils {
         }
         return plan;
     }
+
+    public static Set<LogicalCatalogRelation> 
getLogicalScanFromRootPlan(LogicalPlan rootPlan) {
+        Set<LogicalCatalogRelation> tableSet = new HashSet<>();
+        tableSet.addAll((Collection<? extends LogicalCatalogRelation>) rootPlan
+                .collect(LogicalCatalogRelation.class::isInstance));
+        return tableSet;
+    }
+
+    /**
+     * get table set from plan root.
+     */
+    public static ImmutableSet<TableIf> getTableSet(LogicalPlan plan) {
+        Set<LogicalCatalogRelation> tableSet = new HashSet<>();
+        tableSet.addAll((Collection<? extends LogicalCatalogRelation>) plan
+                .collect(LogicalCatalogRelation.class::isInstance));
+        ImmutableSet<TableIf> resultSet = tableSet.stream().map(e -> 
e.getTable())
+                .collect(ImmutableSet.toImmutableSet());
+        return resultSet;
+    }
 }
diff --git 
a/regression-test/data/nereids_tpcds_shape_sf100_p0/constraints/query23.out 
b/regression-test/data/nereids_tpcds_shape_sf100_p0/constraints/query23.out
index 8491810ebf8..01138e6aea9 100644
--- a/regression-test/data/nereids_tpcds_shape_sf100_p0/constraints/query23.out
+++ b/regression-test/data/nereids_tpcds_shape_sf100_p0/constraints/query23.out
@@ -4,22 +4,21 @@ PhysicalCteAnchor ( cteId=CTEId#0 )
 --PhysicalCteProducer ( cteId=CTEId#0 )
 ----PhysicalProject
 ------filter((cnt > 4))
---------hashAgg[GLOBAL]
-----------PhysicalDistribute[DistributionSpecHash]
-------------hashAgg[LOCAL]
---------------PhysicalProject
-----------------hashJoin[INNER_JOIN] hashCondition=((store_sales.ss_item_sk = 
item.i_item_sk)) otherCondition=() build RFs:RF1 i_item_sk->[ss_item_sk]
-------------------PhysicalProject
---------------------hashJoin[INNER_JOIN] 
hashCondition=((store_sales.ss_sold_date_sk = date_dim.d_date_sk)) 
otherCondition=() build RFs:RF0 d_date_sk->[ss_sold_date_sk]
-----------------------PhysicalProject
-------------------------PhysicalOlapScan[store_sales] apply RFs: RF0 RF1
-----------------------PhysicalDistribute[DistributionSpecReplicated]
-------------------------PhysicalProject
---------------------------filter(d_year IN (2000, 2001, 2002, 2003))
-----------------------------PhysicalOlapScan[date_dim]
-------------------PhysicalDistribute[DistributionSpecReplicated]
+--------hashAgg[LOCAL]
+----------PhysicalProject
+------------hashJoin[INNER_JOIN] hashCondition=((store_sales.ss_item_sk = 
item.i_item_sk)) otherCondition=() build RFs:RF1 i_item_sk->[ss_item_sk]
+--------------PhysicalDistribute[DistributionSpecHash]
+----------------PhysicalProject
+------------------hashJoin[INNER_JOIN] 
hashCondition=((store_sales.ss_sold_date_sk = date_dim.d_date_sk)) 
otherCondition=() build RFs:RF0 d_date_sk->[ss_sold_date_sk]
 --------------------PhysicalProject
-----------------------PhysicalOlapScan[item]
+----------------------PhysicalOlapScan[store_sales] apply RFs: RF0 RF1
+--------------------PhysicalDistribute[DistributionSpecReplicated]
+----------------------PhysicalProject
+------------------------filter(d_year IN (2000, 2001, 2002, 2003))
+--------------------------PhysicalOlapScan[date_dim]
+--------------PhysicalDistribute[DistributionSpecHash]
+----------------PhysicalProject
+------------------PhysicalOlapScan[item]
 --PhysicalCteAnchor ( cteId=CTEId#2 )
 ----PhysicalCteProducer ( cteId=CTEId#2 )
 ------PhysicalProject
@@ -56,10 +55,7 @@ PhysicalCteAnchor ( cteId=CTEId#0 )
 --------------hashAgg[LOCAL]
 ----------------PhysicalUnion
 ------------------PhysicalProject
---------------------hashJoin[RIGHT_SEMI_JOIN] 
hashCondition=((catalog_sales.cs_item_sk = frequent_ss_items.item_sk)) 
otherCondition=() build RFs:RF4 cs_item_sk->[item_sk]
-----------------------PhysicalDistribute[DistributionSpecHash]
-------------------------PhysicalProject
---------------------------PhysicalCteConsumer ( cteId=CTEId#0 ) apply RFs: RF4
+--------------------hashJoin[LEFT_SEMI_JOIN] 
hashCondition=((catalog_sales.cs_item_sk = frequent_ss_items.item_sk)) 
otherCondition=()
 ----------------------PhysicalDistribute[DistributionSpecHash]
 ------------------------PhysicalProject
 --------------------------hashJoin[LEFT_SEMI_JOIN] 
hashCondition=((catalog_sales.cs_bill_customer_sk = 
best_ss_customer.c_customer_sk)) otherCondition=()
@@ -74,18 +70,18 @@ PhysicalCteAnchor ( cteId=CTEId#0 )
 ----------------------------PhysicalDistribute[DistributionSpecHash]
 ------------------------------PhysicalProject
 --------------------------------PhysicalCteConsumer ( cteId=CTEId#2 )
-------------------PhysicalProject
---------------------hashJoin[RIGHT_SEMI_JOIN] 
hashCondition=((web_sales.ws_item_sk = frequent_ss_items.item_sk)) 
otherCondition=() build RFs:RF6 ws_item_sk->[item_sk]
 ----------------------PhysicalDistribute[DistributionSpecHash]
 ------------------------PhysicalProject
---------------------------PhysicalCteConsumer ( cteId=CTEId#0 ) apply RFs: RF6
+--------------------------PhysicalCteConsumer ( cteId=CTEId#0 )
+------------------PhysicalProject
+--------------------hashJoin[LEFT_SEMI_JOIN] 
hashCondition=((web_sales.ws_item_sk = frequent_ss_items.item_sk)) 
otherCondition=()
 ----------------------PhysicalDistribute[DistributionSpecHash]
 ------------------------PhysicalProject
 --------------------------hashJoin[LEFT_SEMI_JOIN] 
hashCondition=((web_sales.ws_bill_customer_sk = 
best_ss_customer.c_customer_sk)) otherCondition=()
 ----------------------------PhysicalDistribute[DistributionSpecHash]
-------------------------------hashJoin[INNER_JOIN] 
hashCondition=((web_sales.ws_sold_date_sk = date_dim.d_date_sk)) 
otherCondition=() build RFs:RF5 d_date_sk->[ws_sold_date_sk]
+------------------------------hashJoin[INNER_JOIN] 
hashCondition=((web_sales.ws_sold_date_sk = date_dim.d_date_sk)) 
otherCondition=() build RFs:RF4 d_date_sk->[ws_sold_date_sk]
 --------------------------------PhysicalProject
-----------------------------------PhysicalOlapScan[web_sales] apply RFs: RF5
+----------------------------------PhysicalOlapScan[web_sales] apply RFs: RF4
 --------------------------------PhysicalDistribute[DistributionSpecReplicated]
 ----------------------------------PhysicalProject
 ------------------------------------filter((date_dim.d_moy = 5) and 
(date_dim.d_year = 2000))
@@ -93,4 +89,7 @@ PhysicalCteAnchor ( cteId=CTEId#0 )
 ----------------------------PhysicalDistribute[DistributionSpecHash]
 ------------------------------PhysicalProject
 --------------------------------PhysicalCteConsumer ( cteId=CTEId#2 )
+----------------------PhysicalDistribute[DistributionSpecHash]
+------------------------PhysicalProject
+--------------------------PhysicalCteConsumer ( cteId=CTEId#0 )
 
diff --git 
a/regression-test/suites/nereids_tpcds_shape_sf100_p0/constraints/query23.groovy
 
b/regression-test/suites/nereids_tpcds_shape_sf100_p0/constraints/query23.groovy
index 33d3f5d2176..43e5a404bb8 100644
--- 
a/regression-test/suites/nereids_tpcds_shape_sf100_p0/constraints/query23.groovy
+++ 
b/regression-test/suites/nereids_tpcds_shape_sf100_p0/constraints/query23.groovy
@@ -29,6 +29,8 @@ suite("query23") {
     sql 'set forbid_unknown_col_stats=true'
     sql 'set enable_nereids_timeout = false'
     sql 'set enable_runtime_filter_prune=false'
+    sql 'set runtime_filter_type=8'
+    sql "set enable_nereids_rules='ELIMINATE_GROUP_BY_KEY'"
     def ds = """with frequent_ss_items as 
  (select substr(i_item_desc,1,30) itemdesc,i_item_sk item_sk,d_date 
solddate,count(*) cnt
   from store_sales


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to