Understanding GENERATE in DAX with Practical Examples
In this article, we will learn how to use GENERATE in DAX to combine two table expressions and how it differs from a simple CROSS JOIN-style combination.
We will also see how GENERATE can use values from the first table to filter the second table for each individual row.
📥 Practice Data:
Download Practice Data Free
🎥 Prefer learning through video?
DAX YouTube Playlist
What Is GENERATE in DAX?
GENERATE combines two table expressions by evaluating the second table expression for each row of the first table.
This is the key idea to understand when working with GENERATE.
The basic syntax is:
GENERATE(
<First Table>,
<Second Table Expression>
)
The second table expression can access the current row of the first table. This makes GENERATE particularly useful when the second table needs to be dynamically filtered based on values from the first table.
Think of GENERATE as: “For every row in the first table, evaluate the second table expression and append its rows.”
1. Retrieve the Student Names
Before understanding GENERATE, the query first retrieves the student names from the students table.
Query
EVALUATE
SELECTCOLUMNS(students, students[NAME])
Step-by-step explanation
Step 1: EVALUATE
EVALUATE returns the result of a table expression.
Step 2: SELECTCOLUMNS
SELECTCOLUMNS creates a new table containing the column specified from the source table.
Step 3: students[NAME]
This is the student name column that we want to return.
What will you get?
The result is a single-column table containing the names from the students table.
| NAME |
|---|
| Arun |
| Priya |
| Rahul |
The names above are only for illustration. Your actual output depends on the data in your students table.
This creates the first table that will be supplied to GENERATE.
2. Retrieve the Payments Table
The next query simply returns the payments table.
Query
EVALUATE
payments
Step-by-step explanation
Step 1: EVALUATE
DAX returns the table expression provided after EVALUATE.
Step 2: payments
The complete payments table is used as the table expression.
What will you get?
You will get all rows and columns available in the payments table.
| Student | Paid |
|---|---|
| 1 | 500 |
| 1 | 750 |
| 2 | 1000 |
The example values are illustrative and may differ from your practice data.
3. GENERATE Used Like a CROSS JOIN
Now we can combine the student names with the complete payments table using GENERATE.
Query
EVALUATE
GENERATE(
SELECTCOLUMNS(students, students[NAME]),
payments
)
Step-by-step explanation
Step 1: First table expression
The first argument is:
SELECTCOLUMNS(students, students[NAME])
This produces the list of student names. GENERATE then processes this table one row at a time.
Step 2: Second table expression
The second argument is:
payments
Because the second expression is simply the complete payments table and does not use the current student row, the same payment rows are generated for every student.
This produces a result that behaves like a CROSS JOIN: every row from the first table is combined with every row from the second table.
Conceptual example
| NAME | Student | Paid |
|---|---|---|
| Arun | 1 | 500 |
| Arun | 2 | 1000 |
| Priya | 1 | 500 |
| Priya | 2 | 1000 |
The example above demonstrates the combination concept. The exact number of rows depends on the number of rows in each table.
When the second table expression does not depend on the current row of the first table, GENERATE can produce a result similar to a Cartesian or CROSS JOIN combination.
4. GENERATE with Filtering Based on the First Table
The real power of GENERATE becomes clearer when the second table expression uses a value from the current row of the first table.
Query
EVALUATE
GENERATE(
SELECTCOLUMNS(students, students[ID], students[NAME]),
SELECTCOLUMNS(
FILTER(payments, payments[Student] = students[ID]),
payments[Paid]
)
)
Step-by-step explanation
Step 1: Create the first table
The first argument is:
SELECTCOLUMNS(students, students[ID], students[NAME])
This creates a table containing the student’s ID and NAME.
| ID | NAME |
|---|---|
| 1 | Arun |
| 2 | Priya |
Step 2: GENERATE processes each student row
GENERATE evaluates the second table expression separately for each row of this first table.
For the current student row, the value of students[ID] is available to the second table expression.
Step 3: FILTER the payments table
The second table expression contains:
FILTER(
payments,
payments[Student] = students[ID]
)
The FILTER function checks every payment and keeps only the rows where payments[Student] matches the ID of the current student.
Step 4: SELECTCOLUMNS returns Paid
After filtering the payments, SELECTCOLUMNS returns only:
payments[Paid]
Therefore, each student is combined only with that student’s matching payment records.
How GENERATE evaluates this query
- Take the first student.
- Read that student’s ID.
- Filter the payments table using that ID.
- Return the matching Paid values.
- Move to the next student.
- Repeat the same process.
Conceptual result
| ID | NAME | Paid |
|---|---|---|
| 1 | Arun | 500 |
| 1 | Arun | 750 |
| 2 | Priya | 1000 |
The result above is only an illustration of the logic. Your actual output depends on the relationships between the values in students[ID] and payments[Student].
The second table expression can reference the current row from the first table. This allows GENERATE to create a row-by-row dynamic combination instead of blindly combining every row with every other row.
5. Understanding the Difference Between the Two GENERATE Examples
| GENERATE Pattern | How It Works |
|---|---|
| GENERATE(Students, payments) | The complete payments table is generated for every student, producing a CROSS JOIN-like result. |
| GENERATE(Students, FILTER(payments, …)) | The payments table is filtered separately for each student using the current student’s ID. |
6. GENERATE vs CROSS JOIN Concept
Both patterns can produce combinations of rows, but the important difference is how the second table expression is evaluated.
| Feature | GENERATE | CROSS JOIN |
|---|---|---|
| Combines rows from two tables | Yes | Yes |
| Second expression evaluated for each first-table row | Yes | No |
| Can reference the current row of the first table | Yes | Not in the same row-context manner |
| Useful for row-dependent filtering | Yes | Not the primary purpose |
GENERATE is more than just a CROSS JOIN replacement. Its important feature is that the second table expression is evaluated in the context of each row of the first table.
7. Removing Duplicate Student Names with DISTINCT
The final query uses DISTINCT to return only unique student names.
Query
EVALUATE
DISTINCT(students[NAME])
Step-by-step explanation
Step 1: DISTINCT
DISTINCT removes duplicate values from the supplied column or table expression.
Step 2: students[NAME]
Here, the expression supplies the student name column.
The result contains each unique student name only once.
| NAME |
|---|
| Arun |
| Priya |
| Rahul |
DISTINCT(students[NAME]) is useful when you need a unique list of student names before performing further table operations.
📝 Practice Exercise
Try modifying the GENERATE query so that it returns the student’s ID, NAME, and only the payment records belonging to that student.
Then try these variations:
- Return only students who have at least one payment.
- Return the student’s name along with every matching payment.
- Use DISTINCT to create a unique list of students before generating payment rows.
Conclusion
In this article, we learned how GENERATE works in DAX and why it is useful when one table expression needs to be evaluated for every row of another table.
- GENERATE combines the rows produced by two table expressions.
- The second table expression is evaluated for each row of the first table.
- When the second expression is independent of the first table, the result can behave like a CROSS JOIN.
- When the second expression references the current row of the first table, GENERATE can create a row-dependent result.
- FILTER can be used inside GENERATE to select matching rows from the second table.
- DISTINCT can be used to create a unique list before further table operations.
The most important concept to remember is simple: GENERATE takes each row from the first table and evaluates the second table expression for that row.
📥 Practice Data:
Download Practice Data Free
🎥 Prefer learning through video?
DAX YouTube Playlist