@@ -5,58 +5,67 @@ description:
55 This document describes available hints and when to use them.
66---
77
8- QuestDB's query optimizer automatically selects execution plans for SQL queries based on heuristics. The default
9- execution strategy should be the fastest for most datasets. You may use hints to select a specific execution strategy
10- which may (or may not) outperform the default strategy . SQL hints influence the execution strategy of queries without
11- changing their semantics.
8+ QuestDB's query optimizer automatically selects execution plans for SQL queries based on heuristics. While the default
9+ execution strategy should be the fastest for most scenarios, you can use hints to select a specific strategy that may
10+ better suit your data's characteristics . SQL hints influence the execution strategy of queries without changing their
11+ semantics.
1212
1313## Hint Syntax
1414
1515In QuestDB, SQL hints are specified as SQL block comments with a plus sign after the opening comment marker. Hints must
16- be placed immediately after the SELECT keyword:
16+ be placed immediately after the ` SELECT ` keyword:
1717
1818``` questdb-sql title="SQL hint syntax"
1919SELECT /*+ HINT_NAME(parameter1 parameter2) */ columns FROM table;
2020```
2121
22- Hints are entirely optional and designed to be a safe optimization mechanism:
22+ Hints are designed to be a safe optimization mechanism:
2323
24- - The database will use default optimization strategies when no hints are provided
25- - Syntax errors inside a hint block won't fail the entire SQL query
26- - The database safely ignores unknown hints
27- - Only block comment hints (` /*+ HINT */ ` ) are supported, not line comment hints (` --+ HINT ` )
24+ - The database uses default optimization strategies when no hints are provided.
25+ - Syntax errors inside a hint block won't fail the entire SQL query.
26+ - The database safely ignores unknown hints.
27+ - Only block comment hints (` /*+ HINT */ ` ) are supported, not line comment hints (` --+ HINT ` ).
2828
29- ## Available Hints
29+ -----
3030
31- ### USE_ASOF_BINARY_SEARCH
31+ ## Binary Search Optimizations and Hints
3232
33- The ` USE_ASOF_BINARY_SEARCH ` hint enables a specialized binary search optimization for
34- non-keyed [ ASOF joins ] ( /reference/sql/asof-join/ ) when filtering is applied to the joined table. This hint requires two
35- parameters that specify the table aliases participating in the join .
33+ Since QuestDB 9.0.0, QuestDB's optimizer defaults to using a binary search-based strategy for ** ` ASOF JOIN ` ** and
34+ ** ` LT JOIN ` ** (Less Than Join) queries that have a filter on the right-hand side (the joined or lookup table) . This
35+ approach is generally faster as it avoids a full table scan .
3636
37- ``` questdb-sql title="Optimizing ASOF join with binary search"
38- SELECT /*+ USE_ASOF_BINARY_SEARCH(orders md) */
37+ However, for some specific data distributions and filter conditions, the previous strategy of performing a parallel full
38+ table scan can be more performant. For these cases, QuestDB provides hints to * avoid* the default binary search.
39+
40+ ### AVOID\_ ASOF\_ BINARY\_ SEARCH and AVOID\_ LT\_ BINARY\_ SEARCH
41+
42+ These hints instruct the optimizer to revert to the pre-9.0 execution strategy for ` ASOF JOIN ` and ` LT JOIN ` queries,
43+ respectively. This older strategy involves performing a full parallel scan on the joined table to apply filters * before*
44+ executing the join.
45+
46+ - ` AVOID_ASOF_BINARY_SEARCH(left_table_alias right_table_alias) ` : Use for ** ` ASOF JOIN ` ** queries.
47+ - ` AVOID_LT_BINARY_SEARCH(table_alias) ` : Use for ** ` LT JOIN ` ** queries.
48+
49+ <!-- end list -->
50+
51+ ``` questdb-sql title="Avoiding binary search for an ASOF join"
52+ SELECT /*+ AVOID_ASOF_BINARY_SEARCH(orders md) */
3953 orders.ts, orders.price, md.md_ts, md.bid, md.ask
4054FROM orders
4155ASOF JOIN (
4256 SELECT ts as md_ts, bid, ask FROM market_data
43- WHERE state = 'VALID ' --filter on the joined table
57+ WHERE state = 'INVALID ' -- Highly selective filter
4458) md;
4559```
4660
4761#### How it works
4862
49- By default (without this hint), QuestDB processes ASOF joins by:
50-
51- 1 . Applying filters to the joined table in parallel
52- 2 . Joining the filtered results to the main table
53-
54- With the ` USE_ASOF_BINARY_SEARCH ` hint, QuestDB changes the execution strategy:
63+ The ** default strategy (binary search)** works as follows:
5564
56- 1 . For each record in the main table, it uses [ binary search] ( https://en.wikipedia.org/wiki/Binary_search ) to locate
57- a record with a matching timestamp in the joined table
58- 2 . Starting from this located timestamp match , it then iterates backward through rows in the joined table, in a single
59- thread, until finding a row that matches the filter condition
65+ 1 . For each record in the main table, it uses a binary search to quickly locate a record with a matching timestamp in
66+ the joined table.
67+ 2 . Starting from this located timestamp, it then iterates backward through rows in the joined table, in a single thread,
68+ evaluating the filter condition until a match is found.
6069
6170<Screenshot
6271alt="Diagram showing execution of the USE_ASOF_BINARY_SEARCH hint"
@@ -65,36 +74,39 @@ src="images/docs/concepts/asof-join-binary-search-strategy.svg"
6574width={745}
6675/>
6776
68- #### When to use
77+ The ** hinted strategy ( ` AVOID_..._BINARY_SEARCH ` ) ** forces this plan:
6978
70- This optimization is particularly beneficial when:
79+ 1 . Apply the filter to the * entire* joined table in parallel.
80+ 2 . Join the filtered (and now much smaller) result set to the main table.
7181
72- - The joined table is significantly larger than the main table
73- - The filter on the joined table has low selectivity (meaning it doesn't eliminate many rows)
74- - The joined table data is likely to be "cold" (not cached in memory)
82+ #### When to use the AVOID hints
7583
76- When joined table data is cold, the default strategy must read all rows from disk to evaluate the filter. This becomes
77- especially expensive on slower I/O systems like EBS (Elastic Block Storage). The binary search approach significantly
78- reduces I/O operations by reading only the specific portions of data needed for each join operation.
84+ You should only need these hints in a specific scenario: when the filter on your joined table is ** highly selective** .
7985
80- However, when a filter is highly selective (eliminates most rows), the binary search strategy may be less efficient.
81- This happens because after finding a timestamp match, the strategy must iterate backward in a single thread, evaluating
82- the filter condition at each step until it finds a matching row. With highly selective filters, this sequential search
83- may need to examine many rows before finding a match.
86+ A filter is considered highly selective if it eliminates a very large percentage of rows (e.g., more than 95%). In this
87+ situation, the hinted strategy can be faster because:
8488
85- As a rule of thumb, the binary search strategy tends to outperform the default strategy when the filter eliminates less
86- than 5% of rows from the joined table. However, optimal performance also depends on other factors such as the ratio
87- between main and joined table sizes, available hardware resources, disk I/O performance, and data distribution.
89+ - The parallel pre-filtering step rapidly reduces the joined table to a very small size.
90+ - The subsequent join operation is then very fast.
8891
89- In contrast , the default strategy processes and filters the joined table in parallel, which can be much faster for
90- highly selective filters despite requiring an initial full table scan .
92+ Conversely , the default binary search can be slower with highly selective filters because its single-threaded backward
93+ scan may have to check many rows before finding one that satisfies the filter condition .
9194
92- #### Execution Plan Observation
93- You can verify how QuestDB executes your query by examining its execution plan
94- with the [ ` EXPLAIN ` statement] ( /reference/sql/explain/ ) :
95+ For most other cases, especially with filters that have low selectivity or when the joined table data is not in
96+ memory ("cold"), the default binary search is significantly faster as it minimizes I/O operations.
9597
96- ``` questdb-sql title="Observing execution plan with USE_ASOF_BINARY_SEARCH"
97- EXPLAIN SELECT /*+ USE_ASOF_BINARY_SEARCH(orders md) */
98+ -----
99+
100+ ### Execution Plan Observation
101+
102+ You can verify how QuestDB executes your query by examining its execution plan with the ` EXPLAIN ` statement.
103+
104+ #### Default Execution Plan (Binary Search)
105+
106+ Without any hints, a filtered ` ASOF JOIN ` will use the binary search strategy.
107+
108+ ``` questdb-sql title="Observing the default execution plan"
109+ EXPLAIN SELECT
98110 orders.ts, orders.price, md.md_ts, md.bid, md.ask
99111FROM orders
100112ASOF JOIN (
@@ -103,18 +115,20 @@ ASOF JOIN (
103115) md;
104116```
105117
106- When the hint is applied successfully, the execution plan will show a Filtered AsOf Join Fast Scan operator,
107- confirming that the binary search strategy is being used:
118+ The execution plan will show a ` Filtered AsOf Join Fast Scan ` operator, confirming the binary search strategy is being
119+ used.
108120
109121<Screenshot
110- alt="Screen capture of the EXPLAIN output for USE_ASOF_BINARY_SEARCH "
122+ alt="Screen capture of the EXPLAIN output showing the default Filtered AsOf Join Fast Scan "
111123src="images/docs/concepts/filtered-asof-plan-example.png"
112124/>
113125
114- For comparison, here's what happens without the hint:
126+ #### Hinted Execution Plan (Full Scan)
127+
128+ When you use the ` AVOID_ASOF_BINARY_SEARCH ` hint, the plan changes.
115129
116- ``` questdb-sql title="Observing execution plan without USE_ASOF_BINARY_SEARCH "
117- EXPLAIN SELECT
130+ ``` questdb-sql title="Observing execution plan with the AVOID hint "
131+ EXPLAIN SELECT /*+ AVOID_ASOF_BINARY_SEARCH(orders md) */
118132 orders.ts, orders.price, md.md_ts, md.bid, md.ask
119133FROM orders
120134ASOF JOIN (
@@ -123,12 +137,10 @@ ASOF JOIN (
123137) md;
124138```
125139
126- The execution plan will show:
127-
128- - A standard ` AsOf Join ` operator instead of ` Filtered AsOf Join Fast Scan `
129- - A separate filtering step that processes the joined table in parallel first
140+ The execution plan will now show a standard ` AsOf Join ` operator and a separate, preceding filtering step on the joined
141+ table.
130142
131143<Screenshot
132- alt="Screen capture of the EXPLAIN output for default ASOF join"
144+ alt="Screen capture of the EXPLAIN output for the hinted ASOF join, showing a separate filter "
133145src="images/docs/concepts/default-asof-plan-example.png"
134146/>
0 commit comments