JOIN and DEFAULT Advanced Patterns
This guide covers advanced patterns for combining JOIN and DEFAULT options, plus real-world scenarios for handling orphan records (records with broken or missing relationships). These patterns are essential for building robust reports that gracefully handle incomplete data.
Understanding Orphan Records
Orphan records occur when a foreign key field points to a record that doesn't exist, or when the field is empty. This commonly happens when:
- A customer is deleted but their orders remain
- Data is imported with incomplete relationships
- Users haven't filled in optional relation fields
- Multi-hop paths break at any intermediate step
AXEL provides two strategies for handling orphans: DEFAULT values and JOIN modes.
Combining Options
You can combine VIA, JOIN, and DEFAULT in any order within a RELATED call. This flexibility allows you to build exactly the behavior you need.
VIA + DEFAULT
Use DEFAULT to provide meaningful fallback values when the relationship path is broken:
RELATED(
[Departments][name],
VIA(product, category, department),
DEFAULT: "General"
)
What happens: If any hop in the path (product → category → department) is missing, AXEL returns "General" instead of NULL.
VIA + JOIN: INNER
Use INNER JOIN to exclude rows where the relationship doesn't exist:
RELATED(
[Regions][name],
VIA(customer, region),
JOIN: INNER
)
What happens: Rows where the customer or region relationship is missing are excluded from the result entirely.
Full Combination
Combine all three options for complete control:
RELATED(
[Regions][manager],
VIA(customer, region),
JOIN: LEFT,
DEFAULT: "No Regional Manager"
)
What happens: All rows are included (LEFT JOIN), but orphans show "No Regional Manager" instead of NULL.
Options Order Doesn't Matter
AXEL parses options by keyword, so these are all equivalent:
RELATED([Table][col], VIA(field), JOIN: LEFT, DEFAULT: "X")
RELATED([Table][col], DEFAULT: "X", VIA(field), JOIN: LEFT)
RELATED([Table][col], JOIN: LEFT, DEFAULT: "X", VIA(field))
Use whatever order feels most readable for your team.
DEFAULT vs JOIN: INNER - Choosing the Right Strategy
These two approaches solve different problems. Choose based on your use case:
| Scenario | Use DEFAULT | Use JOIN: INNER |
|---|---|---|
| Display user-friendly data | ✓ | |
| Show all records with fallback | ✓ | |
| Calculate accurate aggregates | ✓ | |
| Exclude incomplete data | ✓ | |
| Keep record count intact | ✓ | |
| Data quality analysis | ✓ |
When to Use DEFAULT
- Reports and dashboards: Users expect to see all their data, even incomplete records
- Export/download features: Users want everything, with clear indicators for missing data
- Counting records: You need accurate record counts regardless of relationship status
When to Use JOIN: INNER
- Financial calculations: Only include records with verified related data
- Analysis accuracy: Exclude records that might skew results
- Data cleanup workflows: Find only records with complete relationships
Real-World Scenario: Customer Order Management
This scenario demonstrates practical patterns for an order management system where data completeness varies.
Business Context
- Some orders are walk-in sales (no customer account linked)
- Some customers haven't set their region preference
- Reports need to display meaningful data without confusing NULL values
- Financial reports need accurate figures from verified accounts only
Complete Order Report with DEFAULT
This query creates a user-friendly report where every field has a meaningful value:
ADDCOLUMNS(
[Orders],
"Customer", RELATED(
[Customers][name],
VIA(customer),
DEFAULT: "Walk-in Customer"
),
"Email", RELATED(
[Customers][email],
VIA(customer),
DEFAULT: ""
),
"Region", RELATED(
[Regions][name],
VIA(customer, region),
DEFAULT: "Unassigned"
),
"Regional Manager", RELATED(
[Regions][manager],
VIA(customer, region),
DEFAULT: "Contact HQ"
)
)
Result:
| Order | Customer | Region | Regional Manager |
|---|---|---|---|
| ORD001 | John Smith | North | Sarah Johnson |
| ORD002 | Walk-in Customer | Unassigned | Contact HQ |
| ORD003 | Jane Doe | Unassigned | Contact HQ |
Why this works:
- ORD001 has complete data, so RELATED returns actual values
- ORD002 has no customer link, so both customer and region default
- ORD003 has a customer but no region, so only region-related fields default
Revenue Analysis with INNER JOIN
For financial accuracy, only count revenue from verified customer accounts:
-- Total revenue from verified customer orders only
SUMX(
FILTER([Orders],
NOT(ISBLANK(RELATED([Customers][id], VIA(customer), JOIN: INNER)))
),
[amount]
)
Why use INNER here: Walk-in sales might have different processing rules, refund policies, or commission structures. Separating them ensures accurate reporting.
Comparing Both Approaches
-- All orders (for operations team)
"Total Orders", COUNTX([Orders], 1)
-- Orders with verified customers (for finance team)
"Verified Orders", COUNTX(
FILTER([Orders],
NOT(ISBLANK(RELATED([Customers][id], VIA(customer), JOIN: INNER)))
),
1
)
RELATEDTABLE with JOIN Options
JOIN options also work with RELATEDTABLE for one-to-many relationships:
-- All customers, even those without orders (LEFT JOIN - default)
ADDCOLUMNS(
[Customers],
"Order Count", COUNTX(RELATEDTABLE([Orders], VIA(customer)), 1)
)
-- Only customers who have placed orders (INNER JOIN)
ADDCOLUMNS(
FILTER([Customers],
COUNTX(RELATEDTABLE([Orders], VIA(customer), JOIN: INNER), 1) > 0
),
"Order Count", COUNTX(RELATEDTABLE([Orders], VIA(customer)), 1)
)
Best Practices
1. Choose Based on Use Case
| Use Case | Recommendation |
|---|---|
| User-facing reports | DEFAULT with meaningful values |
| Data analysis/metrics | JOIN: INNER for accuracy |
| Dashboards/visuals | DEFAULT for cleaner display |
| Data exports | DEFAULT with empty string "" |
| Troubleshooting | No DEFAULT (see actual NULLs) |
2. Be Consistent Within Reports
Use the same DEFAULT values throughout a report for the same type of missing data:
-- Good: Consistent DEFAULT handling across columns
ADDCOLUMNS([Orders],
"Customer", RELATED([Customers][name], VIA(customer), DEFAULT: "Unknown"),
"Region", RELATED([Regions][name], VIA(customer, region), DEFAULT: "Unknown")
)
-- Poor: Inconsistent defaults confuse users
ADDCOLUMNS([Orders],
"Customer", RELATED([Customers][name], VIA(customer), DEFAULT: "N/A"),
"Region", RELATED([Regions][name], VIA(customer, region), DEFAULT: "Unassigned")
)
3. Test with Edge Cases
Always verify your formulas handle these scenarios correctly:
- Records with completely missing relationships
- Empty related tables (no customers exist at all)
- Multi-hop paths where middle hops are missing (customer exists but has no region)
- Circular relationships (if your data model allows them)
4. Document Your Defaults
When using DEFAULT values, consider adding a legend or note to your report explaining what fallback values mean:
Note: "Walk-in Customer" indicates orders without a linked customer account.
Next Steps
- JOIN and DEFAULT Basics - Core syntax and simple examples
- RELATEDTABLE Function - Working with one-to-many relationships
- Cross-Table Operations - FILTER and SUMMARIZE with RELATED