Module Talon.Join
Joins of two queries by conditions on their columns.
left |> Query.join ~on right pairs the rows of left and right that the condition on matches. A condition is a value, a conjunction of atoms built with (&&) inside Join.( … ), as in Join.(keys [ "ticker" ] && eq "day" "date"). An atom names a left column, then a right column. A kind says which rows are kept, and a count how many matches each row must have. Each condition runs as one algorithm (Algorithms).
Matching. A join keeps exactly the pairs of rows on which every atom holds. It is the join on all, restricted to those pairs. An equality atom holds by key identity (Type.compare_value), so null keys match. A comparison holds where Expr.(<) or its sibling is true on the two values, in talon's total order (NaN after every number and -0. equal to 0.), and never at a null. A value cannot fail a join, and a null or a period whose values come in either order (overlaps) changes only which pairs match.
Columns. A Semi or Anti join has the left columns. Another join has the left columns, then the right columns without those of equality atoms, in order, so the key of eq l r appears once, as l. In a Full join that key has the common type of l and r (Type.common), since an unmatched right row gives it r's value. Every other column keeps its type. Except in a Semi or Anti join, a name on both sides is a problem, and talon adds no suffixes. Rename one side first.
Order. A join's rows come in left's order, each left row followed by its matches in right's order, and a Full join then appends right's unmatched rows in order.
Conditions
type condThe type for conditions, which are conjunctions of atoms.
keys ns matches the rows whose columns ns are the same keys on both sides. It is eq n n for each n of ns.
Raises Invalid_argument if ns is empty, since a join on no key is all, written so, or if ns names a column twice.
eq l r matches the rows whose left column l and right column r are the same key. l and r must meet (Type.common).
lt l r matches the rows whose left column l comes before the right column r, as Expr.(<) compares. l and r must meet (Type.common) at a type with an order, which an extension type lacks.
inside p (s, e) matches the rows whose left column p is from the right column s, included, to the right column e, excluded. It is ge p s && lt p e, so it matches only rows whose e is after their s.
overlaps (ls, le) (rs, re) matches the rows whose left period (ls, le) and right period (rs, re) overlap, as SQL-92's OVERLAPS predicate defines it (ISO/IEC 9075:1992, 8.11), in talon's total order. A period's start is the lesser of its two values and its stop the greater; with one value null, its start is the other one and its stop is unknown, and a period of two nulls overlaps nothing. Two periods overlap where their starts are equal and neither stop is unknown, or where one's start is after the other's start and before the other's known stop. So (0, 2) and (2, 3) do not overlap, (5, 5) overlaps (3, 8) and (5, 9) but not (1, 5), and (8, 3) is (3, 8). The four columns must meet at a type with an order.
c0 && c1 matches the pairs that both c0 and c1 match.
Raises Invalid_argument if the conjunction holds position and another atom, or holds one atom twice.
Algorithms
- A condition of equality atoms alone (
keys,eq) runs as an equality join, in O(n + m + matches) for sides of n and m rows. - Equality atoms, if any, with at least one comparison (
lt,le,gt,ge,inside) oroverlapsrun as a range join (Range joins). positiontakes no other atom and runs as its own algorithm.all, the condition with no atom, runs every pair, soall && cisc.
Range joins
A range join sorts one side's rows within their equality keys, the columns of its equality atoms, by the band column. Each row of the other side searches that order for its band, the rows the bounding atoms select. The band column is the column that comparisons name most, where a left and a right column of one name are two columns. On a tie between the sides it is the right column, and on a tie within a side the one written first. If the condition has an overlaps, the first one bounds the band and each side is sorted by its periods' starts. Otherwise the comparisons that name the band column bound the band, when they meet that column at the type of the first such comparison. Every other comparison or overlaps is tested on each pair the bands read.
Cost of a range join
A side needs no sort when its rows come grouped by their equality keys, each group ascending in the band column, which the join checks by reading them. The right side of an overlaps needs none when its rows come grouped that way by their periods' starts. A range join whose bands read p pairs, on a side of s rows searched from a side of o rows, costs O(s log s + o log s + p), less the sort when the side needs none. Matches out of the join's order are reordered once. An overlaps join sorts and searches each side. When every atom but the equality atoms bounds the band, a Semi or Anti join costs O(s log s + o log s), and the checks of each_left and each_right add no p term.
Kinds and counts
type kind = - #
| Inner(*The matched pairs.
*) - #
| Left(*The matched pairs, and each left row without a match, the right columns null.
*) - #
| Full(*As
*)Left, then each right row without a match, the left columns null except the keys, which take the right row's. - #
| Semi(*Each left row with a match, once, with the left columns only.
*) - #
| Anti(*Each left row without a match, with the left columns only.
*)
The type for join kinds, which say which rows a join keeps. A right join is a left join with the arguments swapped.