Skip to main content
cancel
Showing results for 
Search instead for 
Did you mean: 

Register now to learn Fabric in free live sessions led by the best Microsoft experts. From Apr 16 to May 9, in English and Spanish.

Reply
anandav
Skilled Sharer
Skilled Sharer

How to reference columns in virtual tables?

Hi,

 

I am creating a virtual table usingVAR. How can I refer to the columns of this newly created virtual table in the same table creation DAX?

New Table =
VAR JointTable = NATURALLEFTOUTERJOIN(SeatNumbers,SeatBookings)
VAR Test = 'JointTable'[SeatNum]*2
RETURN
JointTable

 

VAR Test is not working and the error message "Cannot find table 'JointTable'" is displayed.

 

Capture.JPG

 

Any help will be much appreciated.

1 ACCEPTED SOLUTION

Hi again @anandav

 

Thanks for the additional info.

From what you've provided, could I suggest a different approach, as doing the fill-down in DAX is possible but a little awkward.

 

I'm making an assumption that you are really interested in SeatNum and Booked Customer from your screenshot below. If so, I would propose this:

 

 

DAX Table = 
VAR SeatNumbersPlusBookings = 
    GENERATEALL (
        SeatNumbers,
        GENERATE (
            SeatBookings,
            INTERSECT (
                GENERATESERIES ( SeatBookings[Seat Start], SeatBookings[Seat End] ),
                { SeatNumbers[SeatNum] }
            )
        )
    )
VAR FinalTable =
    SELECTCOLUMNS (
        SeatNumbersPlusBookings,
        "SeatNum", SeatNumbers[SeatNum],
        "Booked Customer",
        VAR CurrentCustomer = SeatBookings[Customer]
        RETURN
            IF ( ISBLANK ( CurrentCustomer ), "Empty", CurrentCustomer )
    )
RETURN
    FinalTable

I'm using GENERATEALL to do the join rather than NATURALLEFTOUTERJOIN since your physical SeatBookings table don't have all the required values (since it contains Seat Start and Seat End), and GENERATE to convert the start/end values to a range.

 

 

You could of course include additional columns on top of the two output by the above.

 

Regards,

Owen


Owen Auger
Did I answer your question? Mark my post as a solution!
Blog
Twitter
LinkedIn

View solution in original post

9 REPLIES 9
OwenAuger
Super User
Super User

Hi @anandav

 

It looks like there are two problems here:

 

  1. You are trying to assign an expression to the variable Test that includes a 'naked' reference to the SeatNum column, without being in a row context. Were you trying to add a column to JointTable? If so you would need to write something like
    VAR Test = ADDCOLUMNS ( JointTable, "SeatNum Doubled", SeatNumbers[SeatNum]*2 )
    Note I changed the column reference in red, see point 2 below.
  2. When you create a variable and assign a table value to it, like JointTable, you cannot follow the naming convention used with physical tables and subsequently refer to columns of the variable table as VariableName[ColumnName]. Instead:
    • If the column originated from a physical table without any renaming, which generally means linage is maintained, you can refer to it by its original fully qualified column name OriginalTable[OriginalColumn].
    • If the column was added with the name ColumnName (such as with ADDCOLUMNS, SELECTCOLUMNS or SUMMARIZE) then refer to it as [ColumnName]
    • In your example, I am guessing that SeatNum column comes from the SeatNumbers table. If so, within JoinTable it can be referred to as SeatNumbers[SeatNum].
    • Note that for a reference to a column of a table variable to even make sense, you must either be writing an expression in a row context (such as within ADDCOLUMNS, SUMX, FILTER), or providing a column reference to a function that acts on tables (such as SUMMARIZE).

 

Could post back what final output you were looking for with New Table? It would help give you a precise answer on what you should do.

 

Regards,

Owen


Owen Auger
Did I answer your question? Mark my post as a solution!
Blog
Twitter
LinkedIn

Hi @OwenAuger,

 

Thanks a lot for the detail reply. That is a very clear explanation.

 

I was trying to create a table and fill down the missing values.

Presently I have done it in two steps.

1. Create the table.

DAX Table =
VAR JointTable = NATURALLEFTOUTERJOIN(SeatNumbers,SeatBookings)
RETURN
JointTable

 

2. Then fill down the missing value in a new column.

Customer Fill Down =
VAR LstNoBlankCustomer =
    CALCULATE (
        LASTNONBLANK ( 'DAX Table'[SeatNum], 1 ),
        FILTER (
            ALL ( 'DAX Table' ),
            'DAX Table'[SeatNum] <= EARLIER ( 'DAX Table'[SeatNum] )
                && NOT ( ISBLANK ( 'DAX Table'[Customer] ) )
        )
    )
RETURN
    CALCULATE (
        MAX ( 'DAX Table'[Customer] ),
        FILTER ( ALL ( 'DAX Table' ), 'DAX Table'[SeatNum] = LstNoBlankCustomer )
    )

 

Repeat the new column step for Seat Start and Seat End.

 

So the final table is :

Capture.JPG

 

What I was trying to achieve is instead of creating the virtual table and then adding columns to it do it in a single dax create table step.

Hi again @anandav

 

Thanks for the additional info.

From what you've provided, could I suggest a different approach, as doing the fill-down in DAX is possible but a little awkward.

 

I'm making an assumption that you are really interested in SeatNum and Booked Customer from your screenshot below. If so, I would propose this:

 

 

DAX Table = 
VAR SeatNumbersPlusBookings = 
    GENERATEALL (
        SeatNumbers,
        GENERATE (
            SeatBookings,
            INTERSECT (
                GENERATESERIES ( SeatBookings[Seat Start], SeatBookings[Seat End] ),
                { SeatNumbers[SeatNum] }
            )
        )
    )
VAR FinalTable =
    SELECTCOLUMNS (
        SeatNumbersPlusBookings,
        "SeatNum", SeatNumbers[SeatNum],
        "Booked Customer",
        VAR CurrentCustomer = SeatBookings[Customer]
        RETURN
            IF ( ISBLANK ( CurrentCustomer ), "Empty", CurrentCustomer )
    )
RETURN
    FinalTable

I'm using GENERATEALL to do the join rather than NATURALLEFTOUTERJOIN since your physical SeatBookings table don't have all the required values (since it contains Seat Start and Seat End), and GENERATE to convert the start/end values to a range.

 

 

You could of course include additional columns on top of the two output by the above.

 

Regards,

Owen


Owen Auger
Did I answer your question? Mark my post as a solution!
Blog
Twitter
LinkedIn

Hi @OwenAuger,

 

I was trying to understand the solution but ran into some problems. I tried to seperate each part of the DAX into VARs but it gives different results compared to using all in one DAX statement.

 

Table =
 VAR SeatsINBookedRange = GENERATESERIES ( MIN(SeatBookings[Seat Start]), MAX(SeatBookings[Seat End]) )
 VAR AllSeats = SeatNumbers
 VAR BookedAndEmptySeats = INTERSECT(SeatsINBookedRange, AllSeats)
 VAR Test1 = GENERATE(SeatBookings, BookedAndEmptySeats)
 VAR CustomerSeatBookings =
        GENERATE (
            SeatBookings,
            INTERSECT (
                GENERATESERIES ( SeatBookings[Seat Start], SeatBookings[Seat End] ),
                SeatNumbers
            )
        )
//VAR Test2 = GENERATEALL(SeatBookings, CustomerSeatBookings)
VAR SeatNumbersPlusBookings =
    GENERATEALL (
        SeatNumbers,
        GENERATE (
            SeatBookings,
            INTERSECT (
                GENERATESERIES ( SeatBookings[Seat Start], SeatBookings[Seat End] ),
                { SeatNumbers[SeatNum] }
            )
        )
    )
RETURN
SeatNumbersPlusBookings
 
Questions:
1.  VAR SeatsINBookedRange = GENERATESERIES ( MIN(SeatBookings[Seat Start]), MAX(SeatBookings[Seat End]) )
Thoug in the compsite DAX statement SeatBookings[Seat Start] can be referenced when in the VAR I had to use MIN and MAX functions).
Why is thsi?
 
2.  VAR AllSeats = SeatNumbers
Couldn't create a table with {} as it is not allowed.
"A single value for column 'SeatNum' in table 'SeatNumbers' cannot be determined. This can happen when a measure formula refers to a column that contains many values without specifying an aggregation such as min, max, count, or sum to get a single result."
 
3.  VAR BookedAndEmptySeats = INTERSECT(SeatsINBookedRange, AllSeats)
Returns the same table as in Step#1. Value from 1 to 19
 
4.   VAR Test1 = GENERATE(SeatBookings, BookedAndEmptySeats)
Return wrong results which is a cartisian product of tables.
Capture.JPG
 
 
5.  VAR CustomerSeatBookings
Combination of steps 1,2,3,4 in single DAX statement gives correct esults.
Capture.JPG
 
6. VAR Test2 = GENERATEALL(SeatBookings, CustomerSeatBookings) 
Gives an error "Function GENERATEALL does not allow two columns with the same name 'SeatBookings'[Customer]."
 
7.  VAR SeatNumbersPlusBookings
From your solution, gives the correct results.
Capture.JPG
If I change  { SeatNumbers[SeatNum] } to SeatNumbers table reference the results are wrong. What is  { SeatNumbers[SeatNum] } means?
Will you be able to help explain why the compsite DAX statement works and the individual statements don't?
Thanks in advance. 

Hi again @anandav

 

Thanks for the follow-up questions!

 

I may have to give you a more detailed answer later, but the general explanation is that the value returned by each expression is dependent on the context it is evaluated in. In particular, GENERATEALL and GENERATE  take the table supplied as the first argument, and evaluate the second argument (a table expression) in the row context of each row of the first argument.

 

When you evaluate the various expressions independently, you get strange results because they were intended to be evaluated within a particular (row) context. Also, in a row context, you can refer to the values of columns in the current row with a "naked" column reference, such as SeatBookings[Seat Start].

 

Diagram below:

image.png

 

 

Also the curly-braces syntax is a table constructor. In this case, { SeatNumbers[SeatNum] } creates a 1x1 table containing the SeatNum value from the current row of SeatNumbers. I am using this to filter the series of seat numbers created by GENERATESERIES. Looking at this again, I could just as well have used FILTER, as in something like FILTER ( GENERATESERIES(...), [Value] = SeatNumbers[SeatNum] )

 

Let me know if that helps - I will try to get back and reply on your specific questions later though.

 

Best regards,

Owen


Owen Auger
Did I answer your question? Mark my post as a solution!
Blog
Twitter
LinkedIn

Thanks a lot for the detail explanation Owen.

DAX gets confusing at times since some functions like clauclate we have to work from outer function to inner fucntion and others from inner to oueter (as I understand). But your diagram helps a lot!

 

Is it ok if I use your explanation in the blog I have done with credit to you?

I have added the solution with credit to you but also wanted to include this explanation iof it is ok with you.

Sure, no problem, feel free to use it 🙂
Glad it helped.
Oh I see you are also in Auckland, so may well run into you some time 😀

Owen Auger
Did I answer your question? Mark my post as a solution!
Blog
Twitter
LinkedIn

Thanks for the approval @OwenAuger.

Yes, I am in Auckland and have been to to your presentation in the Auckland Power BI forum. Man Very Happy

Hopefully we can catch up when you are there next time.

Hi @OwenAuger,

Really elegant solution. My solution will not be memory efficient for large tables. Your solution is really good.

Thanks a lot for taking time to help solve this.

 

PS. I realised I have a lot more to learn/understand on using DAX.

Helpful resources

Announcements
Microsoft Fabric Learn Together

Microsoft Fabric Learn Together

Covering the world! 9:00-10:30 AM Sydney, 4:00-5:30 PM CET (Paris/Berlin), 7:00-8:30 PM Mexico City

PBI_APRIL_CAROUSEL1

Power BI Monthly Update - April 2024

Check out the April 2024 Power BI update to learn about new features.

April Fabric Community Update

Fabric Community Update - April 2024

Find out what's new and trending in the Fabric Community.