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

Enhance your career with this limited time 50% discount on Fabric and Power BI exams. Ends September 15. Request your voucher.

Reply
Varan_15
Helper III
Helper III

row to column in power query

Hi All,

I have data like same worksteps (input, Pending) with entry and exit date time i want to convert into All the worksteps with entry - exit column level in power query.

 

Please refer below data and expected result.

Raw source table:

Process IDProcess datetimeWorkstepstatus
1212001Aug2024 15:35:52InputEntry
1212001Aug2024 15:43:39Inputexit
1212001Aug2024 15:43:39PendingEntry
1212005Aug2024 13:27:37pendingexit
1212005Aug2024 13:27:37InputEntry
1212005Aug2024 13:52:53Inputexit
1212005Aug2024 13:52:53PendingEntry
1212006Aug2024 14:05:55Pendingexit
1212006Aug2024 14:05:55InputEntry
1212006Aug2024 15:22:47Inputexit
1212006Aug2024 15:35:47PendingEntry

 

 

Expected result 1 :

process IDWorkstepEntryExit
12120Input 01Aug2024 15:35:5201Aug2024 15:43:39
12120Pending 01Aug2024 15:43:3905Aug2024 13:27:37
12120Input 05Aug2024 13:27:3705Aug2024 13:52:53
12120Pending 05Aug2024 13:52:5306Aug2024 14:05:55
12120Input 06Aug2024 14:05:5506Aug2024 15:22:47
12120Pending 06Aug2024 15:35:47Null

 

 

Or expected result 2:

process IDWorkstepEntryExit
12120Input 101Aug2024 15:35:5201Aug2024 15:43:39
12120Pending 101Aug2024 15:43:3905Aug2024 13:27:37
12120Input 205Aug2024 13:27:3705Aug2024 13:52:53
12120Pending 205Aug2024 13:52:5306Aug2024 14:05:55
12120Input 306Aug2024 14:05:5506Aug2024 15:22:47
12120Pending 306Aug2024 15:35:47Null

 

 Result 1 or result 2 anything is fine how to achive using in power query

 

Thanks in advance,

MS

 

2 ACCEPTED SOLUTIONS
Ashish_Mathur
Super User
Super User

Hi,

This M code works

let
    Source = Excel.CurrentWorkbook(){[Name="Data"]}[Content],
    #"Uppercased Text" = Table.TransformColumns(Source,{{"Workstep", Text.Upper, type text}}),
    #"Grouped Rows" = Table.Group(#"Uppercased Text", {"Process ID", "Workstep"}, {{"Count", each Table.AddIndexColumn(_,"Index",1,1)}},GroupKind.Local),
    #"Expanded Count" = Table.ExpandTableColumn(#"Grouped Rows", "Count", {"Process datetime", "status", "Index"}, {"Process datetime", "status", "Index"}),
    #"Added Index" = Table.AddIndexColumn(#"Expanded Count", "Order", 1, 1, Int64.Type),
    #"Grouped Rows1" = Table.Group(#"Added Index", {"Process ID", "Index"}, {{"Count", each Table.AddIndexColumn(_,"Index1",1,1)}}),
    #"Expanded Count1" = Table.ExpandTableColumn(#"Grouped Rows1", "Count", {"Workstep", "Process datetime", "status", "Order", "Index1"}, {"Workstep", "Process datetime", "status", "Order", "Index1"}),
    #"Sorted Rows" = Table.Sort(#"Expanded Count1",{{"Order", Order.Ascending}}),
    #"Removed Columns" = Table.RemoveColumns(#"Sorted Rows",{"Order", "Index"}),
    #"Changed Type" = Table.TransformColumnTypes(#"Removed Columns",{{"Process datetime", type datetime}}),
    #"Pivoted Column" = Table.Pivot(#"Changed Type", List.Distinct(#"Changed Type"[status]), "status", "Process datetime"),
    #"Removed Columns1" = Table.RemoveColumns(#"Pivoted Column",{"Index1"}),
    #"Sorted Rows1" = Table.Sort(#"Removed Columns1",{{"Process ID", Order.Ascending}, {"Entry", Order.Ascending}})
in
    #"Sorted Rows1"

Hope this helps.

Ashish_Mathur_0-1729316587766.png

 


Regards,
Ashish Mathur
http://www.ashishmathur.com
https://www.linkedin.com/in/excelenthusiasts/

View solution in original post

PavanLalwani
Resolver II
Resolver II

To transform the data from row to column in Power Query and achieve either **Result 1** or **Result 2**, follow these steps:

### Steps in Power Query:

1. **Sort the Data**: Ensure the data is sorted by `Process ID`, `Workstep`, and `Process datetime`.

2. **Group the Data**:
- Use **Group By** on `Process ID` and `Workstep`.
- Inside **Group By**, aggregate the **Entry** and **Exit** times:
- For **Entry**, choose **Min**.
- For **Exit**, choose **Max** (filter for "Exit" status).

3. **Conditional Column**: If needed, add a counter or index column to differentiate repeated worksteps for **Result 2**.

This will reshape the data as you need.

View solution in original post

16 REPLIES 16
MSuser5
Helper III
Helper III

Final:

End DateTime =
VAR StartDateTime = MAX(YourTable[StartDateTime]) -- The start date and time for the current row context
VAR HoursToAdd = [Additional Hours] -- Reference the dynamic measure for additional hours
VAR WorkingStartHour = 8
VAR WorkingEndHour = 16
VAR WorkingHoursPerDay = WorkingEndHour - WorkingStartHour

-- Filter the calendar table to get only future working days starting from the start date
VAR FilteredCalendar =
FILTER(
Calendar,
Calendar[Date] >= DATEVALUE(StartDateTime) &&
Calendar[WorkingDay] = 1
)

-- Calculate the number of complete working days required
VAR FullDaysRequired = INT(HoursToAdd / WorkingHoursPerDay)
VAR RemainingHours = MOD(HoursToAdd, WorkingHoursPerDay)

-- Find the date after adding the required number of working days
VAR EndDate =
MINX(
FILTER(
ADDCOLUMNS(
FilteredCalendar,
"CumulativeDays",
RANKX(
FilteredCalendar,
Calendar[Date],
,
ASC,
DENSE
)
),
[CumulativeDays] > FullDaysRequired
),
Calendar[Date]
)

// Start calculating the remaining hours if any
VAR InitialHour =
IF(
HOUR(StartDateTime) < WorkingStartHour,
WorkingStartHour, -- Set to 8 AM if before working hours
HOUR(StartDateTime) -- Use the current hour otherwise
)

VAR TotalEndHour = InitialHour + RemainingHours -- Add remaining hours to the initial hour

VAR AdjustedEndDate =
IF(
TotalEndHour > WorkingEndHour,
EndDate + 1, -- Move to the next working day if we exceed 4 PM
EndDate -- Stay on the current end date
)

VAR AdjustedEndHour =
IF(TotalEndHour > WorkingEndHour,
TotalEndHour - WorkingHoursPerDay, -- Subtract the working hours to move to the next working day
TotalEndHour -- Keep within the working hours
)

RETURN
AdjustedEndDate + TIME(AdjustedEndHour, 0, 0) -- Return the date with adjusted hours

MSuser5
Helper III
Helper III

Try 4:

End DateTime =
VAR StartDateTime = MAX(YourTable[StartDateTime]) -- The start date and time for the current row context
VAR HoursToAdd = [Additional Hours] -- Reference the dynamic measure for additional hours
VAR WorkingStartHour = 8
VAR WorkingEndHour = 16
VAR WorkingHoursPerDay = WorkingEndHour - WorkingStartHour

-- Filter the calendar table to get only future working days starting from the start date
VAR FilteredCalendar =
FILTER(
Calendar,
Calendar[Date] >= DATEVALUE(StartDateTime) &&
Calendar[WorkingDay] = 1
)

-- Calculate the number of complete working days required
VAR FullDaysRequired = INT(HoursToAdd / WorkingHoursPerDay)
VAR RemainingHours = MOD(HoursToAdd, WorkingHoursPerDay)

-- Find the date after adding the required number of working days
VAR EndDate =
MINX(
FILTER(
ADDCOLUMNS(
FilteredCalendar,
"CumulativeDays",
RANKX(
FilteredCalendar,
Calendar[Date],
,
ASC,
DENSE
)
),
[CumulativeDays] > FullDaysRequired
),
Calendar[Date]
)

-- Calculate the final time based on the remaining hours
VAR EndTime =
IF (
RemainingHours > 0,
// If there's remaining time, adjust based on the current time
VAR CurrentHour = HOUR(StartDateTime)
VAR CurrentMinute = MINUTE(StartDateTime)

// Check if the start time is before working hours
VAR EffectiveStartHour = IF(CurrentHour < WorkingStartHour, WorkingStartHour, CurrentHour)

// Determine the final hour within working hours
VAR FinalHour = MIN(EffectiveStartHour + RemainingHours, WorkingEndHour)

// If adding remaining hours exceeds working hours, set to working end hour
IF (FinalHour >= WorkingEndHour, WorkingEndHour, FinalHour) + TIME(0, 0, 0) // Reset minutes and seconds
)
ELSE
// If no remaining hours, maintain the start time
TIME(HOUR(StartDateTime), MINUTE(StartDateTime), SECOND(StartDateTime))

RETURN
EndDate + EndTime

MSuser5
Helper III
Helper III

Try 3:

End DateTime =
VAR StartDateTime = MAX(YourTable[StartDateTime]) -- The start date and time for the current row context
VAR HoursToAdd = [Additional Hours] -- Reference the dynamic measure for additional hours
VAR WorkingStartHour = 8
VAR WorkingEndHour = 16
VAR WorkingHoursPerDay = WorkingEndHour - WorkingStartHour

-- Filter the calendar table to get only future working days starting from the start date
VAR FilteredCalendar =
FILTER(
Calendar,
Calendar[Date] >= DATEVALUE(StartDateTime) &&
Calendar[WorkingDay] = 1
)

-- Calculate the number of complete working days required
VAR FullDaysRequired = INT(HoursToAdd / WorkingHoursPerDay)
VAR RemainingHours = MOD(HoursToAdd, WorkingHoursPerDay)

-- Find the date after adding the required number of working days
VAR EndDate =
MINX(
FILTER(
ADDCOLUMNS(
FilteredCalendar,
"CumulativeDays",
RANKX(
FilteredCalendar,
Calendar[Date],
,
ASC,
DENSE
)
),
[CumulativeDays] > FullDaysRequired
),
Calendar[Date]
)

-- Calculate the final time based on the remaining hours
VAR StartHour =
IF(
HOUR(StartDateTime) < WorkingStartHour,
WorkingStartHour,
MAX(HOUR(StartDateTime), WorkingStartHour)
)
VAR EndTime =
TIME(StartHour + RemainingHours, MINUTE(StartDateTime), SECOND(StartDateTime))

RETURN
EndDate + EndTime

MSuser5
Helper III
Helper III

Try 2

End DateTime =
VAR StartDateTime = MAX(YourTable[StartDateTime]) -- The start date and time for the current row context
VAR HoursToAdd = [Additional Hours] -- Reference the dynamic measure for additional hours
VAR WorkingStartHour = 8
VAR WorkingEndHour = 16
VAR WorkingHoursPerDay = WorkingEndHour - WorkingStartHour

-- Filter the calendar table to get only future working days starting from the start date
VAR FilteredCalendar =
FILTER(
Calendar,
Calendar[Date] >= DATEVALUE(StartDateTime) &&
Calendar[WorkingDay] = 1
)

-- Calculate how many full working days are required
VAR FullDaysRequired = INT(HoursToAdd / WorkingHoursPerDay)
VAR RemainingHours = MOD(HoursToAdd, WorkingHoursPerDay)

-- Identify the date when the required full days are used up
VAR EndDate =
MINX(
FILTER(
ADDCOLUMNS(
FilteredCalendar,
"CumulativeDays",
RANKX(
ALL(FilteredCalendar),
Calendar[Date],
,
ASC,
DENSE
)
),
[CumulativeDays] > FullDaysRequired
),
Calendar[Date]
)

-- Calculate the final time based on remaining hours
VAR EndTime =
IF(
HOUR(StartDateTime) < WorkingStartHour,
TIME(WorkingStartHour + RemainingHours, 0, 0),
TIME(HOUR(StartDateTime) + RemainingHours, MINUTE(StartDateTime), SECOND(StartDateTime))
)

RETURN
EndDate + EndTime

MSuser5
Helper III
Helper III

New:

EndDateTime =
VAR StartDateTime = YourTable[StartDateTime] -- The start date and time
VAR HoursToAdd = [Additional Hours] -- Measure for additional hours
VAR WorkingStartHour = 8
VAR WorkingEndHour = 16
VAR WorkingHoursPerDay = WorkingEndHour - WorkingStartHour

-- Filter the calendar table to get only future working days from the start date
VAR FilteredCalendar =
FILTER(
Calendar,
Calendar[Date] >= DATEVALUE(StartDateTime) &&
Calendar[WorkingDay] = 1
)

-- Calculate the number of complete days needed to accumulate the required working hours
VAR FullDaysRequired = INT(HoursToAdd / WorkingHoursPerDay)
VAR RemainingHours = MOD(HoursToAdd, WorkingHoursPerDay)

-- Find the date when all the full days have been used up
VAR EndDate =
MINX(
FILTER(
ADDCOLUMNS(
FilteredCalendar,
"CumulativeDays",
COUNTROWS(FILTER(FilteredCalendar, Calendar[Date] <= EARLIER(Calendar[Date])))
),
[CumulativeDays] > FullDaysRequired
),
Calendar[Date]
)

-- Calculate the final time based on remaining hours
VAR EndTime =
IF(
HOUR(StartDateTime) < WorkingStartHour,
TIME(WorkingStartHour + RemainingHours, 0, 0),
TIME(HOUR(StartDateTime) + RemainingHours, MINUTE(StartDateTime), SECOND(StartDateTime))
)

  • RETURN
    EndDate + EndTime
MSuser5
Helper III
Helper III

NewDateTime =
VAR StartDateTime = YourTable[DateTimeColumn]
VAR HoursToAdd = [Additional Hours] -- Reference to the dynamic measure
VAR WorkingStartHour = 8
VAR WorkingEndHour = 16
VAR WorkingHoursPerDay = WorkingEndHour - WorkingStartHour

-- Filter calendar to get only future working days from the start date
VAR FilteredCalendar =
FILTER(
Calendar,
Calendar[Date] >= DATEVALUE(StartDateTime) &&
Calendar[WorkingDay] = 1
)

-- Accumulate the working hours
VAR Result =
ADDCOLUMNS(
FilteredCalendar,
"AccumulatedHours",
VAR CurrentDay = Calendar[Date]
VAR StartTime = IF(CurrentDay = DATEVALUE(StartDateTime), MAX(WorkingStartHour, HOUR(StartDateTime)), WorkingStartHour)
VAR EndTime = WorkingEndHour
VAR AvailableHours = EndTime - StartTime
RETURN
MIN(HoursToAdd, AvailableHours)
)

-- Sum the accumulated hours to determine the end date and time
VAR TotalHoursAccumulated = SUMX(Result, [AccumulatedHours])
VAR AdditionalDays = INT(TotalHoursAccumulated / WorkingHoursPerDay)
VAR RemainingHours = MOD(TotalHoursAccumulated, WorkingHoursPerDay)

-- Calculate the final date and time
VAR FinalDate = MINX(FILTER(FilteredCalendar, Calendar[Date] >= DATEVALUE(StartDateTime)), Calendar[Date] + AdditionalDays)
VAR FinalTime = TIME(WorkingStartHour + RemainingHours, 0, 0)

RETURN
FinalDate + FinalTime

PavanLalwani
Resolver II
Resolver II

To transform the data from row to column in Power Query and achieve either **Result 1** or **Result 2**, follow these steps:

### Steps in Power Query:

1. **Sort the Data**: Ensure the data is sorted by `Process ID`, `Workstep`, and `Process datetime`.

2. **Group the Data**:
- Use **Group By** on `Process ID` and `Workstep`.
- Inside **Group By**, aggregate the **Entry** and **Exit** times:
- For **Entry**, choose **Min**.
- For **Exit**, choose **Max** (filter for "Exit" status).

3. **Conditional Column**: If needed, add a counter or index column to differentiate repeated worksteps for **Result 2**.

This will reshape the data as you need.

Ashish_Mathur
Super User
Super User

Hi,

This M code works

let
    Source = Excel.CurrentWorkbook(){[Name="Data"]}[Content],
    #"Uppercased Text" = Table.TransformColumns(Source,{{"Workstep", Text.Upper, type text}}),
    #"Grouped Rows" = Table.Group(#"Uppercased Text", {"Process ID", "Workstep"}, {{"Count", each Table.AddIndexColumn(_,"Index",1,1)}},GroupKind.Local),
    #"Expanded Count" = Table.ExpandTableColumn(#"Grouped Rows", "Count", {"Process datetime", "status", "Index"}, {"Process datetime", "status", "Index"}),
    #"Added Index" = Table.AddIndexColumn(#"Expanded Count", "Order", 1, 1, Int64.Type),
    #"Grouped Rows1" = Table.Group(#"Added Index", {"Process ID", "Index"}, {{"Count", each Table.AddIndexColumn(_,"Index1",1,1)}}),
    #"Expanded Count1" = Table.ExpandTableColumn(#"Grouped Rows1", "Count", {"Workstep", "Process datetime", "status", "Order", "Index1"}, {"Workstep", "Process datetime", "status", "Order", "Index1"}),
    #"Sorted Rows" = Table.Sort(#"Expanded Count1",{{"Order", Order.Ascending}}),
    #"Removed Columns" = Table.RemoveColumns(#"Sorted Rows",{"Order", "Index"}),
    #"Changed Type" = Table.TransformColumnTypes(#"Removed Columns",{{"Process datetime", type datetime}}),
    #"Pivoted Column" = Table.Pivot(#"Changed Type", List.Distinct(#"Changed Type"[status]), "status", "Process datetime"),
    #"Removed Columns1" = Table.RemoveColumns(#"Pivoted Column",{"Index1"}),
    #"Sorted Rows1" = Table.Sort(#"Removed Columns1",{{"Process ID", Order.Ascending}, {"Entry", Order.Ascending}})
in
    #"Sorted Rows1"

Hope this helps.

Ashish_Mathur_0-1729316587766.png

 


Regards,
Ashish Mathur
http://www.ashishmathur.com
https://www.linkedin.com/in/excelenthusiasts/

@Ashish_Mathur  @PavanLalwani 

 

Thanks for your reply it's working both methods as well.

 

Regards,

Varan

@Ashish_Mathur ,

 

Thanks for your update i have follow the same but getting error while using in SQL table as source

 

Expression.Error: There were too many elements in the enumeration to complete the operation.
Details:
[List]

 

I have noticed that were as Entry and Exit date& time both are same for both workstep then making this error kindly suggest how to avaoid incase of same time for all.

 

Thanks,

Hi,

As per the data that you shared, my code is working fine.  Share data with the row which are triggering that error.


Regards,
Ashish Mathur
http://www.ashishmathur.com
https://www.linkedin.com/in/excelenthusiasts/

@Ashish_Mathur ,

 

Thanks for your update, For example i have other scenario data which is same workstep and entry - exit time as well in that case getting error.

 

Process IDProcess datetimeWorkstepstatus
1212001/08/2024 15:35InputEntry
1212001/08/2024 15:43Inputexit
1212001/08/2024 15:43PendingEntry
1212005/08/2024 13:27pendingexit
1212005/08/2024 13:27InputEntry
1212005/08/2024 13:52Inputexit
1212005/08/2024 13:52PendingEntry
1212006/08/2024 14:05Pendingexit
1212006/08/2024 14:05InputEntry
1212006/08/2024 15:22Inputexit
1212006/08/2024 15:35Pendingexit
1212006/08/2024 14:05PendingEntry
1212006/08/2024 14:05InputEntry
1212006/08/2024 15:35Pendingexit

example raw table :

 

 

Expected Output:

Process IDWorkstepEntryExit
12120Input 01/08/2024 15:3501/08/2024 15:43
12120Pending 01/08/2024 15:4305/08/2024 13:27
12120Input 05/08/2024 13:2705/08/2024 13:52
12120Pending 05/08/2024 13:5206/08/2024 14:05
12120Input 06/08/2024 14:0506/08/2024 15:22
12120Input 06/08/2024 14:05Null
12120Pending 06/08/2024 14:0506/08/2024 15:35
12120Pending Null06/08/2024 15:35

 

but i'm getting error:

Expression.Error: There were too many elements in the enumeration to complete the operation.
Details:
[List]

 

kindly suggest  Thanks,

 

Hi,

My code works perfectly.  See image

Ashish_Mathur_0-1729466536438.png

 


Regards,
Ashish Mathur
http://www.ashishmathur.com
https://www.linkedin.com/in/excelenthusiasts/
Varan_15
Helper III
Helper III

please suggest how to achive usig power query  regarding above highlighted.

 

Thanks in advance

Kedar_Pande
Super User
Super User

@Varan_15 

M code:

let
Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
#"Changed Type" = Table.TransformColumnTypes(Source,{{"Process ID", type text}, {"Process datetime", type datetime}, {"Workstep", type text}, {"status", type text}}),
#"Sorted Rows" = Table.Sort(#"Changed Type",{{"Process ID", Order.Ascending}, {"Process datetime", Order.Ascending}}),
#"Filtered Rows" = Table.SelectRows(#"Sorted Rows", each ([status] = "Entry" or [status] = "exit")),
#"Grouped Rows" = Table.Group(#"Filtered Rows", {"Process ID", "Workstep"}, {{"All Data", each _, type table}}),
#"Expanded Data" = Table.ExpandTableColumn(#"Grouped Rows", "All Data", {"Process datetime", "status"}),
#"Added Exit Date" = Table.AddColumn(#"Expanded Data", "Exit", each if [status] = "Entry" then List.First(List.Select(#"Expanded Data"[Process datetime], (x) => x > [Process datetime] and [Workstep] = [Workstep])) else null)
in
#"Added Exit Date"


💌 If this helped, a Kudos 👍 or Solution mark would be great! 🎉
Cheers,
Kedar
Connect on LinkedIn

@Kedar_Pande ,

 

Thanks for your reply.

 

 

Anyhow as per your logic may need some similar changes based on data discrepancy as i have data like begins with exit time  those may have to exclude it.

 

example raw data:

Process IDProcess datetimeWorkstepstatus
1212001Aug2024 14:10:30PendingExit
1212001Aug2024 15:35:52InputEntry
1212001Aug2024 15:43:39Inputexit
1212001Aug2024 15:43:39PendingEntry
1212005Aug2024 13:27:37pendingexit
1212005Aug2024 13:27:37InputEntry
1212005Aug2024 13:52:53Inputexit
1212005Aug2024 13:52:53PendingEntry
1212006Aug2024 14:05:55Pendingexit
1212006Aug2024 14:05:55InputEntry
1212006Aug2024 15:22:47Inputexit
1212006Aug2024 15:35:47PendingEntry

 

 

So expected output:

process IDWorkstepEntryExit
12120Pending Null01Aug2024 14:10:30
12120Input 01Aug2024 15:35:5201Aug2024 15:43:39
12120Pending 01Aug2024 15:43:3905Aug2024 13:27:37
12120Input 05Aug2024 13:27:3705Aug2024 13:52:53
12120Pending 05Aug2024 13:52:5306Aug2024 14:05:55
12120Input 06Aug2024 14:05:5506Aug2024 15:22:47
12120Pending 06Aug2024 15:35:47Null

 

Kindly help to achive 

thanks,

Helpful resources

Announcements
August Power BI Update Carousel

Power BI Monthly Update - August 2025

Check out the August 2025 Power BI update to learn about new features.

August 2025 community update carousel

Fabric Community Update - August 2025

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