Uh oh!
There was an error while loading. Please reload this page.
[SPARK-30768][SQL] Constraints inferred from inequality attributes - #27518
[SPARK-30768][SQL] Constraints inferred from inequality attributes#27518wangyum wants to merge 11 commits into
Conversation
SparkQA
commented
Feb 10, 2020
Test build #118120 has finished for PR 27518 at commit
|
wangyum
commented
Feb 10, 2020
retest this please |
Uh oh!
There was an error while loading. Please reload this page.
SparkQA
commented
Feb 10, 2020
Test build #118131 has finished for PR 27518 at commit
|
wangyum
commented
Feb 10, 2020
retest this please |
SparkQA
commented
Feb 10, 2020
Test build #118163 has finished for PR 27518 at commit
|
peter-toth
commented
Feb 11, 2020
Is the title correct? Isn't what you do here is constant propagation among constraints? BTW, I have a PR open to enhance |
wangyum
commented
Feb 12, 2020
@peter-toth I'd like to handle another case in this PR: spark.sql("CREATE TABLE `dw_user_state_history` (`id` DECIMAL(18,0), `change_time` date, `to_state` DECIMAL(9,0)) USING parquet")
spark.sql("CREATE TABLE `dw_user_cntry_hist` (`user_id` DECIMAL(18,0), `start_dt` DATE, `end_dt` DATE) USING parquet")
spark.sql(
""" |SELECT | count(*) |FROM | dw_user_state_history ush | INNER JOIN dw_user_cntry_hist uch ON (uch.user_id = ush.id AND CAST(ush.change_time AS date) >= uch.start_dt AND CAST(ush.change_time AS date) < uch.end_dt) |WHERE | change_time between '2019-07-01 00:00:00' AND '2019-07-02 00:00:00' |""".stripMargin).explain()The exepected physical plan: |
@wangyum, I'm a but confused now. As far as I see based on your changes, in this PR you substitute
But, I don't see how it will help with the example query here: #27518 (comment) |
wangyum
commented
Feb 12, 2020
@peter-toth999126c helps the example query. |
SparkQA
commented
Feb 12, 2020
Test build #118309 has finished for PR 27518 at commit
|
Thanks @wangyum. I see now that you basically reverted your first commit. |
wangyum
commented
Feb 13, 2020
@peter-toth Please go ahead. I reverted the first commit because this test will fail: test("Constraints shouldn't be inferred from cast equality constraint(filter lower data type)") {
valtestRelation1=LocalRelation('a.int)
valtestRelation2=LocalRelation('b.long)
valoriginalLeft= testRelation1.where('a===1).subquery('left)
valoriginalRight= testRelation2.subquery('right)
valleft= testRelation1.where(IsNotNull('a) &&'a===1).subquery('left)
valright= testRelation2.where(IsNotNull('b)).subquery('right)
Seq(Some("left.a".attr.cast(LongType) ==="right.b".attr),
Some("right.b".attr ==="left.a".attr.cast(LongType))).foreach { condition =>
testConstraintsAfterJoin(originalLeft, originalRight, left, right, Inner, condition)
}
} |
# Conflicts: # sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/optimizer/InferFiltersFromConstraintsSuite.scala
SparkQA
commented
Feb 14, 2020
Test build #118434 has finished for PR 27518 at commit
|
SparkQA
commented
Feb 16, 2020
Test build #118505 has finished for PR 27518 at commit
|
SparkQA
commented
Feb 17, 2020
Test build #118526 has finished for PR 27518 at commit
|
wangyum
commented
Feb 17, 2020
Metrics of Analyzer/Optimizer Rules for 02:32:26.475WARN org.apache.spark.sql.TPCDSQuerySuite:===Metrics of Analyzer/OptimizerRules===Total number of runs: 224786Totaltime: 45.803692546 seconds
RuleEffectiveTime/TotalTimeEffectiveRuns/TotalRuns org.apache.spark.sql.catalyst.optimizer.Optimizer$OptimizeSubqueries6898974687/884058764247/772 org.apache.spark.sql.catalyst.optimizer.ColumnPruning671684614/2740648387327/2364 org.apache.spark.sql.catalyst.analysis.Analyzer$ResolveSubquery1637851386/178094283451/2159 org.apache.spark.sql.catalyst.analysis.Analyzer$ResolveAggregateFunctions624455301/174445969649/2159 org.apache.spark.sql.catalyst.analysis.DecimalPrecision1136163267/1355664883361/2159 org.apache.spark.sql.catalyst.analysis.Analyzer$ResolveReferences958843179/1132184862813/2159 org.apache.spark.sql.catalyst.optimizer.PruneFilters21041535/8754806635/1978 org.apache.spark.sql.catalyst.optimizer.BooleanSimplification7949392/7579899834/1592 org.apache.spark.sql.catalyst.analysis.TypeCoercion$ImplicitTypeCasts301831588/65764540278/2159 org.apache.spark.sql.catalyst.optimizer.PushDownPredicates374351767/641828516758/1979 org.apache.spark.sql.catalyst.analysis.Analyzer$ResolveMissingReferences14541719/63253665410/2159 org.apache.spark.sql.catalyst.optimizer.NullPropagation39937489/58760352442/1592 org.apache.spark.sql.catalyst.optimizer.ReorderJoin244728546/572349474177/1592 org.apache.spark.sql.catalyst.optimizer.ConstantFolding158687531/564225840194/1592 org.apache.spark.sql.catalyst.optimizer.ReorderAssociativeOperator0/5471651210/1592 org.apache.spark.sql.catalyst.optimizer.SimplifyConditionals0/5329217450/1592 org.apache.spark.sql.catalyst.optimizer.SimplifyBinaryComparison0/5275306710/1592 org.apache.spark.sql.catalyst.optimizer.RemoveNoopOperators48706059/526075410116/2364 org.apache.spark.sql.catalyst.optimizer.SimplifyExtractValueOps0/5118074200/1592 org.apache.spark.sql.catalyst.optimizer.SimplifyCasts45722796/51012445583/1592 org.apache.spark.sql.catalyst.optimizer.SimplifyCaseConversionExpressions0/4932196690/1592 org.apache.spark.sql.catalyst.optimizer.OptimizeIn12300150/49144820427/1592 org.apache.spark.sql.catalyst.analysis.UpdateAttributeNullability20208595/48441347312/674 org.apache.spark.sql.catalyst.optimizer.RemoveDispensableExpressions0/4807559360/1592 org.apache.spark.sql.catalyst.optimizer.LikeSimplification799298/4636093791/1592 org.apache.spark.sql.catalyst.optimizer.CollapseProject92682031/457556748215/1978 org.apache.spark.sql.catalyst.optimizer.ReplaceNullWithFalseInPredicate0/4566588520/1592 org.apache.spark.sql.catalyst.analysis.TypeCoercion$FunctionArgumentConversion227217097/44223385456/2159 org.apache.spark.sql.catalyst.optimizer.InferFiltersFromConstraints406969136/435459925278/386After this PR: 02:28:49.937WARN org.apache.spark.sql.TPCDSQuerySuite:===Metrics of Analyzer/OptimizerRules===Total number of runs: 224786Totaltime: 47.011460872 seconds
RuleEffectiveTime/TotalTimeEffectiveRuns/TotalRuns org.apache.spark.sql.catalyst.optimizer.Optimizer$OptimizeSubqueries7073196527/895085492647/772 org.apache.spark.sql.catalyst.optimizer.ColumnPruning724531405/2931435267327/2364 org.apache.spark.sql.catalyst.analysis.Analyzer$ResolveSubquery1789988207/194219617951/2159 org.apache.spark.sql.catalyst.analysis.Analyzer$ResolveAggregateFunctions717122415/183821949949/2159 org.apache.spark.sql.catalyst.analysis.DecimalPrecision1276704718/1524939842361/2159 org.apache.spark.sql.catalyst.analysis.Analyzer$ResolveReferences1003010234/1192920997813/2159 org.apache.spark.sql.catalyst.optimizer.PruneFilters21471500/9526603735/1978 org.apache.spark.sql.catalyst.optimizer.BooleanSimplification9761473/7732054694/1592 org.apache.spark.sql.catalyst.analysis.TypeCoercion$ImplicitTypeCasts329430374/73406740578/2159 org.apache.spark.sql.catalyst.analysis.Analyzer$ResolveMissingReferences12112700/70971920310/2159 org.apache.spark.sql.catalyst.optimizer.PushDownPredicates399755753/663934266758/1979 org.apache.spark.sql.catalyst.optimizer.ReorderJoin266383835/593070778177/1592 org.apache.spark.sql.catalyst.optimizer.NullPropagation35781183/57841782342/1592 org.apache.spark.sql.catalyst.optimizer.RemoveNoopOperators48387593/540807035116/2364 org.apache.spark.sql.catalyst.optimizer.SimplifyBinaryComparison0/5357086340/1592 org.apache.spark.sql.catalyst.optimizer.ConstantFolding114115625/533552246194/1592 org.apache.spark.sql.catalyst.optimizer.OptimizeIn12632788/52948750727/1592 org.apache.spark.sql.catalyst.optimizer.SimplifyCaseConversionExpressions0/5137459560/1592 org.apache.spark.sql.catalyst.optimizer.SimplifyConditionals0/5085861600/1592 org.apache.spark.sql.catalyst.optimizer.InferFiltersFromConstraints459543325/490828096278/386 |
SparkQA
commented
Feb 17, 2020
Test build #118570 has finished for PR 27518 at commit
|
HyukjinKwon
commented
Feb 25, 2020
retest this please |
SparkQA
commented
Feb 25, 2020
Test build #118895 has finished for PR 27518 at commit
|
SparkQA
commented
Feb 26, 2020
Test build #118980 has finished for PR 27518 at commit
|
SparkQA
commented
Mar 7, 2020
Test build #119516 has finished for PR 27518 at commit
|
SparkQA
commented
Mar 20, 2020
Test build #120106 has finished for PR 27518 at commit
|
SparkQA
commented
Jun 8, 2020
Test build #123638 has finished for PR 27518 at commit
|
SparkQA
commented
Jun 12, 2020
Test build #123919 has finished for PR 27518 at commit
|
HyukjinKwon
commented
Jun 26, 2020
cc @maryannxue and @cloud-fan FYI |
| case _: GreaterThanOrEqual => true | ||
| case _: LessThan => true | ||
| case _: LessThanOrEqual => true | ||
| case _: EqualTo => true |
There was a problem hiding this comment.
EqualTo should not be needed here, as the inferEqualityConstraints should cover all cases including it.
There was a problem hiding this comment.
inferEqualityConstraints can not handle all cases, such as constraint with cast.
There was a problem hiding this comment.
For example: cast(a as double) > cast(b as double) and cast(b as double) = 1
| val lessThans = binaryComparisons.map { | ||
| case EqualTo(l, r) if l.foldable => EqualTo(r, l) | ||
| case GreaterThan(l, r) => LessThan(r, l) | ||
| case GreaterThanOrEqual(l, r) => LessThanOrEqual(r, l) | ||
| case other => other | ||
| } |
There was a problem hiding this comment.
Doesn't this duplicate the greaterThans block?
Here you have a < b < c and in the other block you have c > b > a
There was a problem hiding this comment.
No. for example:a > b and 5 > a. we can not infer anything. but we can infer that b < 5 after rewriting a > b and 5 > a as b < a and a < 5.
There was a problem hiding this comment.
Is it because of the foldable check? Without it, it should be inferable.
| var inferredConstraints = Set.empty[Expression] | ||
| greaterThans.foreach { | ||
| case op @ BinaryComparison(source: Attribute, destination: Expression) | ||
| if destination.foldable => |
There was a problem hiding this comment.
I think that the foldability is not needed here. The new constraints do not have to only involve constants, but also any attribute.
There was a problem hiding this comment.
To avoid generating too many constraints. For example: a > b > c > 1. The expected inferred constraints are: a > 1 and b > 1. a > c is useless.
There was a problem hiding this comment.
If a and c are in tihe same side of a join, then it can be pushed down.
There was a problem hiding this comment.
How to push down a > c if both a and c are not foldable?
There was a problem hiding this comment.
I'm sorry, I used a wrong word. I meant pushed through the join into one of the sides.
| do { | ||
| lastInequalityInferred = inferInequalityConstraints(constraints ++ inferred) | ||
| inferred ++= lastInequalityInferred | ||
| } while (lastInequalityInferred.nonEmpty) |
There was a problem hiding this comment.
Could you hit a infinite loop with non deterministic filters? As they are never semantically equal to any other expression (including themselves). I hit that problem in #29650, where I was also working on constraint inference , but from EqualNullSafe.
wangyum
commented
Dec 14, 2020
Thank you all. Merged it to our internal Spark version. |
What changes were proposed in this pull request?
In our production environment, there are many queries similar to this pattern:
In this case, we can infer more constraints to improve query performance. E.g.
spark_30768_2.start_dt <= '2020-02-07'inferred fromspark_30768_2.start_dt <= spark_30768_1.src_cre_dt <= '2020-02-07'andspark_30768_2.end_dt > '2020-02-01'inferred fromspark_30768_2.end_dt > spark_30768_1.src_cre_dt >= '2020-02-01'. This PR add support infer these constraints from inequality attributes.Why are the changes needed?
Improve query performance. Teradata support this optimization:
https://docs.teradata.com/reader/Ws7YT1jvRK2vEr1LpVURug/V~FCwD9BL7gY4ac3WwHInw?section=xcg1472241575102__application_of_transitive_closure_section
Does this PR introduce any user-facing change?
No.
How was this patch tested?
Unit test and benchmark test.
Benchmark code and benchmark result:
Before this PR:
After this PR:
Also test this feature in our production environment, it can significantly improve the query performance of at least 6 SQLs (a total of 200 SQLs):
For e.g. SQL 372. It prevents (18,413,424,580 - 162,205,133 = 18,251,219,447) rows from participating in shuffle:

Before this PR:
After this PR:
