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

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


The following commit(s) were added to refs/heads/branch-4.1 by this push:
     new f2156159c19 branch-4.1 : [fix](join) Do not push a no-input project 
item into a join whose condition refers to it- #69748 (#69814)
f2156159c19 is described below

commit f2156159c19854281fe0ea93dc52e68c4d484e29
Author: starocean999 <[email protected]>
AuthorDate: Mon Nov 2 09:23:04 2026 +0800

    branch-4.1 : [fix](join) Do not push a no-input project item into a join 
whose condition refers to it- #69748 (#69814)
    
    pick https://github.com/apache/doris/pull/69748
    
    Issue Number: close #xxx
    
    Related PR: #xxx
    
    Problem Summary:
    
    ### Release note
    
    None
    
    ### Check List (For Author)
    
    - Test <!-- At least one of them must be included. -->
        - [ ] Regression test
        - [ ] Unit Test
        - [ ] Manual test (add detailed scripts or steps below)
        - [ ] No need to test or manual test. Explain why:
    - [ ] This is a refactor/code format and no logic has been changed.
            - [ ] Previous test can cover this change.
            - [ ] No code files have been changed.
            - [ ] Other reason <!-- Add your reason?  -->
    
    - Behavior changed:
        - [ ] No.
        - [ ] Yes. <!-- Explain the behavior change -->
    
    - Does this need documentation?
        - [ ] No.
    - [ ] Yes. <!-- Add document PR link here. eg:
    https://github.com/apache/doris-website/pull/1214 -->
    
    ### Check List (For Reviewer who merge this PR)
    
    - [ ] Confirm the release note
    - [ ] Confirm test cases
    - [ ] Confirm document
    - [ ] Add branch pick label <!-- Add branch pick label that this PR
    should merge into -->
---
 .../doris/nereids/rules/exploration/CBOUtils.java  |  63 +++++++++++
 .../join/PushDownProjectThroughInnerOuterJoin.java |  20 +++-
 .../join/PushDownProjectThroughSemiJoin.java       |  30 +++++-
 .../PushDownProjectThroughInnerOuterJoinTest.java  |  22 ++++
 .../join/PushDownProjectThroughSemiJoinTest.java   |  21 ++++
 .../distinct_split_constant_join.out               |   4 +
 .../distinct_split_constant_join.groovy            | 118 +++++++++++++++++++++
 7 files changed, 272 insertions(+), 6 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/CBOUtils.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/CBOUtils.java
index 0ac0c6ea415..58706671456 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/CBOUtils.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/CBOUtils.java
@@ -26,6 +26,8 @@ import 
org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
 import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
 import org.apache.doris.nereids.util.Utils;
 
+import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -42,6 +44,67 @@ public class CBOUtils {
                 .collect(Collectors.toSet());
     }
 
+    /**
+     * Collect the exprIds of the project items which read no input, e.g. the 
constant materialized by constant
+     * propagation, now() or 1 + 1.
+     *
+     * <p>Such an item can not be pushed into a child of a join: the join 
condition may refer to it, and pushing the
+     * item into one side while the condition keeps referring to it from the 
other side makes both children of the
+     * join output the same exprId. PhysicalHashJoin#getHashConjunctsExprIds 
splits the join keys by exprId only, so
+     * both keys would be taken as the keys of one side and the required hash 
properties of the other side stay
+     * empty.
+     */
+    public static Set<ExprId> 
collectNoInputProjectItemIds(List<NamedExpression> projectItems) {
+        Set<ExprId> noInputProjectItemIds = new HashSet<>();
+        for (NamedExpression item : projectItems) {
+            if (item.getInputSlotExprIds().isEmpty()) {
+                noInputProjectItemIds.add(item.getExprId());
+            }
+        }
+        return noInputProjectItemIds;
+    }
+
+    /** whether the join condition refers to any of the given exprIds */
+    public static boolean joinConditionRefersTo(
+            LogicalJoin<? extends Plan, ? extends Plan> join, Set<ExprId> 
exprIds) {
+        if (exprIds.isEmpty()) {
+            return false;
+        }
+        for (Expression conjunct : join.getHashJoinConjuncts()) {
+            if (refersToAny(conjunct, exprIds)) {
+                return true;
+            }
+        }
+        for (Expression conjunct : join.getOtherJoinConjuncts()) {
+            if (refersToAny(conjunct, exprIds)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static boolean refersToAny(Expression expression, Set<ExprId> 
exprIds) {
+        for (Slot slot : expression.getInputSlots()) {
+            if (exprIds.contains(slot.getExprId())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * The project above a join after pushing its items down: an item which 
reads no input stays in this project and
+     * keeps its expression, an item which has been pushed down is referenced 
by its output slot of the join below.
+     */
+    public static List<NamedExpression> projectAboveJoin(
+            LogicalProject<?> project, Set<ExprId> noInputProjectItemIds) {
+        List<NamedExpression> projectItems = new 
ArrayList<>(project.getProjects().size());
+        for (NamedExpression item : project.getProjects()) {
+            projectItems.add(noInputProjectItemIds.contains(item.getExprId()) 
? item : item.toSlot());
+        }
+        return projectItems;
+    }
+
     public static Plan newProjectIfNeeded(Set<ExprId> requiredExprIds, Plan 
plan) {
         if (requiredExprIds.equals(plan.getOutputExprIdSet())) {
             return plan;
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/PushDownProjectThroughInnerOuterJoin.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/PushDownProjectThroughInnerOuterJoin.java
index 905af662a2d..a328a615d51 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/PushDownProjectThroughInnerOuterJoin.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/PushDownProjectThroughInnerOuterJoin.java
@@ -92,7 +92,7 @@ public class PushDownProjectThroughInnerOuterJoin implements 
ExplorationRuleFact
     }
 
     private Plan pushdownProject(LogicalProject<LogicalJoin<GroupPlan, 
GroupPlan>> project) {
-        LogicalJoin<GroupPlan, GroupPlan> join = project.child();
+        LogicalJoin<? extends Plan, ? extends Plan> join = project.child();
         Set<ExprId> aOutputExprIdSet = join.left().getOutputExprIdSet();
         Set<ExprId> bOutputExprIdSet = join.right().getOutputExprIdSet();
 
@@ -124,7 +124,21 @@ public class PushDownProjectThroughInnerOuterJoin 
implements ExplorationRuleFact
                     e instanceof Slot ? childrenSlots.get((Slot) e) : e
             )).map(e -> (NamedExpression) e).collect(Collectors.toList());
         }
+        // A project item which reads no input, e.g. the constant materialized 
by constant propagation, now() or
+        // 1 + 1, must not be pushed into either side of the join. If the join 
condition refers to such an item,
+        // pushing it into one side would make both children of the join 
output the same exprId, because the
+        // condition keeps referring to it from the other side. Such an item 
is evaluated from nothing, so keeping
+        // it in the project above the join loses no optimization, but the 
condition can not be folded either: a
+        // constant join key makes one side contribute no key at all, which 
breaks the same property derivation.
+        // So the whole pushdown is rejected for this branch, the join keeps 
its columns and stays valid.
+        Set<ExprId> noInputProjectItemIds = 
CBOUtils.collectNoInputProjectItemIds(projects);
+        if (CBOUtils.joinConditionRefersTo(join, noInputProjectItemIds)) {
+            return null;
+        }
         for (NamedExpression namedExpression : projects) {
+            if (noInputProjectItemIds.contains(namedExpression.getExprId())) {
+                continue;
+            }
             Set<ExprId> usedExprIds = namedExpression.getInputSlotExprIds();
             if (aOutputExprIdSet.containsAll(usedExprIds)) {
                 aProjects.add(namedExpression);
@@ -156,7 +170,7 @@ public class PushDownProjectThroughInnerOuterJoin 
implements ExplorationRuleFact
 
         if (!rightContains) {
             Plan newJoin = join.withChildren(newLeft, join.right());
-            return new 
LogicalProject<>(ImmutableList.copyOf(project.getOutput()), newJoin);
+            return new LogicalProject<>(CBOUtils.projectAboveJoin(project, 
noInputProjectItemIds), newJoin);
         }
 
         Builder<NamedExpression> newBProject = 
ImmutableList.<NamedExpression>builder().addAll(bProjects);
@@ -167,7 +181,7 @@ public class PushDownProjectThroughInnerOuterJoin 
implements ExplorationRuleFact
         Plan newRight = new LogicalProject<>(newBProject.build(), 
join.right());
 
         Plan newJoin = join.withChildren(newLeft, newRight);
-        return new LogicalProject<>(ImmutableList.copyOf(project.getOutput()), 
newJoin);
+        return new LogicalProject<>(CBOUtils.projectAboveJoin(project, 
noInputProjectItemIds), newJoin);
     }
 
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/PushDownProjectThroughSemiJoin.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/PushDownProjectThroughSemiJoin.java
index 11efc82fc21..e7ab2570dac 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/PushDownProjectThroughSemiJoin.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/PushDownProjectThroughSemiJoin.java
@@ -21,6 +21,7 @@ import org.apache.doris.nereids.rules.Rule;
 import org.apache.doris.nereids.rules.RuleType;
 import org.apache.doris.nereids.rules.exploration.CBOUtils;
 import org.apache.doris.nereids.rules.exploration.ExplorationRuleFactory;
+import org.apache.doris.nereids.trees.expressions.ExprId;
 import org.apache.doris.nereids.trees.expressions.NamedExpression;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.plans.GroupPlan;
@@ -66,6 +67,9 @@ public class PushDownProjectThroughSemiJoin implements 
ExplorationRuleFactory {
                             return null;
                         }
                         Plan newLeft = pushdownProject(project);
+                        if (newLeft == null) {
+                            return null;
+                        }
                         return topJoin.withChildren(newLeft, topJoin.right());
                     
}).toRule(RuleType.PUSH_DOWN_PROJECT_THROUGH_SEMI_JOIN_LEFT),
 
@@ -80,6 +84,9 @@ public class PushDownProjectThroughSemiJoin implements 
ExplorationRuleFactory {
                             return null;
                         }
                         Plan newRight = pushdownProject(project);
+                        if (newRight == null) {
+                            return null;
+                        }
                         return topJoin.withChildren(topJoin.left(), newRight);
                     
}).toRule(RuleType.PUSH_DOWN_PROJECT_THROUGH_SEMI_JOIN_RIGHT)
                 );
@@ -99,16 +106,33 @@ public class PushDownProjectThroughSemiJoin implements 
ExplorationRuleFactory {
 
     private Plan pushdownProject(LogicalProject<LogicalJoin<GroupPlan, 
GroupPlan>> project) {
         LogicalJoin<GroupPlan, GroupPlan> join = project.child();
+        // A project item which reads no input, e.g. the constant materialized 
by constant propagation, now() or
+        // 1 + 1, must not be pushed into the join: the join condition may 
refer to such an item, and pushing it
+        // into the child while the condition keeps referring to it from the 
other side makes both children of the
+        // join output the same exprId. Keeping the item above the join while 
the condition refers to it is also
+        // invalid, so the pushdown is rejected for this branch.
+        Set<ExprId> noInputProjectItemIds = 
CBOUtils.collectNoInputProjectItemIds(project.getProjects());
+        if (CBOUtils.joinConditionRefersTo(join, noInputProjectItemIds)) {
+            return null;
+        }
         Set<Slot> conditionLeftSlots = CBOUtils.joinChildConditionSlots(join, 
true);
 
-        List<NamedExpression> newProject = new 
ArrayList<>(project.getProjects());
-        Set<Slot> projectUsedSlots = 
project.getProjects().stream().map(NamedExpression::toSlot)
+        List<NamedExpression> newProject = new ArrayList<>();
+        for (NamedExpression expr : project.getProjects()) {
+            if (!noInputProjectItemIds.contains(expr.getExprId())) {
+                newProject.add(expr);
+            }
+        }
+        if (newProject.isEmpty()) {
+            return null;
+        }
+        Set<Slot> projectUsedSlots = 
newProject.stream().map(NamedExpression::toSlot)
                 .collect(Collectors.toSet());
         conditionLeftSlots.stream().filter(slot -> 
!projectUsedSlots.contains(slot))
                 .forEach(newProject::add);
         Plan newLeft = new LogicalProject<>(newProject, join.left());
 
         Plan newJoin = join.withChildren(newLeft, join.right());
-        return new LogicalProject<>(ImmutableList.copyOf(project.getOutput()), 
newJoin);
+        return new LogicalProject<>(CBOUtils.projectAboveJoin(project, 
noInputProjectItemIds), newJoin);
     }
 }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/join/PushDownProjectThroughInnerOuterJoinTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/join/PushDownProjectThroughInnerOuterJoinTest.java
index 4c1370da1c0..e11c0c97aab 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/join/PushDownProjectThroughInnerOuterJoinTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/join/PushDownProjectThroughInnerOuterJoinTest.java
@@ -45,6 +45,28 @@ class PushDownProjectThroughInnerOuterJoinTest implements 
MemoPatternMatchSuppor
 
     private ConnectContext connectContext = 
MemoTestUtils.createConnectContext();
 
+    @Test
+    public void 
rejectPushdownWhenProjectItemReadsNoSlotAndIsUsedByJoinCondition() {
+        // project '014' AS `id`#t1.id, t1.name, t2.id, t2.name
+        // the constant reads no slot and reuses the exprId of t1.id, and the 
join below refers to t1.id:
+        // pushing the project into the join would make both children of the 
join output t1.id
+        List<NamedExpression> projectExprs = ImmutableList.of(
+                new Alias(scan1.getOutput().get(0).getExprId(), 
Literal.of(14), scan1.getOutput().get(0).getName()),
+                scan1.getOutput().get(1),
+                scan2.getOutput().get(0),
+                scan2.getOutput().get(1)
+        );
+        LogicalPlan plan = new LogicalPlanBuilder(scan1)
+                .join(scan2, JoinType.INNER_JOIN, Pair.of(0, 0))
+                .projectExprs(projectExprs)
+                .join(scan3, JoinType.INNER_JOIN, Pair.of(1, 1))
+                .build();
+
+        PlanChecker.from(connectContext, plan)
+                
.applyExploration(PushDownProjectThroughInnerOuterJoin.INSTANCE.buildRules())
+                .checkMemo(memo -> Assertions.assertEquals(1, 
memo.getRoot().getLogicalExpressions().size()));
+    }
+
     @Test
     public void pushBothSide() {
         pushBothSideHelper(JoinType.INNER_JOIN);
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/join/PushDownProjectThroughSemiJoinTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/join/PushDownProjectThroughSemiJoinTest.java
index 7f20bc3401e..030458737d6 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/join/PushDownProjectThroughSemiJoinTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/join/PushDownProjectThroughSemiJoinTest.java
@@ -33,6 +33,7 @@ import org.apache.doris.nereids.util.PlanChecker;
 import org.apache.doris.nereids.util.PlanConstructor;
 
 import com.google.common.collect.ImmutableList;
+import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
 import java.util.List;
@@ -42,6 +43,26 @@ class PushDownProjectThroughSemiJoinTest implements 
MemoPatternMatchSupported {
     private final LogicalOlapScan scan2 = 
PlanConstructor.newLogicalOlapScan(1, "t2", 0);
     private final LogicalOlapScan scan3 = 
PlanConstructor.newLogicalOlapScan(2, "t3", 0);
 
+    @Test
+    public void 
rejectPushdownWhenProjectItemReadsNoSlotAndIsUsedByJoinCondition() {
+        // project '014' AS `id`#t1.id, t1.name
+        // the constant reads no slot and reuses the exprId of t1.id, and the 
semi join below refers to t1.id:
+        // pushing the project into the semi join would make both children of 
the join output t1.id
+        List<NamedExpression> projectExprs = ImmutableList.of(
+                new Alias(scan1.getOutput().get(0).getExprId(), 
Literal.of(14), scan1.getOutput().get(0).getName()),
+                scan1.getOutput().get(1)
+        );
+        LogicalPlan plan = new LogicalPlanBuilder(scan1)
+                .join(scan2, JoinType.LEFT_SEMI_JOIN, Pair.of(0, 0))
+                .projectExprs(projectExprs)
+                .join(scan3, JoinType.INNER_JOIN, Pair.of(1, 1))
+                .build();
+
+        PlanChecker.from(MemoTestUtils.createConnectContext(), plan)
+                
.applyExploration(PushDownProjectThroughSemiJoin.INSTANCE.buildRules())
+                .checkMemo(memo -> Assertions.assertEquals(1, 
memo.getRoot().getLogicalExpressions().size()));
+    }
+
     @Test
     public void pushdownProject() {
         // project (t1.id + 1) as alias, t1.name
diff --git 
a/regression-test/data/nereids_rules_p0/distinct_split/distinct_split_constant_join.out
 
b/regression-test/data/nereids_rules_p0/distinct_split/distinct_split_constant_join.out
new file mode 100644
index 00000000000..22eaec4ddd7
--- /dev/null
+++ 
b/regression-test/data/nereids_rules_p0/distinct_split/distinct_split_constant_join.out
@@ -0,0 +1,4 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !constant_predicate_join --
+014    C22035  shop_99 191     322     2000000 50      100
+
diff --git 
a/regression-test/suites/nereids_rules_p0/distinct_split/distinct_split_constant_join.groovy
 
b/regression-test/suites/nereids_rules_p0/distinct_split/distinct_split_constant_join.groovy
new file mode 100644
index 00000000000..dab195a8415
--- /dev/null
+++ 
b/regression-test/suites/nereids_rules_p0/distinct_split/distinct_split_constant_join.groovy
@@ -0,0 +1,118 @@
+// 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.
+
+suite("distinct_split_constant_join") {
+    sql "set enable_nereids_planner=true"
+    sql "set be_number_for_test=1"
+    // the project above the join materializes the constant of a column which 
the join condition still refers to.
+    // Such an item must not be pushed into the join, otherwise both children 
of the join output the same exprId
+    // and the required hash properties of one side stay empty, see 
ProjectJoinReorderHelper#normalize.
+    sql "set enable_dphyp_optimizer=true"
+    // force the shuffle plan, so the required distribution of the join 
children is derived instead of being
+    // satisfied by the colocate plan; that is the path where a duplicated 
exprId across the join children shows up
+    sql "set disable_colocate_plan=true"
+    sql "set multi_distinct_strategy=2"
+
+    sql "drop table if exists test_split_constant_base"
+    sql """create table test_split_constant_base (
+              org_code varchar(20),
+              dc_code varchar(20),
+              date_key date,
+              people_count bigint,
+              optimum_store_count bigint
+            ) engine=olap
+            duplicate key(org_code, dc_code, date_key)
+            distributed by hash(org_code) buckets 10
+            properties("replication_num"="1")"""
+
+    sql "drop table if exists test_split_constant_sign"
+    sql """create table test_split_constant_sign (
+              org_code varchar(20),
+              dc_code varchar(20),
+              date_key date,
+              poi_code varchar(40),
+              if_optimum int,
+              sign_duration bigint
+            ) engine=olap
+            duplicate key(org_code, dc_code, date_key, poi_code)
+            distributed by hash(org_code) buckets 10
+            properties("replication_num"="1")"""
+
+    sql "drop table if exists test_split_constant_shop"
+    sql """create table test_split_constant_shop (
+              org_code varchar(20),
+              dc_code varchar(20),
+              dc_name varchar(100)
+            ) engine=olap
+            duplicate key(org_code, dc_code, dc_name)
+            distributed by hash(org_code) buckets 10
+            properties("replication_num"="1")"""
+
+    sql """insert into test_split_constant_base
+           select '014', 'C22035', date_add('2026-10-01', number % 31), number 
% 7, number % 11
+           from numbers("number"="2000")"""
+    sql """insert into test_split_constant_sign
+           select '014', 'C22035', date_add('2026-10-01', number % 31),
+                  concat('poi_', number % 100), number % 2, number % 100
+           from numbers("number"="20000")"""
+    sql """insert into test_split_constant_shop
+           select '014', 'C22035', concat('shop_', number)
+           from numbers("number"="100")"""
+
+    sql "analyze table test_split_constant_base with sync"
+    sql "analyze table test_split_constant_sign with sync"
+    sql "analyze table test_split_constant_shop with sync"
+
+    // the inner aggregate is split into one cte per distinct function, and 
every split group by the same
+    // org_code / dc_code columns, so the outer query joins on a column whose 
exprId is defined twice.
+    // `org_code in ('014')` and `dc_code in ('C22035')` make those columns 
constant, so constant propagation
+    // materializes them as `'014' AS org_code` in the project above the join.
+    order_qt_constant_predicate_join """
+        select
+            t1.org_code,
+            t1.dc_code,
+            max(t2.dc_name) as dc_name,
+            sum(ifnull(t1.people_count, 0)) as people_count,
+            sum(ifnull(t1.optimum_store_count, 0)) as optimum_store_count,
+            max(ifnull(t2.sign_count, 0)) as sign_count,
+            max(ifnull(t2.sign_optimum_store_count, 0)) as 
sign_optimum_store_count,
+            max(ifnull(t2.sign_store_count, 0)) as sign_store_count
+        from test_split_constant_base t1
+        inner join (
+            select
+                t1.org_code,
+                t1.dc_code,
+                max(t2.dc_name) as dc_name,
+                count(1) as sign_count,
+                count(distinct if(t1.if_optimum = 1, t1.poi_code, null)) as 
sign_optimum_store_count,
+                count(distinct t1.poi_code) as sign_store_count
+            from test_split_constant_sign t1
+            left join (
+                select distinct org_code, dc_code, dc_name from 
test_split_constant_shop
+            ) t2 on t1.org_code = t2.org_code and t1.dc_code = t2.dc_code
+            where t1.date_key >= '2026-10-01' and t1.date_key <= '2026-10-31'
+              and t1.org_code in ('014')
+              and t1.dc_code in ('C22035')
+            group by t1.org_code, t1.dc_code
+        ) t2 on t1.org_code = t2.org_code and t1.dc_code = t2.dc_code
+        where t1.date_key = '2026-10-24'
+          and t1.org_code in ('014')
+          and t1.dc_code in ('C22035')
+        group by t1.org_code, t1.dc_code
+        order by 1, 2
+    """
+}


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

Reply via email to