Joining Data from Multiple Tables
Overview
Data is often stored with other data to which it is related. For example, a company might save its employee-related data in one table, and its organizational data in another, as shown below.
|
|
However, there may be instances where you would like to combine - or join - certain columns of data from multiple tables for further analysis. For example, there might be a time where we would like to have one table that shows some or all the columns from both tables shown above.
Depending on our specific needs and the type of data available, we would use one of the several join-related methods available in the Deephaven query language.
Method |
Syntax |
---|---|
|
|
Left Table vs. Right Table
The tables used in any join method are usually referred to as the left table and the right table. The left table is the one to which you want to add columns of data. The right table is the one from which you are selecting columns of data to join to the left table. In most cases, there must be at least one common column of matching data that will be used as a key for joining the data.
Using the tables shown below, we will assume we want to get data from the departmentTable
(the right table) and join it to the employeeTable
(the left table). Both tables have DeptID
as a common column, so we can use that column as the key that will allow us to compare the data in each row and then subsequently join the content from the right table to the left table.
|
|
Here is the basic format and syntax for all join methods:
leftTable.joinMethod(rightTable, "ColumnsToMatch", "ColsToJoin")
First we list the name of the left table followed by the specific join method we want to use. The specific join method is then followed by three arguments:
- The name of the right table,
- the name of the column or columns on which to match, and
- the name of the column or columns in the right table to join to the left table.
This is the fundamental format for writing join statements in Deephaven. However, there are variations in the syntax for different circumstances.
Joining tables using multiple matching columns
It is possible to join tables with multiple matching columns. In these circumstances, we can add additional criteria within the ColumnsToMatch
argument as needed.
For a join with two different ColumnsToMatch
, the format would look like this, with each matching column listed and separated by commas within the quotes containing the second argument.
leftTable.join(rightTable, "MatchCol1,MatchCol2", "ColumnsToJoin")
Joining multiple columns
It is very common to join more than one column from the right table to the left table. To list more than one column to join, we need to include the names of all of the columns separated by commas within the quotes containing the third argument.
leftTable.join(rightTable, "ColumnsToMatch", "Col1ToJoin,Col2ToJoin,Col3ToJoin")
If we want to join all the columns from the right table, we could omit the third argument entirely.
leftTable.join(rightTable, "ColumnsToMatch")
Joining tables when the ColumnsToMatch
have different names
When joining data from two different tables, you may need to match on columns that do not have the same name. For example, in the tables below, the column representing the department number has a different name in each table.
|
|
To join the two tables based on this common column, we need to use the equals sign to tell Deephaven which pairs of columns it should match. DeptID
is the name of the column containing the matching criteria in the left table and DeptNumber
is the name of the column in the right column. Therefore, the following argument would be used for the ColumnsToMatch
portion of the query: "DeptID=DeptNumber
"
The entire query would look like this:
leftTable.join(rightTable, "DeptID=DeptNumber", "ColumnsToJoin")
Joining tables when you need to rename a column being joined
When joining data from two different tables, you may find a need to rename one or more of the right table's columns you want to join. For example, both tables below have a column labeled Telephone.
|
|
Even though these columns have the same name, they do not represent the same information. In the left table, the Telephone
column contains the employee's telephone number. In the right table, the Telephone
column contains the department's telephone number. To ensure we keep the distinction between the two columns, we will need to rename the right table's column that will be joined to the left table. This is accomplished within the query itself with DeptTelephone=Telephone
. Therefore, the following syntax would be used to rename the right table's column during the join process:
employeeTable.join(departmentTable,"DeptID", "DeptName, DeptTelephone=Telephone")
The Natural Join Method
When using a Natural Join method, the left table always retains the same number of rows with which it started. If there are no matching values in the corresponding column(s) of the right table for a specific row, data from the right table would still be joined to the left table using NULL values for the appended cell(s).
Since the results of a Natural Join will always have the same number of rows as in the original left table, using a Natural Join where there are multiple matching rows in the right table for any single rows in the left table will result in an error.
The syntax of a Natural Join looks like this:
leftTable.naturalJoin(rightTable, "ColumnsToMatch", "ColumnsToJoin")
The syntax for a Natural Join has three arguments:
- The name of the right table,
- the name of the column or columns that contain the matching criteria,
- and the name of the column or columns you want to join to the left table.
Example
Using the two sample tables below, the goal is to combine the data in the two tables using DeptID
as the ColumnsToMatch
. Since it is a naturalJoin
, all the rows in the left table will be combined with data from the rows in the right table, even if there are no matching criteria for some of the rows.
|
|
Here is the query.
t=employeeTable.naturalJoin(departmentTable,"DeptID", "DeptName,Telephone")
t
is the name of the variable used to store the resulting table. employeeTable
is the name of the left table. naturalJoin
is the method, which has three arguments. departmentTable
is the name of the right table. DeptID
is the column we want to use for the matching criteria. And, we want to join the DeptName
and Telephone
columns from the right table to the left table. However, Telephone
is the name of a column already in the left table, so we will need to rename the right table's Telephone
column something else. We will call it DeptTelephone
and adjust the query as follows:
t=employeeTable.naturalJoin(departmentTable,"DeptID", "DeptName,DeptTelephone=Telephone")
When we run this query in the console, Deephaven generates the t
table.
LastName |
DeptID |
Telephone |
DeptName |
DeptTelephone |
---|---|---|---|---|
Rafferty |
31 |
(347) 865-2567 |
Sales |
(646) 523-2154 |
Jones |
33 |
(917) 234-2145 |
Engineering |
(646) 435-4352 |
Steiner |
33 |
(212) 239-5782 |
Engineering |
(646) 435-4352 |
Robinson |
34 |
(952) 326-2355 |
Clerical |
(646) 235-4241 |
Smith |
34 |
NULL |
Clerical |
(646) 235-4241 |
Rogers |
NULL |
NULL |
NULL |
NULL |
As you can see, the t table retains the same six rows and three columns from the original left table. And, the extra two columns from the right table were added to the left table.
In the last row, there is no matching criteria for DeptID
. When using the naturalJoin
method, if there are no matching criteria, the columns from the right table are joined using NULL
values.
The Exact Join Method
The Exact Join method requires exactly one match from the right table for each of the rows in the left table. If there is a row in the left table that does not match any rows in the right table, or there is a row in the left table that matches more than one row in the right table, the operation will fail.
The syntax of the Exact Join method looks like this:
leftTable.exactJoin(rightTable,"ColumnsToMatch","ColumnsToJoin")
The Exact Join method has three arguments:
- The name of the right table,
- the name of the column or columns that contain the matching criteria, and
- the name of the column or columns you want to join to the left table.
Example
Using the two sample tables below, we will write a query using the exactJoin
method to join the data in the right table to the left table.
|
|
We will start out the query as follows:
t=employeeTable.exactJoin(departmentTable,"DeptID", "DeptName,Telephone")
This time we will create a new variable named t
to store the new table. employeeTable
is the name of the left table. exactJoin
is the method, which has three arguments. departmentTable
is the name of the right table. DeptID
is the column to use for the matching criteria. And, we want to join the data in the DeptName
and Telephone
columns from the right table to the left table. However, Telephone
is the name of a column already in the left table, and duplicate column names in a table are not allowed, so we will need to rename the right table's Telephone
column something else. We will call it DeptTelephone
and then adjust the query as follows:
t=employeeTable.exactJoin(departmentTable,"DeptID", "DeptName, DeptTelephone=Telephone")
When we run this query in the console, Deephaven generates the t
table.
t
LastName |
DeptID |
Telephone |
DeptName |
DeptTelephone |
---|---|---|---|---|
Rafferty |
31 |
(347) 865-2567 |
Sales |
(646) 523-2154 |
Jones |
33 |
(917) 234-2145 |
Engineering |
(646) 435-4352 |
Steiner |
33 |
(212) 239-5782 |
Engineering |
(646) 435-4352 |
Robins |
34 |
(952) 326-2355 |
Clerical |
(646) 235-4241 |
Smith |
34 |
NULL |
Clerical |
(646) 235-4241 |
As you can see, the t
table retains the same five rows and three columns from the original left table. And, the extra two columns from the right table are added to the left table. This exactJoin
worked because there was a match in the right table for every DeptID
listed in the left table.
To contrast this, let us look at a scenario where there is not a match.
Example of a failed exactJoin
For this example, we will keep the same departmentTable
used earlier. However, we will change one of the values in the employeeTable
.
|
|
The exactJoin
method expects exactly one match in the right table for each row in the matching columns from the left table. In this case, the DeptID
value for the employee named "Cross" is 37, and there is no match in the right table for this DeptID
. Therefore, this exactJoin
process will fail.
The Join Method
When using the Join method, Deephaven compares the left table to the right table, and for every instance where there are matching criteria in the ColumnsToMatch
argument for both tables, Deephaven will then "join" the data requested from the right table to the left table. Rows that do not have matching criteria will not be included in the result. Also, if there are multiple matches between a row from the left table and rows from the right table, all matching combinations will be included; therefore, it is possible for the results of a join operation to have less rows or more rows than are present in the left table.
The syntax of the Join method looks like this:
leftTable.join(rightTable, "ColumnsToMatch", "ColumnsToJoin", numRightBitsToReserve)
The Join method has three arguments:
- The name of the right table,
- the name of the column or columns that contain the matching criteria,
- the name of the column or columns you want to join to the left table, and
- (optional) the number of bits to reserve for the right table rows. The default is 10; see the
OutOfKeySpaceException
section below for more details.
Example
Using the two tables below, the goal is to join the information in the two tables only when there is matching criteria in each table.
|
|
Here is the query:
t=employeeTable.join(departmentTable,"DeptID", "DeptName,DeptTelephone=Telephone")
This time we will create a new variable named t
to store the new table. employeeTable
is the name of the left table. join
is the method, which has three arguments. departmentTable
is the name of the right table. DeptID
is the column to use for the matching criteria. And, we want to join the data in the DeptName
and Telephone
columns from the right table to the left table.
When we run this query in the console, Deephaven generates the t
table.
t
LastName |
DeptID |
Telephone |
DeptName |
DeptTelephone |
---|---|---|---|---|
Rafferty |
31 |
(347) 865-2567 |
Sales |
(646) 523-2154 |
Jones |
33 |
(917) 234-2145 |
Engineering |
(646) 435-4352 |
Steiner |
33 |
(212) 239-5782 |
Engineering |
(646) 435-4352 |
Robins |
34 |
(952) 326-2355 |
Clerical |
(646) 235-4241 |
Smith |
34 |
NULL |
Clerical |
(646) 235-4241 |
At first glance, the two columns from the right table (DeptName
and DeptTelephone
) have been added to the left table. However, if you look closely, the left table originally had six rows of data, but the new t
table only has five rows. The join method requires a match in the DeptID
column for both tables, but there was not a match in the last row of the Employee table, which was empty or null. As such, that row was not included in the resulting table.
One special case of the join
method is a cross-join, or cartesian product, which takes the following form:
t2=employeeTable.exactJoin(departmentTable,"", "DeptName,DeptTelephone")
Note that the second argument - the ColumnsToMatch
- is empty. The result of this query is a table with every possible combination of rows from the two tables. In this case, with six employee rows and four department rows, the resulting t2 table will contain 24 rows.
Troubleshooting an OutOfKeySpace
Exception
To efficiently produce updates, the bits that represent a key for a given result row are split into two. Unless specified, join reserves 10 bits to represent a right row. When there are too few bits to represent all of the right rows for a given aggregation group the table will shift a bit from the left side to the right side. The default of 10 bits was carefully chosen because it results in an efficient implementation to process live updates.
An OutOfKeySpaceException
is thrown when the total number of bits needed to express the result table exceeds that needed to represent Long.MAX_VALUE
. The exception provides details such as how much key-space the left and right tables are using, how sparse the tables appear to be, and how many of the bits in the result key-space are being used for each table.
There are a few workarounds:
- If the left table is sparse, consider flattening the left table. To flatten a table invoke the no-argument
flatten()
method. - If there are no key-columns and the right table is sparse, consider flattening the right table. (Note: when there are key-columns, flattening the right table will not affect bits allocated to right rows.)
- If the maximum size of a right table's group is small, you can reserve fewer bits by setting numRightBitsToReserve on initialization.
The Left Join Method
The Left Join method returns a table that has the same number of rows as the left table, one column for each of the left table's columns, and one column corresponding to each of the right table's columns that are included in the ColumnsToJoin
argument. However, when the right table's columns are joined to the left table, the data joined is grouped into arrays for each corresponding row in the left table that shares the same matching criteria. For example, consider this list of colors.
Red
White
BluePurple
Green
The Left Join method would allow you to aggregate the values from that list into an array like the following:
[Red, White, Blue, Purple, Green]
The Left Join method also preserves the order of values in each array. As you can see in the array above, the order of the items in the list is the same used in the array.
The syntax of the Left Join method looks like this:
.leftJoin(rightTable, "ColumnsToMatch", "ColumnsToJoin")
The Left Join method has three arguments:
- The name of the right table,
- the name of the column or columns that contain the matching criteria, and
- the name of the column or columns you want to join to the left table.
Example
Using the two tables below, the goal is to combine the data in the two tables. Because we are using the Left Join method, all the rows in the left table will be combined with data from the rows in the right table using the Ticker column as the ColunmToMatch
. If there are no matching criteria in the in the right table for a given row, the joined value will be an empty array.
|
|
Here is the query we will start with:
t=trades.leftJoin(quotes,"Ticker", "Time,Mid")
We created a new variable named t
to store the new table. trades
is the name of the left table. leftJoin
is the method, which has three arguments. Quotes
is the name of the right table. Ticker
is the column we want to use for the matching criteria. Time
and Mid
are the names of the columns we want to join to the left table.
If we use the query as is, we will end up with a column labeled Time
after the query is processed. However, it might not be clear if the time shown refers to trade time or quote time. We can clear that up before the query runs by renaming the right table's Time
column to QuoteTime
using QuoteTime=Time.
We now have our final query.
t=trades.leftJoin(quotes,"Ticker", "QuoteTime=Time,Mid")
When we run this query in the console, Deephaven generates the t
table.
t
Ticker |
Size |
Price |
QuoteTime |
Mid |
---|---|---|---|---|
AAPL |
10 |
2.5 |
[9:30,9:45] |
[3.0,3.2] |
AAPL |
20 |
3.4 |
[9:30,9:45] |
[3.0,3.2] |
AAPL |
-5 |
3.0 |
[9:30,9:45] |
[3.0,3.2] |
IBM |
13 |
100.50 |
[9:30,9:45,10:00] |
[100,100.50,110] |
IBM |
-13 |
110 |
[9:30,9:45,10:00] |
[100,100.50,110] |
GOOG |
36 |
780 |
|
|
The new t
table retains the same five rows and three columns from the original left table. Two columns from the right table have been added to the left table using the Ticker
as the matching criteria. And, the column from the right table named Time
is now named QuoteTime
. However, and this is the primary distinction when using the leftJoin
method, the data joined from the right table is now aggregated for each corresponding row in the left table.
For example, there are three rows in the left table that show trades of Apple, and two rows in the right table that show quotes for Apple. In the new t
table, all of the corresponding row values for the ticker AAPL in the QuoteTime
and Mid
columns from the right table are now aggregated into an array and placed into each row of the left table that has AAPL as its Ticker
.
The result for IBM also reflects this aggregation. For each row detailing IBM in the left table, there are three values, which correspond to the QuoteTime
and Mid
values for each of three rows in the right table that have IBM as the Ticker
.
For the last row, there is no match in the right table for a Ticker
value of GOOG, so the QuoteTime
and Mid
columns for that row would be null.
The As-of Join (aj) and the Reverse As-of Join (raj) Methods
Unique to Deephaven, the As-of Join and the Reverse As-Of Join methods enable users to join data from tables that do not have an exact match in the ColumnsToMatch
argument. This is especially useful when analyzing temporal data.
The As-of Join method allows you to match on the closest value that is less than or equal to, whereas the Reverse As-of Join method allows you to match on the closest value that is equal to or greater than. Let's take a closer look.
The As-of Join Method (aj)
The syntax of the As-of Join method starts out like this:
leftTable.aj(rightTable, "ColumnsToMatch", "ColumnsToJoin")
The As-of Join method is represented by .aj
and has three arguments:
- The name of the right table,
- the name of the column or columns that contain the matching criteria, and
- the name of the column or columns you want to join to the left table.
This syntax is similar to other join methods. However, there is one important difference with the values entered for ColumnsToMatch
. While one matching column can be used in an As-of Join, the method usually involves two or more matching columns. The initial column(s) included in the ColumnsToMatch
argument is used to perform an exact match in both the left table and the right table. However, the last column included in that argument is treated differently, So, let's change the syntax to show this distinction.
.aj(rightTable, "ColumnsToMatch, LastColumnToMatch", "ColumnsToJoin")
(Note: If only one column is used for the ColumnsToMatch
argument, that column is considered the LastColumnToMatch
.)
The column designated as the LastColumnToMatch
does not need to be an exact match. Instead, the As-of Join enables you to find the closest match in the right table that does not exceed the value of this column in the left table. In other words, Deephaven finds the value in the right table that is closest to the corresponding value in the left table without going over. Once the closest-without-going-over value is found in the right table, the values in the rows of the columns listed in the ColumnsToJoin
argument are added to the corresponding row in the left table. Let us look at an example.
Example
Using the two tables below, the goal is to join the two tables to show each Ticker's Mid
value immediately before or at the same time each Trade occurred. That will require joining the QuoteTime
and Mid
columns from the right table to the left table when the Ticker
is an exact match between both tables. However, we will only want the join to occur when the QuoteTime
in the right table is closest without going over the TradeTime
in the left table.
|
|
Let's walk through the query construction. Here is a start:
t=trades.aj(
We first create a new variable named t
to store the new table. trades
is the name of the left table and aj
is the method, which has three arguments. The first argument is the name of the right table, so we will add that to the query
t=trades.aj(quotes,
The next argument involves the ColumnsToMatch
. In this case, Ticker
is the name of the column we want to use for the exact match between both tables, so we will enter it here.
t=trades.aj(quotes,"Ticker,
If you remember, our goal is to find the Mid
price immediately before or at the same time of each Trade
. To do so, we will need to compare the times for each respective event. The time associated with each trade in the left table is recorded in the TradeTime
column. And, the time associated with each Mid
price in the right table is recorded in the QuoteTime
column. So, we will want to match each respective TradeTime
value on a closest-without-going-over basis with the nearest QuoteTime
value. To do that, we will tell Deephaven to match the two columns using TradeTime = QuoteTime
. Our query now looks like this:
t=trades.aj(quotes,"Ticker,TradeTime=QuoteTime"
Finally, we want to join the data from the QuoteTime
and Mid
columns in the right table onto the left table, so we will include those column names as the third argument. Our query is now complete.
t=trades.aj(quotes,"Ticker,TradeTime=QuoteTime","QuoteTime,Mid")
When we run this query in the console, Deephaven generates the new t
table. (The trades
and quotes
table are also included for easy reference.)
|
|
t
Ticker |
Size |
Price |
TradeTime |
QuoteTime |
Mid |
---|---|---|---|---|---|
AAPL |
10 |
2.5 |
9:30 |
|
|
AAPL |
20 |
3.4 |
9:40 |
9:31 |
2.5 |
AAPL |
-5 |
3.0 |
16:00 |
9:45 |
3.4 |
IBM |
13 |
100.50 |
9:30 |
9:30 |
105 |
IBM |
-13 |
110 |
10:00 |
9:52 |
110 |
In the first row of the trades
table, there is a TradeTime
of 9:30 for AAPL. However, for that same ticker, there is no QuoteTime
earlier than or equal to 9:30 in the right table. Since no row in the right table satisfies the conditions of the as-of join process, no data is joined. Null values are used in the QuoteTime
and Mid
columns for this row.
In the second row of the trades
table, there is a TradeTime
of 9:40 for AAPL. In the right table, there is only one QuoteTime
earlier than or equal to the TradeTime
for AAPL. So the row showing 09:31 in the QuoteTime
column is used for the join.
The third row of the trades
table shows a trade in AAPL
at 16:00. In the quotes
table, we can see that there are two QuoteTimes
earlier than or equal to this TradeTime
. One is at 9:31, and the other is at 9:45. The as-of join will use the closest match from the right table when QuoteTime
does not exceed the TradeTime
, so the data associated with the 9:45 QuoteTime
row is joined into the new table because 9:45 was the closest value.
In the fourth row of the trades
table, the TradeTime
for IBM is 9:30. In the right table there is only one QuoteTime
earlier or equal to the respective TradeTime
for IBM, so the data in the row with 09:30 for the QuoteTime
is joined.
The fifth row of the trades
table shows a trade in IBM occurred at 10:00. However, in the quotes
table, we can see there are two times listed in the QuoteTime
column that are earlier than or equal to TradeTime
for IBM. The As-of Join uses the closest value without going over, so the value in the fourth row of the right table is joined.
However, if the left table contains a null value in the LastColumnToMatch
, then no match with the right table will be attempted. In this case, the QuoteTime
and Mid
columns in the joined table would contain null values for those rows.
There is one more important point to note when using the As-of join. The right table must be structured so the data in the LastColumnToMatch
is sorted in ascending order for each group defined by the remaining columns to match.
For example, Ticker
is used for the matching criteria and the quotes
table is the right table. All of the data within the QuoteTime
column for the ticker AAPL must occur in ascending order, and all of the data within the QuoteTime
columns for the ticker IBM must occur in ascending order. However, it is acceptable for a 09:30 IBM record to come after a 09:31 AAPL record.
quotes
Ticker |
QuoteTime |
Mid |
---|---|---|
AAPL |
9:31 |
2.5 |
IBM |
9:30 |
105 |
AAPL |
9:45 |
3.4 |
IBM |
9:52 |
110 |
IBM |
10:11 |
111 |
The Reverse As-of Join Method (raj)
The Reverse As-of Join method is the opposite of the As-of Join method. The As-of Join method allows you to match on the closest value that is less than or equal to, whereas the Reverse As-of Join method allows you to match on closest value that is equal to or greater.
The Reverse As-of Join method is represented by .raj
. Other than that, the syntax used for a Reverse As-of Join is identical to that used for an As-of Join.
Troubleshooting As-of Joins
When data used for As-of Joins (and Reverse As-of Joins) is improperly ordered, the query will present incorrect results without notifying the user, which can create additional problems on subsequent data processing. While inconvenient, this was an intentional design decision based on two primary factors:
- Most data used by Deephaven is carefully structured and ordered upon import, so incorrect ordering is not common.
- The time and computational expense involved in testing for that unlikely condition would be significantly detrimental to performance for all As-of Joins.
Therefore, if you get unexpected and/or incorrect results using an As-of Join or Reverse As-of Join method in your query, you should examine the ordering of the data in the column that serves as the LastColumnToJoin
in the right table.
Data Ordering when Matching on Multiple ColumnsToMatch
Finally, there is one more point to consider in regard to properly ordered data when using the As-of Join and Reverse As-of Join methods. Let us look again at the syntax for an As-of Join:
.aj(rightTable, "ColumnsToMatch, LastColumnToMatch", "ColumnsToJoin")
In our previous example, Ticker
was the only column used for the ColumnsToMatch
and it required an exact match in both tables for the join to occur. The Time
column was used as the LastColumnsToMatch
. It did not have to be an exact match, but it did need to be properly ordered.
However, with As-of Joins, it is possible to have more than one column for the exact match. Let us look at a slightly different version of the syntax with an additional ColumnToMatch
:
.aj(rightTable, "Col1ToMatch, Col2ToMatch, LastColumnToMatch", "ColumnsToJoin")
When there are multiple columns that require the exact match, the data in the LastColumnToMatch
for each set of multiple matching columns must also be properly ordered.
Let us look at a quick example showing only the ColumnsToMatch
and LastColumnToMatch
data in a table. Columns A and B must match exactly, and Column C serves as the LastColumnToMatch
, which does not need to match, but must be ordered properly for each set of ColumnsToMatch
.
Col1ToMatch |
Col2ToMatch |
LastColumnToMatch |
---|---|---|
A |
B |
C |
1 |
1 |
19 |
1 |
2 |
27 |
2 |
3 |
26 |
1 |
1 |
38 |
2 |
3 |
51 |
To determine if this data is in the proper order, we need to look the distinct sets of data in the columns for the exact matches. For simplicity, we will break them out into subtables.
The first and fourth rows are the only rows that have a 1 in both columns A and B, so that is the first subtable.
A |
B |
C |
1 |
1 |
19 |
1 |
1 |
38 |
Only the second row has a 1 in the column A and a 2 in the column B, so we can make that its own subtable.
A |
B |
C |
1 |
2 |
27 |
Finally, rows 3 and 5 are the only rows with a 2 in column A and a 3 in the column B, so we will make that its own subtable as well.
A |
B |
C |
2 |
3 |
26 |
2 |
3 |
51 |
When we look at the ordering of column C in each of three subtables, we can see the data is properly ordered in each. Therefore the As-of Join would be completed correctly.
Last Updated: 16 February 2021 18:07 -04:00 UTC Deephaven v.1.20200928 (See other versions)
Deephaven Documentation Copyright 2016-2020 Deephaven Data Labs, LLC All Rights Reserved