Understanding GENERATE in DAX with Practical Examples

September 23, 2026 87 visits

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:

DAX Syntax
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.

Key takeaway:
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

DAX 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.

Key takeaway:
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

DAX 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

DAX Query
EVALUATE
    GENERATE(
        SELECTCOLUMNS(students, students[NAME]),
        payments
    )

Step-by-step explanation

Step 1: First table expression
The first argument is:

First Table
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:

Second Table
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.

Key takeaway:
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

DAX 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:

First Table
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:

Filtering Payments
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:

Selected Payment Column
payments[Paid]

Therefore, each student is combined only with that student’s matching payment records.

How GENERATE evaluates this query

  1. Take the first student.
  2. Read that student’s ID.
  3. Filter the payments table using that ID.
  4. Return the matching Paid values.
  5. Move to the next student.
  6. 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].

Key takeaway:
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
Important:
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

DAX 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
Key takeaway:
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:

  1. Return only students who have at least one payment.
  2. Return the student’s name along with every matching payment.
  3. 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