Joining an entity to a query with a simple join

The term join comes from relational algebra. The term has special meaning for SQL and the query builder API.

Joining tables in SQL SELECT statements

In SQL, you can join two tables based on columns with matching values to form a new, virtual join table. The rows in the join table contain columns from each original table. On the join table, you can specify restrictions, which can have columns from both tables. When the SQL query runs, the result set has rows and columns from the join table. A join typically involves a column in one table that contains a foreign key value that matches a primary key value in a column in a second table. A primary key is a column with values that uniquely identify each row in a database table. A foreign key is a column in one table that contains values of a primary key in another table. The definition of a foreign key column specifies whether the values in the column are unique.

The following example SQL Select statement uses the JOIN keyword to join the addresses table to the companies table.

SELECT * FROM companies
  JOIN addresses 
    ON addresses.ID = companies.primary_address;

In response to the preceding example SQL statement, the database returns a result set with all the rows in the companies table that have a primary address. The result set includes columns for the companies and columns for their primary addresses. The result set does not include companies without a primary address.

For example, the companies and addresses tables have the following rows of information:

companies:

id    

name                                         

primary_address

c:1

Hoffman Associates

a:1

c:2

Golden Arms Apartments

a:2

c:3

Industrial Wire and Chain

c:4

North Creek Auto

a:3

c:5

Jamison & Sons

a:4

addresses:

id  

street_address           

city               

postal_code

a:1

123 Main St.

White Bluff

AB-2450

a:2

45112 E. Maplewood

Columbus

EF-6370

a:3

3945 12th Ave.

Arlington

IB-4434

a:4

930 Capital Way

Arlington

IR-8775

The preceding example SQL statement returns the following result set.

Result set with companies and addresses tables joined:

id

name

primary_address

id

street_address

city

postal_code

c:1

Hoffman Associates

a:1

a:1

123 Main St.

White Bluff

AB-2450

c:2

Golden Arms Apartments

a:2

a:2

45112 E. Maplewood

Columbus

EF-6370

c:4

North Creek Auto

a:3

a:3

3945 12th Ave.

Arlington

IB-4434

c:5

Jamison & Sons

a:4

a:4

930 Capital Way

Arlington

IR-8775

The columns in the result set are a union of the columns in the companies and addresses tables. The company named “Industrial Wire and Chain” is not in the result set. That row in the companies table has null as the value for primary_address, so no row in the addresses table matches.

Joining entities with the query builder API

With the query builder API, you can join a related entity type to the primary entity type of the query. When you join an entity type to a query, the query builder API construct an internal object to represent the joined entity and its participation in the query.

In the simplest case, use the join method to join a dependent entity type to a query. You must specify the name of a property on the primary entity that the Data Dictionary defines as a foreign key to the dependent entity. Use the Gosu property name, not the actual column name in the database.

For example, the following Gosu code shows how to join the Address entity to a query of the Company entity. The code uses the PrimaryAddress property of Company, which is a foreign key to Address.

uses gw.api.database.Query

var query = Query.make(Company)
query.join(Company#PrimaryAddress) // Join Address entity to the query by a foreign key on Company
var select = query.select()

// Fetch the data with a for loop and print it. 
for (company in select) {
  print (company)
}
Using the sample data shown earlier, the preceding query builder API query produces the following result.

Result object with Company instances from a join query:

Name

Hoffman Associates

Golden Arms Apartments

North Creek Auto

Jamison & Sons

The Company named “Industrial Wire and Chain” is not in the result object. This instance has null as its primary_address, so no instance of Address matches.

A significant difference from SQL is that the query builder API return only instances of the primary entity type. With the query builder API, you use dot notation to access information from the joined entity type. For example:

uses gw.api.database.Query

var query = Query.make(Company)
query.join(Company#PrimaryAddress) // Join Address entity to the query by a foreign key on Company
var select = query.select()

// Fetch the data with a for loop and print it --
for (company in select) {
  print (company + ", " + company.PrimaryAddress.City)
}

The preceding Gosu code fetches the Address instance lazily from the database. If the record is not available in the cache, this code could cause the execution of a query for every company. To reduce the number of database queries, you can use a row query to retrieve the complete set of rows and the necessary columns from the primary and joined tables.

See also