Query
Query Execution
The cost of query execution is dominated by the cost to do Disk I/Os. Therefore, when calculating the cost of executing a physical query plan, we usually count the size of blocks being written to or read from the disk. There are three main types of algorithms:
- Sorting based
- Hash based
- Index based
Nested Loop Join and Fundamental Operations
This is the most native and intuitive way to join. To join R and S, we simply iterate through tuples inside each relation. (i.e. For each tuple in S, iterate tuples in R). This result of this is \(O(n^{2})\). There are however, some optimizations which can make it more efficient either by repeating less in each iteration or leverage Disk I/O's bulk I/O chracteristics.
1 2 3 4 5 6 7 8 9 10 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | |
| Operator | Memory Requirement | Disk I/O Cost |
|---|---|---|
| \(\sigma, \Pi\) | \(1\) | \(B\) |
| \(\gamma, \delta\) | \(B\) | \(B\) |
| \(\cup, \cap, -, \times, \Join\) | $min(B(R), B(S)) | B(R) + B(S)$ |
| \(\Join\) | \(For any M \le 2\) | \(\frac{B(R)B(S)}{M}\) |
Sorting Algorithms
One Pass
This is pretty trivial, if your memory can fit all tuples of both Relations, you can simply use any sorting alrogithm we've learned in the past.
Two Pass
| Algorithm | Memory Requirement | Disk I/O Cost |
|---|---|---|
| Two phase multiway merge sort | \(\frac{B}{M} \le M - 1\) | \(3B(R)\) |
|
| Operator | Memory Requirement | Disk I/O |
|---|---|---|
| \(\gamma, \delta\) | \(\sqrt{B(R)}\) | \(3B(R)\) |
| \(\cup, \cap, -\) | \(\sqrt{B(R) + B(S)}\) | \(3(B(R) + B(S))\) |
| \(\Join\) | \(\sqrt{max(B(R),B(S))}\) | \(5(B(R) + B(S))\) |
| Optimized \(\Join\) | \(\sqrt{B(R) + B(S)}\) | \(3(B(R) + B(S))\) |
Hashing Algorithms
Two Pass
| Operator | Memory Requirement | Desk I/O Cost |
|---|---|---|
| \(\gamma, \delta\) | \(\sqrt{B(R)}\) | \(3B(R)\) |
| \(\cup, \cap, -\) | \(\sqrt{B(S)}\) | \(3(B(R) + B(S))\) |
| \(\Join\) | \(\sqrt{B(S)}\) | \(3(B(R) + B(S))\) |
| \(Optimized \Join\) | \(\sqrt{B(S)}\) |
One Pass Algorithms
Unary Tuple at a Time Operation
- Operation(s): \(\sigma\) and \(\pi\)
- Spatial Cost: 1
- Disk I/O Cost: \(B(R)\)
Unary Full Relation Operation
- Operation(s): \(\delta\) and \(\gamma\)
- Sptial Cost: M - 1
- Sptial Requirement: \(B(\delta(R)) \le M\)
- Disk I/O Cost: \(B(R)\)
Unary Full Relation Operation Spatial Cost
For Unary Full Relation Operation, the spatial cost is M - 1 instead of V(A, R) because we do not know the value of V(A, R) in advanced. Therefore, the physical operator would reserve M - 1 regardless of the value of V(A, R).
Binary Full Relation Operation
- Operation(s): \(\cup, \cap, -, \times, \Join\)
- Spatial Cost:
Relational Algebra
| Operator | Syntax | Description |
|---|---|---|
| \(\Pi\) | ||
| \(\delta\) | ||
| \(\gamma\) |
Fundamentals
Bags vs Sets
Database systems use Bags instead of Sets. Rules can potentially be different for Bags and Sets. Therefore, we must becareful
Commutativity and Associativity
| Commutativity | Associativity |
|---|---|
| \(R \times S = S \times R\) | \((R \times S) \times T = R \times (S \times T)\) |
| \(R \Join S = S \Join R\) | \((R \Join S) \Join T = R \Join (S \Join T)\) |
| \(R \cup S = S \cup R\) | \((R \cup S) \cup T = R \cup (S \cup T)\) |
| \(R \cap S = S \cap R\) | \((R \cap S) \cap T = R \cap (S \cap T)\) |
Selection Pushdown
Selection can significantly reduce the size of necessary Disk I/O, and hence improves the performance of a database query. Pushing selection down the tree means that we can reduce the number of tuples in our relation before executing other operators. Therefore, it is almost always right to push selection (\(\sigma\)) down the tree as far as possible. There are, however, some scenarios where first bringing the selection up the tree, then pushing it down the tree can improve query performance.
Textbook Example 16.8
For example, we are joining two relations that share the same attribute (i.e. StarsIn and Movies). Lets say we want to apply selection condition on movie where year = 2022. We can first pull this selection up:
then push it down the both arguments of the Join operator.
This has the potential to significantly reduce the disk I/O cost on scanning R.
| Law | Intuition | |
|---|---|---|
| \(\sigma_{C_{1} \wedge C_{2}}(R) = \sigma_{C_{1}}(\sigma_{C_{2}}(R)) = \sigma_{C_{2}}(\sigma_{C_{1}}(R))\) | Different conditions applied to selecting the same relation can be done in any order. For example, selecting tuples whose id column in divisible by 2 and 3 can be seprate into first divisible by 2, then divisible by 3 or vise versa. | |
| \(\sigma_{C}(R \cup S) = \sigma_{C}(R) \cup \sigma_{C}(S)\) | When pushing down a selection operator on an union operator, it must be pushed down to both arguments. This is pretty straightforward. Since \(\cup\) essentially concatenate tuples from R with tuples from S, if we wish to apply this condition before the join operation, then we need to apply this condition to both R and S | |
| \(\sigma_{C}(R - S) = \sigma_{C}(R) - \sigma_{C}(S) = \sigma_{C}(R) - S\) | The intuition for this one is slightly trickier. In my opinion, condition C can be pushed down to both side or just R because if certain tuples are selected based on condition C, then removing it from R is sufficient to remove it from \(R - S\). Another way to think about this is that \(difference\) only takeaway whatever S has that also appears in R. If a condition removes certain tuples from R, then we don't need S to remove them anyways. If a condition keeps certain tuples in R, then S would have taken it out anyways. | |
Projection Pushdown
As described in the textbook, projection does not reduce the number of tuples, but rather the length of tuples. In fact, extended project actually increase the length of a tuple.
Duplicate Elimination Pushdown
- \(\delta(R \times S) = \delta(R) \times \delta(S)\)
- \(\delta(R \Join S) = \delta(R) \Join \delta(S)\)
- \(\delta(R \Join_{C} S) = \delta(R) \Join_{C} \delta(S)\)
- \(\delta(\sigma_{C}(R)) = \sigma_{C}(\delta(R))\)
- 🇧\(\delta(R \cap S) = \delta(R) \cap S = R \cap \delta(S) = \delta(R) \cap \delta(S)\)
Join → Cartesian Product
Note
Essentially, \(\Join\) is a Cartetisian Product with \(\sigma_{C}\)
\(R \Join_{\theta} S = \sigma_{\theta}(R \times S)\)
- \(R \Join S = \pi_{L}(\sigma_{C}(R \times S))\)
- \(R \Join_{C} S = \sigma_{C}(R \times S)\)
Grouping & Aggregation
Grouping is not affected by duplicate elimination if the given aggregation function is \(min() or max()\). Group is however, affected by by duplicate elimination if the given aggregation function is \(avg(), count(), sum(), etc...\)
Operational Cost
Size Parameters
- B(R): Number of blocks needed to hold all tuples of R
- T(R): Total number of tuples in R
- V(R,a): Number of distinct value in R for attribute A
- S(R): Number of bytes for each tuple in R
The goal is to estimate the cost of execution a query plan as accurately as possible without actually executing it.
Size Estimation
| Relational Algebra | Size Estimation |
|---|---|
| \(W = \sigma_{z=val}(R)\) | \(T(W) = \frac{T(R)}{V(R,Z)}\) |
| \(W = \sigma_{z \le val}(R)\) | \(T(W) = \frac{T(R)}{3}\) |
| \(W = R1 \Join R2\) | \(T(W) = \frac{T(R1)T(R2)}{max(V(R1, A), V(R2, A))}\) |
The value of V(A, R) is being preserved as we traverse the logical query planning tree in a bottom-up fashion.
Example
Let \(W = (R \Join S) \Join T\)
\(V(W,A) = max(V(T,A), V(R \Join S,A) = max(V(T,A), max(V(R,A), V(S,A)))\)
Selection
| Clustered | Not Clustered | |
|---|---|---|
| Table Scan | B(R) | T(R) |
| Equality Index Scan | B(R)/V(R,a) | T(R)/V(R,a) |
| Inequality Index Scan | B(R)/3 | T(R)/3 |
Join
\(T(R \Join S) = \frac{T(R)T(S)}{max(V(R,A), V(S,A))}\)
The intuition behind this is that we take the cost of a cartesian product then multiple it by the probability that R and S share a common value in attribute A.
Textbook Example 16.23
Preservation of Value Sets
The value of V is being preserved
Assume three relations: R(a,b), S(b,c), U(c,d) with following parameters:
- T(R) = 1000
- V(R,b) = 20
- T(S) = 2000
- V(S,b) = 50
- V(S,c) = 100
- T(U) = 5000
- V(U,c) = 500
\(T((R \Join S) \Join U)\) can be calculated by:
- \(T(R \Join S) = \frac{T(R)T(S)}{max(V(R,b), V(S,b))} = \frac{1000 \times 2000}{50} = 40000\)
- \(T((R \Join S) \Join U) = \frac{T(R \Join S)T(U)}{max(V(R \Join S,c), V(U,c))} = \frac{40000 \times 5000}{max(100,500)} = 400000\)
Other Operations
| Operation | Size | Note |
|---|---|---|
| Union | \(T(R \cup S) = T(R) + T(S)\) | |
| Intersection | \(T(R \cap S) = \frac{min(T(R), T(S))}{2}\) | This is only an estimation because it could range from 0 to \(min(T(R), T(S))\) |
| Difference | \(T(R - S) = \frac{T(R) - T(S)}{2}\) | This is only an estimate because it could range from \(T(R)\) to \(T(R) - T(S)\) |
| Duplicate Elimination | \(T(\delta(R)) = \frac{T(R)}{2}\) | This is also an estimation because it could range from \(0\) to \(T(R)\) |
Pipelining and Materialization
Definition
Materialization: Results of operations are written onto disks
Pipelining: Results are being passed from operations to operations (by tuple)
Materialization in Memory
Materialization in memory is considered as pipelining
Physical query planning tree can be generated by following steps:
- Break the tree into subtress at each edge that represents materialization which will be executed one at a time.
- Order the execution of the subtrees in a buttom-up, left-to-right manner.
- Execute all nodes of each subtree using a network of iterators. All nodes should be executed simultaneously.