The disclosed implementations relate generally to database systems, and more specifically to system features that improve query execution performance.
Data is commonly stored in a relational database and accessed by queries using a prescribed syntax (e.g., Structured Query Language (SQL)). SQL database query engines are normally designed to translate a database query into a relational algebra. In this relational algebra, joins are among the important relational operators and come in a number of variants, including inner joins; left, right, and full outer joins; semi joins; and anti-semi joins. These join variants are sufficient to express most database queries. However, subqueries can occur almost everywhere in a database query, and how a subquery is translated can have a significant impact on execution performance. A conventional approach is to model a database query containing a subquery as mutual recursion (e.g., expressions and queries can refer to and evaluate each other). This mutual recursion approach is simple but non-relational, making many important optimizations nearly impossible. In effect, the mutual recursion approach pre-determines the execution plan of common query patterns to nested-loop-style execution with O(n2) runtime.
Accordingly, some disclosed implementations are directed to translating, optimizing, and executing database queries using two additional join types (in addition to inner joins, outer joins, and semi joins). The two additional join types are “single joins” and a “mark joins.” In some implementations, the single joins and mark joins are introduced in an early stage of query translation and optimization in order to translate specific subquery constructs into an extended relational algebra. In some instances, a single join is added to the operator tree corresponding to the database query when a clause of the database query includes a scalar subquery. In some instances, a mark join operator is added to the operator tree when a clause of the database query includes one of a predetermined set of predicate subqueries. As a result of single joins and mark joins, mutual recursion of expressions and subqueries is broken up so that expressions do not refer to subqueries. Instead, subqueries are translated into re-orderable joins. This is an algebra-based and orthogonal approach that enables additional options for join reordering, supports an associated unnesting technique, and enables a join optimization pipeline in a hybrid OLTP/OLAP high performance database management system.
In accordance with some implementations, a method for retrieving data from a database is performed by a database engine. The database engine executes at a computer system (e.g., one or more computers or CPUs). The computer system has one or more processors and memory storing one or more programs configured for execution by the one or more processors. The database engine receives a database query from a client. The database query including a plurality of clauses. The database engine parses each of the clauses in the database query to form an operator tree including a plurality of join operators.
When a clause includes one of a predetermined set of predicate subqueries, the database engine adds to the operator tree a node that specifies a mark join operator between a respective left data set and a respective right data set. The mark join operator is configured to create a mark column in a respective intermediate result set for the respective node. The mark column specifies, for each tuple of the respective intermediate result set, whether or not the respective left data set has a join partner from the right data set. In some implementations, the predetermined set of predicate subqueries includes an EXISTS subquery, a NOT EXISTS subquery, a UNIQUE subquery, and a quantified comparison predicate. In some implementations, the mark column created for a mark join operator has a Boolean data type.
When a clause includes a scalar subquery, the database engine adds to the operator tree a node that specifies a single join operator between a respective left data set and a respective right data set. The single join operator is configured to raise an error when there is a row in the respective left data set with two or more join partners in the respective right data set. Otherwise, the single join operator is configured to operate as a left outer join.
When a clause does not include a scalar subquery, and does not include any of the predetermined set of predicate subqueries, the database engine adds an inner join operator, an outer join operator, or a semi join operator to the operator tree for each join condition in the clause.
The database engine performs one or more optimization passes on the operator tree to form an optimized execution plan, which includes unnesting one or more mark joins or single joins in the operator tree, and executes the optimized execution plan to retrieve a result set from the database.
In some implementations, each of the plurality of clauses has a clause type selected from the group consisting of: from clause, where clause, group by clause, having clause, select clause, and order by clause. In some implementations, a database query may have at most one clause of each clause type (at the outermost nesting level).
In some implementations, forming the operator tree includes translating the plurality of clauses in an order according to clause type, in the order: 1)from clause, 2) where clause, 3) group by clause, 4) having clause, 5) select clause, and 6) order by clause. In some implementations, forming the operator tree includes incrementally adding operators at a top node of an interim operator tree as the clauses are processed in order.
In some implementations, performing the one or more optimization passes includes identifying in the operator tree a first single join operator that joins a first column of a first left data set to a second column of a first right data set, and in accordance with a determination that the second column is a primary key for the first right data set, replacing the first single join operator with a corresponding left outer join operator between the first left data set and the second left data set.
In some instances, performing one or more optimization passes on the operator tree includes changing the order of the plurality of join operators in the operator tree. In some instances, the plurality of join operators includes a first mark join operator and a first inner join operator, and changing the order of the plurality of join operators in the operator tree includes performing the first mark join operator prior to the first inner join operator. In some implementations, the order of the plurality of join operators is determined according to a cost-based join enumeration method.
In some implementations, performing the one or more optimization passes on the operator tree includes, for each of the plurality of join operators, selecting a left variant or a right variant to implement the respective join operator according to respective sizes of the left and right data sets for the respective join operator (e.g., building a join hash table for the smaller data set).
In some implementations, performing the one or more optimization passes on the operator tree includes translating one or more mark join operators into one or more semi join operators.
In some implementations, performing the one or more optimization passes on the operator tree includes translating one or more outer join operators to one or more inner join operators.
In some implementations, a computer system includes one or more processors, and memory storing one or more programs that are configured for execution by the one or more processors. The one or more programs include instructions for performing any of the methods described herein.
In some implementations, a non-transitory computer readable storage medium stores one or more programs configured for execution by a computer system having one or more processors, and memory. The one or more programs include instructions for performing any of the methods described herein.
Thus, methods and systems are disclosed for translating, optimizing, and executing database queries using single joins and/or mark joins in addition to existing and commonly-known joins (e.g., inner joins, outer joins, and semi joins). For example, when a clause of a database query includes one of a predetermined set of predicate subqueries (e.g., EXISTS, NOT EXISTS, or UNIQUE), a node that specifies a mark join operator is added to the operator tree between a respective left data set and a respective right data set to create a mark column in an intermediate result set. The mark column specifies, for each tuple of the respective intermediate result set, whether or not the respective left data set has a join partner from the right data set. When a clause of a database query includes a scalar subquery, a node that specifies a single join is added to the operator tree operator between the respective left data set and the respective right data set. The single join operator is configured to raise an error when there is a row in the respective left data set with two or more join partners in the respective right data set. The single joins and mark joins are constructs in the extended relational algebra, which enable additional join-reordering options, support associated unnesting techniques, and facilitate the join optimization pipeline.
It is to be understood that both the foregoing general description and the following detailed description are exemplary and explanatory and are intended to provide further explanation of the invention as claimed.
For a better understanding of the aforementioned systems and methods that provide efficient database query processing, reference should be made to the Description of Implementations below, in conjunction with the following drawings in which like reference numerals refer to corresponding parts throughout the figures.
a=bS) in accordance with some implementations.
Reference will now be made to implementations, examples of which are illustrated in the accompanying drawings. In the following description, numerous specific details are set forth in order to provide a thorough understanding of the present invention. However, it will be apparent to one of ordinary skill in the art that the present invention may be practiced without requiring these specific details.
In some implementations, the personal device 102 connects to one or more external database servers 106 and/or a data visualization server 104. Each of the one or more database servers 106 includes a database engine 120 configured to access one or more databases 122 that are stored at the respective database server 106. In some implementations, the data visualization server 104 includes a database engine 120 and one or more databases 122, and the database engine 120 is configured to access one or more databases 122 of the data visualization server 104.
In some implementations, data visualization functionality is implemented only by the data visualization application 222 of the personal device 102 locally. In some implementations, the data visualization functionality is provided collectively by the local data visualization application 222 and the remote data visualization server 104, particularly when corresponding data retrieval or visualization involves resource intensive operations that are preferably implemented at a specialized server. In this situation, the user 100 may build a data visualization using the local application 222 of the personal device 102. In some implementations, one or more requests are sent to the data visualization server 104 to generate data visualizations.
In some implementations, the personal device 102 connects to the external database servers 106 and/or the data visualization server 104 over one or more communications networks 108. The communication networks 108 can include one or more network topologies, including the Internet, intranets, local area networks (LANs), cellular networks, Ethernet, Storage Area Networks (SANs), telephone networks, Bluetooth personal area networks (PAN), and the like. In some implementations, the data visualization server 104 provides a data visualization web application that runs within a web browser 220 on the personal device 102.
In accordance with some implementations, upon receiving a database query that includes a plurality of clauses, the database engine 120 parses the database query to form an operator tree including a plurality of join operators, performs one or more optimization passes on the operator tree to form an optimized execution plan, and executes the optimized execution plan to retrieve a result set from the database. In some implementations, the plurality of join operators includes one or more of: inner joins, outer joins, mark joins, and single joins. In some implementations, the plurality of join operators also includes one or more of: semi joins, anti-semi joins, and dependent join variants of inner joins, semi joins, anti-semi joins, left outer joins, and full outer joins.
During the course of parsing the database query to form the operator tree, the database engine 120 determines, for each of the plurality of clauses, whether the respective clause includes a scalar subquery and/or whether the respective clause includes any of a predetermined set of predicate subqueries, including EXISTS subqueries, NOT EXISTS subqueries, UNIQUE subqueries, and quantified comparison predicates. When the respective clause includes one of the predetermined set of predicate subqueries, the operator tree incorporates a respective node that specifies a mark join operator between a respective left data set and a respective right data set. In some implementations, the database engine 120 determines, for each of the plurality of clauses, whether the respective clause includes a scalar subquery. When the respective clause includes a scalar subquery, the operator tree incorporates a respective node that specifies a single join operator between a respective left data set and a respective right data set. When the respective clause does not include a scalar subquery and does not include any of the predetermined set of predicate subqueries, the operator tree incorporates a standard join operator (e.g., an inner join or an outer join) to the operator tree for each join condition in the respective clause.
An inner join is a cross product of a left data set T1 and a right data set T2 followed by a selection:
T
1
T
2:=σ(T1×T2) (1)
The selection σ identifies the tuples in the cross product that satisfy the predicate p. The inner join computes a combination of matching entries from the left data set T1 and the right data set T2 according to the predicate p (e.g., comparing equality of a field in T1 with a field in T2).
In some instances, a database query includes a correlated subquery, and the subquery (as written) is evaluated for every tuple of a corresponding outer query. Some implementations translate a correlated subquery into a dependent join, which is characterized as:
T
1
T
2
:={t
1∘t2|t1∈T1∧t2∈T2(t1)∧p(t1∘t2)}. (2)
Where p(t1∘t2) determines whether the combination satisfies the predicate. The right-hand side is evaluated for every tuple of the left-hand side. In some instances, the attributes produced by an expression T are denoted by A(T), and free variables occurring in an expression T are denoted by F(T). In some implementations, to evaluate a dependent join, F(T2)⊆A(T1) must hold (i.e., the attributes required by T2 must be produced by T1). In some implementations, a dependent join is associated with one or more transformation rules. In some implementations, a dependent join and the corresponding transformation rules are applied while unnesting, thereby optimizing the database query.
Semi joins, anti-semi joins, left outer joins, and the full outer join are represented by the following formulas:
T2 := {t1|t1 ∈ T1 ∧ ∃t2 ∈ T2 : p(t1 ○ t2)}
T2 := {t1|t1 ∈ T1∧
t2 ∈ T2 : p(t1 ○ t2)}
T2 := (T1
T2) ∪ {t1 ○α∈
T2)}
T2 := (T1
T2) ∪ {t2 ○α∈A(T
T1)}
Each of the above join types has a corresponding dependent join variant that is analogous to the dependent join illustrated above in equations (1) and (2) for an inner join. In addition to the join operators, the group by operator is represented as follows:
ΓA;a:f(e):={x∘(a:f(y))|x∈ΠA(e)∧y={z|z∈e∧∀a∈A:x·a=z·a}} (7)
which groups an input e (e.g., a base relation or a relation computed from another algebra expression) by A, and evaluates one (or more comma separated) aggregation function(s) to compute aggregated attributes. In some situations, A is empty, so a single aggregation tuple is produced as a result of the group by operator.
A single join operator behaves essentially like an outer join operator, except that a single join operator will raise an error when there is a row in the left data set with two or more join partners in the right data set. A single join operator is particularly useful for translating a subquery that is intended to compute a scalar value (i.e., the subquery is expected to produce exactly one column and at most one row.) Specifically, when a subquery produces a single result, the single result is used as a scalar value, and when the subquery produces no result, the scalar value is set as NULL. However, when the subquery produces more than one result, the database engine 120 reports a runtime error. A single join operator is used to express this in the extended relational algebra. The formula for a single join operator can be expressed as:
Using single join operators improves both performance of the database engine 120 and accuracy of the result set returned in response to a database query. On the performance side, a hash-based process for implementing the single join has a runtime of O(n), which is much better than the O(n2) runtime associated with a conventional mutual recursion approach. More importantly, unlike other join implementations, the single join operator reports an error if more than one join partner is found.
A mark join operator creates a mark column in the intermediate result set created for the join. The mark column specifies, for each tuple of the respective intermediate result set, whether or not the respective left data set has a join partner from the right data set. The mark join creates a new attribute column (e.g., a Boolean data field) to specify whether or not each tuple has at least one join partner:
T
1
p
M;m
T
2
:={t
1∘(m:(t2∈T2:p(t1∘t2)))|t1∈T1} (9)
In some implementations, a mark join is evaluated in O(n) time when using hashing. Importantly, the query optimizer can optimize expressions in the relational algebra that include mark joins. In some implementations, the mark join operator is used to translate predicate subqueries that arise from exists, not exists, unique, and quantified comparisons, and may be further optimized to offer an efficient evaluation strategy.
In some implementations, the memory 214, or the computer readable storage medium of the memory 214, stores the following programs, modules, and data structures, or a subset thereof:
The database engine 120 further includes a plurality of executable modules. In some implementations, the memory 214, or the computer readable storage medium of the memory 214, stores the following executable modules of the database engine 120, or a subset thereof:
Each of the above identified executable modules, applications, or sets of procedures may be stored in one or more of the previously mentioned memory devices, and corresponds to a set of instructions for performing a function described above. The above identified modules or programs (i.e., sets of instructions) need not be implemented as separate software programs, procedures, or modules, and thus various subsets of these modules may be combined or otherwise rearranged in various implementations. In some implementations, the memory 214 stores a subset of the modules and data structures identified above. Furthermore, in some implementations, the memory 214 stores additional modules or data structures not described above.
Although
In some implementations, a canonical translation method is applied to map a database query to the extended relational algebra. Consider the query:
In some instances, a database query includes two or more correlated subqueries. For example, consider:
Professors(Γ∅,total:sum(ECTS)σPersId=LecturerCourses).
In some implementations, a query optimization module 230 will replace the dependent join with an inner join, a semi join, an anti-semi join, a left outer join, a full outer join, a single join, or a mark join as a result of unnesting.
In some instances, a database query includes a subquery that is expected to return exactly one column value from one row (i.e., a scalar subquery). Such a query is translated into a single join operator. The value of the scalar subquery expression is a value of a select list item of the subquery. If the subquery does not return any row, then the value of the scalar subquery expression is NULL. Consider the following example query:
Professorstrue1σPersId=Boss∨JobTitle=‘personal assistant’(Assistants)
In some implementations, the query optimization module 230 of the database engine 120 further moves the correlated predicate into the join operator during subsequent query optimization, resulting in:
ProfessorsPersId=Boss1σJobTitle=‘personal assistant’(Assistants).
In some instances, a database query includes a predicate subquery that arises from EXISTS, NOT EXISTS, UNIQUE, or a quantified comparison. For example, consider the following query:
σ(m∨Sabbatical)(ProfessorsPersId=LecturerM:mCourses)
Another example of creating a mark join arises from the following database query:
Courses c1c
The mark join has a result column someEqual, which is either TRUE, FALSE, or NULL (i.e., unknown).
The where clause 410 is processed next. The database engine 120 uses (412) a mark join operator to represent an exists subquery, a not exists subquery, a unique subquery, or a quantified subquery. The database engine 120 adds the mark join operator to the top of the operator tree. For each scalar subquery identified in the where clause, the database engine 120 introduces (414) a single join operator to represent the scalar subquery and adds the single join operator to the top of the operator tree. For each scalar expression (not a subquery) in the where clause, the expression is translated (416) directly. Each mark join operator, single join operator, or directly translated expression is added (418) to the top of the operator tree using a selection operator σ.
The group by clause 420 (if it exists) is processed next. The database engine 120 uses (422) a mark join operator, a single join operator, or direct translation to represent the group by expressions in the same way that the database engine 120 processes a where clause in operations 412-416. Each mark join operator, single join operator, or directly translated expression is added (424) to the top of the operator tree using a group by operator Γ.
The database engine 120 next processes the having clause 426 (if it exists). The logic for the having clause is (428) the same as the logic for the where clause in operations 412-416. Like a where clause, each mark join operator, single join operator, or directly translated expression is added to the top of the operator tree using a selection operator σ.
The database engine 120 processes the select clause 430 next. The select clause is translated (432) in the same way the database engine 120 translates a where clause in operations 412-416. Each mark join operator, single join operator, or directly translated expression is added (434) to the top of the operator tree using a projection operator Π.
Finally, the database engine 120 processes the order by clause 436, if it exists. The database engine 120 translates (438) the order by clause in the same way it translates a where clause in operations 412-416. Each the mark join operator, single join operator, or the directly translated expression is added (438) to the top of the operator tree using a sort operator.
The process 400 translates a database query into the extended relational algebra without having to fall back to mutual recursion between operators and expressions. In particular, the translation process 400 can be optimized by a query optimization module 230, leveraging efficient join implementations. More details on query optimization are discussed below with reference to
The query optimization module 230 performs one or more optimization passes on the canonical plan 504 to form an optimized execution plan 506. In some implementations, the query optimization module 230 includes an unnesting module 232, a predicate pushdown module 234, a join ordering module 236, and a join algorithm selector 238. In some implementations, the unnesting module 232 is configured to unnest any query or subquery independently of whether the query or subquery is associated with a correlated subquery. Mark joins and single joins are building blocks used by the unnesting module 232 to express the corresponding query or subquery in the extended relational algebra. More details on the unnesting process are provided by Thomas Neumann and Alfons Kemper in “Unnesting Arbitrary Queries,” Datenbanksysteme fur Business, Technologie und Web (BTW), 16. Fachtagung des GI-Fachbereichs “Datenbanken und Informationssysteme” (DBIS), 4.-6.3.2015 in Hamburg, Germany, Proceedings, pages 383-402, 2015, which is hereby incorporated by reference in its entirety.
Some implementations include a predicate pushdown module 234, which pushes down a subset of predicate subqueries of a database query to where the data is stored. By filtering the data at the source, network traffic is reduced.
The join ordering module 236 reorders the execution of join operators in an optimal order. In some implementations, the order of the plurality of join operators is determined according to a cost-based join enumeration method. In some instances, the plurality of join operators includes a first mark join operator and a first inner join operator, and changing the order of the plurality of join operators in the operator tree includes performing the first mark join operator prior to the first inner join operator. For example, consider the following database query using the tables 300, 330, and 360 in
(σ(m∨Sabbatical)(ProfessorsPersId=LecturerM:mCourses))
PersId=BossAssistants
If there are more assistants than professors, performing the mark join first is faster than starting with the inner join. In some implementations, due to transitivity of the join predicates (PersId=Boss and Lecturer=PersId), a mark join between Course and Assistants is implemented first. The decision between the three join orders is done by a cost-based join enumeration method. In some implementations, the database engine 120 uses a graph-based dynamic programming method (also called DPhyp) for enumerating connected components without cross products. In some implementations, both the cost-based join enumeration method and the graph-based dynamic programming method determine the order of joins according to ordering constraints of non-inner joins.
In some implementations, the database engine 120 has a left variant (e.g., left mark join) and a right variant (e.g., right mark join) for a join operator. Both variants of the join operator semantically produce the same result (except with left and right inputs swapped). However, in some situations, different join variants can lead to different query execution performance. For example, in hash-based execution, a hash table is built from a left input (the build input). A tuple from a right input (the probe input) results in a hash table lookup in the hash table. Because hash table insertion is usually slower than lookup, the query optimization module 230 typically swaps the argument order of joins so that the table with fewer rows is on the left (e.g., based on cardinality estimates).
In some instances, the query optimization module 230 translates a mark join into a semi join or anti-semi join, thereby avoiding maintaining a marker and achieving a faster processing rate. For example, consider the following database query based on the tables 300 and 330 in
In some implementations, the query optimization module 230 translates outer joins into inner joins when there is a null-rejecting predicate. For example, consider the following example where converting to an inner join is possible:
In some implementations, the query optimization module 230 replaces a left single join with a left outer join when a subquery is known to compute at most one row. For example, consider the following database query:
A join algorithm selector 238 selects specific join operator implementations. a=bS) according to the pseudocode 600. Here, a hash table holds indexed hash values for the tuples from one side (the hash of the attribute a), and the other side probes the hash table to find join partners (using a hash of the attribute b).
a=bS, when a has the data type numeric (6,3) and b has the data type integer. The internal representation of numbers is different. The integer value 3 should join to the numeric value 3.000, but not to the numeric value 3.001. Typically, using native hash functions for different data types does not yield the correct results. In some implementations, a join is performed on the most restrictive data type. In this example, the integer data type is more restrictive than the numeric (6,3) data type. Any value that cannot be represented exactly as an integer does not have a join partner, and thus can be omitted from the hash table. The pseudocode 610 in
a=bS) using a main-memory algorithm in accordance with some implementations, and
In accordance with the pseudocode 630, each of the tuples in R is processed and stored (622) in the hash table, and initially set as unmarked. When the join fields have different data types, the hash table rows are different depending on whether the field value has an exact cast to the more restrictive data type. Here, the data values 1.0, 3.0, and 5.0 have exact casts to an integer data type, so their index keys 623 are saved in the hash table. On the other hand, the data values 3.1 and 7.1 cannot be exactly cast as integers, so their key values are not included in the index. Instead, corresponding extra rows 624 are created. If this were an inner join or a right outer join, the extra rows for R would not be created. For left and full outer joins, the extra rows are stored in an extra hash table bucket.
Next, the tuples from S are processed. The local markers for the S tuples are initially set to zero, and the process probes (626) the hash table to identify potential join candidates. When a join candidate is found in the hash table, both the corresponding S and R tuples are marked as having join partners before emitting the joined tuple. After probing the hash table, it is therefore determined whether each S tuple has a join partner. If an S tuple does not have a join partner, the process emits the tuple after padding it with NULL values for the fields from R. After processing the S tuples, the process does a final sweep over the hash table and emits all tuples that do not have join partners. In some instances, this includes a subset of tuples that have data type mismatches. The final result 628 of the join includes rows arising for four different reasons: (1) s1 and s4 from S have no matching partner from R; (2) r3 and r2 from R match to s2 and s3 from S; (3) r5 from R has no matching entry from S; and (4) r1 and r4 from R could not possibly have matching partners from S because it would be impossible for an integer valued field to match the value 3.1 or 7.1. In some circumstances, these last two reasons are considered together because they have the same net result (no partner in S).
While implementing the mark join, the database engine 120 checks for NULL values in the join attribute of S. If it encounters a NULL value, the database engine 120 determines that each output tuple has the marker that has a value of TRUE or NULL, but not a value of FALSE (because the NULL value would “join” with all of them). If NULL is not encountered, the database engine 120 does a hash table lookup and marks all matching tuples with TRUE. Afterwards, the database engine 120 scans the hash table and outputs all tuples with their respective markers. Under some circumstances, if the database engine 120 does not find any tuples in S at all, an initial NULL marker from a “null” list is converted into FALSE. If a tuple is marked as FALSE and the database engine 120 identifies a NULL value in S, the whole tuple is now marked as NULL, and the NULL value implicitly joins with it. It is noted that a right mark join is analogous to that of the left mark join shown in
a=b∧c>dS). The predicate of the quasi equality join has an equality component that can be evaluated using a hash join. The predicate of the quasi equality join also has a non-equality component. For an inner join, the corresponding predicate is split (e.g.,
a=b∧c>d≡σc>d(
a=bS). For an outer join, a corresponding predicate cannot be split, and additional restrictions are evaluated directly while the join is implemented in order to avoid incorrect results. Thus, in some implementations, a combined evaluation method using the hash join and additional restrictions is applied. In some implementations, extra logic is applied to evaluate an expression during the hash join. It is noted that in some implementations, a non-equality part of a join condition (e.g., the non-equality component of the predicate of the quasi equality join) returns NULL as a result. When the join condition includes a mark join, the NULL result returned by the non-equality part results in a NULL marker if a current marker is FALSE for the mark join.
For some complex predicates, hash joins are not possible. The database engine 120 needs a corresponding main-memory algorithm to process the predicate. For example, the database engine 120 processes complex predicates using a blockwise nested loop join, where chunks of R are loaded into memory and joined with tuples from S. This main memory algorithm has an asymptotic cost that is substantially equal to that of a naive nested loop join. However, in practice, a blockwise nested loop join is much faster (e.g., by orders of magnitude).
In some implementations, a join initializes an empty buffer, and then loads as many tuples from R into the buffer as possible. When the buffer is full, joinBuffer is called to join all tuples from S with current buffer content in the buffer, mark the join partners, and emit results. After reading S, the database engine 120 emits all unmarked tuples from the buffer after padding the unmarked tuples with NULL, and clears the buffer. This process continues until R has been processed completely. Finally, all unmarked tuples from S are emitted while being padded with NULL.
In some implementations, the marking uses the same logic as in the equality case. For example, to mark a left-hand side, the database engine 120 uses one byte per tuple in the buffer for marking. To mark a right-hand side that is accessed multiple times without being materialized in memory, the database engine 120 maintains an additional vector and spools it to disk. Some implementations use an associated data structure that assigns a bit value to each tuple and uses interval compression. In many cases the number of tuples that qualify (or conversely, fail to qualify) is small, so the associated data structure stores only a small amount of data.
In the TPC queries, all join types occur, either before or after optimization. There are fewer TPC-H queries than TPC-DS queries, and TPC-H queries are less complex. In TPC-H, single joins do not occur and all of the left mark joins can be translated to four left or right semi (anti) join variants. In TPC-DS, both single and mark joins are needed even after optimization. The table 700 also shows that both the right and left variants are chosen by the query optimization module 230. Thus, the table 700 indicates that the “zoo” of join variants is needed, and a query optimization module 230 benefits from having all these variants.
Consider the following database query on the TPC-H data set (scale factor 1):
In accordance with some implementations, the database engine 120 includes a query translation and optimization pipeline. As SQL has evolved to allow nested subqueries in different parts of a database query, there is a practical need for advanced join operators (e.g., single joins and mark joins) to avoid recursive evaluation with an unbearable O(n2) runtime. The TPC-H and TPC-DS benchmarks reveal that all join variants discussed herein are actually used in these query sets. The new join operators are involved in logical query translation and optimization, and impact the physical algorithmic implementation of these join operators.
In accordance with the method 800 for retrieving data from the database, a database engine 120 of the computer system 200 receives (802) a database query from a client. Each of the plurality of clauses has (804) a clause type that is one of: from clause, where clause, group by clause, having clause, select clause, and order by clause. In some implementations, the database query has (806) at most one clause of each clause type (not including the clauses within subqueries).
After receiving the database query, the database engine 120 parses (808) the database query to form an operator tree including a plurality of join operators. In some implementations, forming the operator tree includes (810) translating the plurality of clauses in an order according to clause type, in the order: 1)from clause, 2) where clause, 3) group by clause, 4) having clause, 5) select clause, and 6) order by clause. In some implementations, the join operators are incrementally added (812) at the top node of an interim operator tree as the clauses are processed in order. More details for translating each of the above clause types are discussed above with reference to
For each of the plurality of clauses, when the respective clause includes one of a predetermined set of predicate subqueries, the database engine 120 adds (814) to the operator tree a respective node that specifies a mark join operator between a respective left data set and a respective right data set. The mark join operator is configured to create (816) a mark column in a respective intermediate result set for the respective node. In some implementations, the mark column created for a mark join operator has (818) a Boolean data type. The mark column specifies (820), for each tuple of the respective intermediate result set, whether or not the respective left data set has a join partner from the right data set. The predetermined set of predicate subqueries includes (822) EXISTS subqueries, NOT EXISTS subqueries, UNIQUE subqueries, and quantified comparison predicates.
For each of the plurality of clauses, when the respective clause includes a scalar subquery, the database engine 120 adds (824) to the operator tree a respective node that specifies a single join operator between a respective left data set and a respective right data set. The single join operator is configured to raise (826) an error when there is a row in the respective left data set with two or more join partners in the respective right data set, and the single join operator is configured (828) to operate as a left outer join otherwise. In addition, the database engine 120 adds (830) a respective inner join operator or a respective outer join operator to the operator tree for each join condition in the respective clause that is not part of a scalar subquery and not one of the predetermined set of predicate subqueries.
After forming the operator tree, the database engine 120 performs (832) one or more optimization passes on the operator tree (and/or the corresponding execution plan) to form an optimized execution plan. The one or more optimization passes includes (834) unnesting operators using one or more mark joins or single joins. In some implementations, performing the one or more optimization passes further includes (836), for each of the plurality of join operators, selecting a left variant or a right variant to implement the respective join operator according to respective sizes of left and right data sets for the respective join operator. In some implementations, a first single join operator is identified (838) in the operator tree, and the first single join operator joins a first column of a first left data set to a second column of a first right data set. When the second column is a primary key for the first right data set, the database engine then replaces (840) the first single join operator with a corresponding left outer join operator between the first left data set and the second left data set. In some implementations, performing one or more optimization passes includes (842) changing the order of the plurality of operators. In some implementations, the plurality of operators includes (844) a first mark join operator and a first inner join operator, and changing the order of the plurality of join operators includes performing the first mark join operator prior to the first inner join operator. In some implementations, the order of the plurality of join operators is determined (846) according to a cost-based join enumeration method.
In some implementations, performing the one or more optimization passes includes (848) translating one or more mark join operators to one or more semi join operators. In some implementations, performing the one or more optimization passes includes (850) translating one or more outer join operators to one or more inner join operators. More details on the one or more optimization passes are provided above with reference to
The database engine 120 executes (852) the optimized execution plan to retrieve a result set from the database.
It should be understood that the particular order in which the operations in
The terminology used in the description of the invention herein is for the purpose of describing particular implementations only and is not intended to be limiting of the invention. As used in the description of the invention and the appended claims, the singular forms “a,” “an,” and “the” are intended to include the plural forms as well, unless the context clearly indicates otherwise. It will also be understood that the term “and/or” as used herein refers to and encompasses any and all possible combinations of one or more of the associated listed items. It will be further understood that the terms “comprises” and/or “comprising,” when used in this specification, specify the presence of stated features, steps, operations, elements, and/or components, but do not preclude the presence or addition of one or more other features, steps, operations, elements, components, and/or groups thereof.
The foregoing description, for purpose of explanation, has been described with reference to specific implementations. However, the illustrative discussions above are not intended to be exhaustive or to limit the invention to the precise forms disclosed. Many modifications and variations are possible in view of the above teachings. The implementations were chosen and described in order to best explain the principles of the invention and its practical applications, to thereby enable others skilled in the art to best utilize the invention and various implementations with various modifications as are suited to the particular use contemplated.
This application is a continuation of U.S. patent application Ser. No. 17/371,023, filed Jul. 8, 2021, titled “Optimizing Database Query Execution by Extending the Relational Algebra to Include Non-Standard Join Operators,” which is a continuation of U.S. patent application Ser. No. 15/890,277, filed on Feb. 6, 2018, titled “Optimizing Database Query Execution by Extending the Relational Algebra to Include Non-Standard Join Operators,” now U.S. Pat. No. 11,068,520, which is a continuation-in-part of U.S. patent application Ser. No. 15/700,023, filed on Sep. 8, 2017, titled “Adaptive Interpretation and Compilation of Database Queries,” now U.S. Pat. No. 11,055,331, which claims priority to U.S. Provisional Application Ser. No. 62/532,959, filed on Jul. 14, 2017, titled “Generating Adaptively Optimized Code for Database Queries,” and also claims priority to U.S. Provisional Application Ser. No. 62/418,246, filed on Nov. 6, 2016, titled “High Performance Relational Database System,” each of which is incorporated by reference herein in its entirety.
Number | Date | Country | |
---|---|---|---|
62532959 | Jul 2017 | US | |
62418246 | Nov 2016 | US |
Number | Date | Country | |
---|---|---|---|
Parent | 17371023 | Jul 2021 | US |
Child | 18381165 | US | |
Parent | 15890277 | Feb 2018 | US |
Child | 17371023 | US | |
Parent | 15700023 | Sep 2017 | US |
Child | 15890277 | US |