Skip to content

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:

  1. Sorting based
  2. Hash based
  3. 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
class Tuple:
    pass

class Relation:
    pass

for s: Tuple in S: Relation:
    for r: Tuple in S: Relation:
        if condition(r, s):
            return t = r join s
 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
# if B(S) <= B(R) and B(S) > M

class Block:
    pass

class Tuple:
    pass

class Relation:
    pass


for chunk: List[Block] in S: Relation:

    read_chunk_into_main_memory_buffer()
    organize_tuples_in_chunk_into_datastructure_with_searchkey_as_common_attributed_for_R_and_S

    for b: Block in R: Relation:

        read_b_into_main_memory_buffer()

        for r: Tuple in b:

            result = find_tuples_in_chunk_that_joins_with_r()

            return result
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:

\[R \Join \sigma_{C}(S) = \sigma_{C}(R \Join S)\]

then push it down the both arguments of the Join operator.

\[\sigma_{C}(R) \Join \sigma_{C}(S)\]

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:

  1. \(T(R \Join S) = \frac{T(R)T(S)}{max(V(R,b), V(S,b))} = \frac{1000 \times 2000}{50} = 40000\)
  2. \(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:

  1. Break the tree into subtress at each edge that represents materialization which will be executed one at a time.
  2. Order the execution of the subtrees in a buttom-up, left-to-right manner.
  3. Execute all nodes of each subtree using a network of iterators. All nodes should be executed simultaneously.
Back to top