Saturday, November 15, 2025

How to display Maximum Time Value per Metric in a Power BI Column Chart

How to display Max Time Value of each Metric on a Column Chart in Power BI
Scenario:
Suppose we have a dataset where the Metric_Value column contains DateTime values for each Metric_Name, as shown below.


From this dataset, we need to extract the time component from Metric_Value and convert it into a numeric format so it can be used to display properly on a clustered column chart.

The refined Dataset with Time Values is as follows:
let
    SampleData = 
    #table(
        {"Metric_Id", "Metric_Name", "Metric_Value", "Metric_DataType"},
        {
            {1, "JobRunDateTime", #datetime(2023, 5, 10, 14, 30, 00), "DateTime"},
            {2, "JobRunDateTime", #datetime(2024, 1, 15, 21, 45, 30), "DateTime"},
            {3, "JobStartTime", #time(9, 45, 15), "Time"},
            {4, "JobStartTime", #time(14, 30, 10), "Time"}
        }
    ),

// Convert Time to duration:
_Duration = Table.AddColumn(
        SampleData,
        "TimeAsDuration",
        each #duration(
                0,
                Time.Hour([Metric_Value]),
                Time.Minute([Metric_Value]),
                Time.Second([Metric_Value])
            ),
        type duration
    ),

// Convert duration to total seconds (numeric):
_TotalSeconds = Table.AddColumn(
        _Duration,
        "TimeAsSeconds",
        each Duration.TotalSeconds(
            #duration(
                0,
                Time.Hour([Metric_Value]),
                Time.Minute([Metric_Value]),
                Time.Second([Metric_Value])
                )
            ),
        type number
    ),

// Convert Time into Hour.MinuteSeconds numeric format with padded Seconds:
_NumericTime = Table.AddColumn(
        _TotalSeconds,
        "TimeAsNumber",
        each Number.Round(
            Number.FromText(
            Text.From(Time.Hour([Metric_Value])) & "." &
            Text.PadStart(Text.From(Time.Minute([Metric_Value])),2,"0") &
            Text.PadStart(Text.From(Time.Second([Metric_Value])),2,"0")
            ), 
        4),
        type number
    )

in
    _NumericTime

The final formatted dataset includes a TimeAsNumber column, which represents the numeric conversion of time values. We will use this column to generate a clustered column chart that displays the maximum time value for each metric.

The TimeAsDuration (d.HH:mm:ss) and TimeAsSeconds columns were created for reference purposes only and are not used further in this example.


We will use the below measure to format the TimeAsNumber to Time (HH:mm:ss) to display on the Chart for the Data Labels.

Format TimeLabel =
VAR TimeValue = MAX(tbl_Metrics_TimeValues[TimeAsNumber])   -- E.g. 9.4515
VAR Hrs   = INT(TimeValue)                             -- Hours
VAR Mnts_Sec  = ROUND((TimeValue- Hrs) * 10000,0)      -- Minutes+Seconds
VAR Mnts   = INT(DIVIDE(Mnts_Sec,100))                 -- Minutes
VAR Sec   = MOD(Mnts,100)                              -- Seconds
RETURN
FORMAT(Hrs,"0") & ":" & FORMAT(Mnts,"00") & ":" & FORMAT(Sec,"00")

Generate the Clustered Column Chart:
We will use the Metric Name on the X-Axis and TimeAsNumber column on Y-axis to generate the Clustered Column chart with Max value for each Metric.


Since the purpose is not to display the Time as Number, hide the Y‑axis values, the Y‑axis title, and the tooltips.
Next, in the Data Labels > Values section of the chart formatting options, assign the measure Format TimeLabel Value (created earlier) so that the actual metric Time values are displayed on columns instead of the number.


We can use the below measure to format the Columns of the Chart.
Metric ColorFormat =
SWITCH(
    SELECTEDVALUE(tbl_Metrics_TimeValues[Metric_Name]),
    "JobRunDateTime",    "#1E90FF",    -- Azure Blue
    "JobStartTime",      "#4682B4",    -- SteelBlue
    "#808080"         -- Gray (default color)
    )

Result:
The final output is a clustered column chart with Metric_Name on the X‑axis and metric Max time values shown as data labels.


--------------------------------------------------------------------------------------------------------
Thanks
--------------------------------------------------------------------------------------------------------

How to display the Mixed Data Type Values of a Metric on a Column Chart in Power BI

How to display the Values of a Metric with Mixed Data Types on a Column Chart in Power BI
Scenario: Different Data Type values in the same Column
Suppose we have a dataset where the column Metric_Value contains values of different data types for each Metric_Name, as shown below.

let
    SampleData = 
    #table(
        {"Metric_Id", "Metric_Name", "Metric_Value", "Metric_DataType"},
        {
            {1, "ProductName", "Apple", "Text"}, {2, "ProductName", "Banana", "Text"},            
            {3, "UnitsSold", 150, "Number"}, {4, "UnitsSold", 275, "Number"},            
            {5, "GrowthRate", 0.12, "Percent"}, {6, "GrowthRate", 0.18, "Percent"},            
            {7, "OrderDate", #date(2023, 5, 10), "Date"}, 
            {8, "OrderDate", #date(2024, 1, 15), "Date"},            
            {9, "OrderDateTime", #datetime(2023, 5, 10, 14, 30, 00), "DateTime"},
            {10, "OrderDateTime", #datetime(2024, 1, 15, 9, 45, 30), "DateTime"},            
            {11, "OrderTime", #time(9, 45, 15), "Time"},
            {12, "OrderTime", #time(21, 30, 10), "Time"},            
            {13, "IsActive", true, "Logical"}, {14, "IsActive", false, "Logical"}
        }
    ),
    ChangedTypes = Table.TransformColumnTypes(
        SampleData,
        {
            {"Metric_Id", Int64.Type}, {"Metric_Name", type text},
            {"Metric_Value", type any}, {"Metric_DataType", type text}
        }
    )
in
    ChangedTypes


Displaying Max Value of each Metric on a Clustered Column Chart:
A clustered column chart in Power BI requires numeric or aggregated values on the Y‑axis in order to generate the visualization. When working with metrics that contain mixed data types, only those values that can be converted into numbers or aggregated meaningfully can be plotted on the chart.
In our scenario, the column Metric_Value contains values of different data types (such as Text, Date, and Time) that cannot be directly converted into numbers. However, these values can still be displayed on a clustered column chart as explained below.

Step1: Create a Measure to Calculate and Format Max Value of each Metric
The below measure will be used to calculate the Max Value of each Metric and format Values to the required display format:

Metric Max Value =
SWITCH(
    SELECTEDVALUE(tbl_Metrics_DataTypeMix[Metric_Name]),
    "ProductName", MAX(tbl_Metrics_DataTypeMix[Metric_Value]),
    "UnitsSold",
        MAXX(
            tbl_Metrics_DataTypeMix,
            VALUE(tbl_Metrics_DataTypeMix[Metric_Value])
        ),
   
    "GrowthRate",
        FORMAT(
            MAXX(
                tbl_Metrics_DataTypeMix,
                VALUE(tbl_Metrics_DataTypeMix[Metric_Value])
            ),
            "0.00%"
        ),
   
    "OrderDate",
        FORMAT(
            MAXX(
                tbl_Metrics_DataTypeMix,
                DATEVALUE(tbl_Metrics_DataTypeMix[Metric_Value])
            ),
            "dd-MM-yyyy"
        ),
   
    "OrderDateTime",
        FORMAT(
            MAXX(
                tbl_Metrics_DataTypeMix,
                VALUE(tbl_Metrics_DataTypeMix[Metric_Value])
            ),
            "dd-MM-yyyy HH:mm:ss"
        ),
   
    "OrderTime",
        FORMAT(
            MAXX(
                tbl_Metrics_DataTypeMix,
                TIMEVALUE(tbl_Metrics_DataTypeMix[Metric_Value])
            ),
            "HH:mm:ss"
        ),  

    "IsActive",
        FORMAT(
            MAXX(
                tbl_Metrics_DataTypeMix,
                SWITCH(
                    TRUE(),
                    tbl_Metrics_DataTypeMix[Metric_Value] = "true", 1,
                    tbl_Metrics_DataTypeMix[Metric_Value] = "false", 0,
                    BLANK()
                )
            ),
            "General Number"
        ),

    BLANK()
)

Step2: Create a Clustered Column Chart to show Max Value of each Metric
Create a clustered column chart with Metric_Name on the X‑axis and count of Metric_Name on the Y‑axis. 


Since the purpose is not to display the count, hide the Y‑axis values, the Y‑axis title, and the tooltips.
Next, in the Data Labels > Values section of the chart formatting options, assign the measure Metric Max Value (created earlier) so that the actual metric values are displayed on columns instead of the count.
 

We can use the below measure to format the Columns of the Chart.
Metric ColorFormat =
SWITCH(
    SELECTEDVALUE(tbl_Metrics_DataTypeMix[Metric_Name]),    

    "ProductName",    "#1E90FF",    -- Azure Blue
    "UnitsSold",      "#4682B4",    -- SteelBlue
    "GrowthRate",     "#3CB371",    -- MediumSeaGreen
    "OrderDate",      "#FF8C00",    -- DarkOrange
    "OrderDateTime",  "#6A5ACD",    -- SlateBlue
    "OrderTime",      "#008080",    -- Teal
    "IsActive",       "#228B22",    -- ForestGreen
    "#808080"         -- Gray (default color)
)

Result:
The final output is a clustered column chart with Metric_Name on the X‑axis and metric Max values shown as data labels. The Y‑axis values, title, and tooltips are hidden so only the relevant metric values are visible.


Notes: 
For text columns, the maximum values will be determined based on alphabetical order. This means the chart will display the last value in sort sequence rather than a numeric maximum.

--------------------------------------------------------------------------------------------------------
Thanks
--------------------------------------------------------------------------------------------------------

Sunday, November 2, 2025

How to Analyze Data Pipeline Run Statistics Using DAX in Power BI

How to Analyze Data Pipeline Run Statistics Using DAX and M-Query
In modern data systems, pipelines are essential for automating the flow of information across platforms and regions. They often run multiple times a day with mixed results, some successful, others failing. To monitor performance effectively, especially to catch recent data load failures, it's important to isolate the latest run for each pipeline. Without this focus, historical logs can become overwhelming and difficult to interpret.

This article explores how to solve that challenge using Power BI. By combining Power Query and DAX, you can flag the latest run, calculate run durations, and extract key metrics like run status and data load success. This approach helps build a clear, dynamic view of pipeline health for better monitoring and decision-making.

Scenario:
Suppose we have a dataset that captures the execution history of data pipelines across various regions. Each record in the dataset represents a single pipeline run and includes columns such as:
  • Pipeline_ID: Unique identifier for each pipeline
  • Pipeline_Name: Descriptive name of the pipeline
  • Pipeline_Region: Deployment region (e.g., US-East, EU-Central)
  • Pipeline_Run_Status: Outcome of the run (Success or Fail)
  • Data_Load_Status: Whether the data load was successful or not.
  • Pipeline_Run_StartDate: Timestamp when the Pipeline run started.
  • Pipeline_Run_EndDate: Timestamp when the Pipeline run completed.

Now we will use this Pipeline Run log data to analyze it further by identifying the latest run for each pipeline, calculating how long each run took, and evaluating whether the data load was successful or not.

Pipeline_Run_Hours:  We can create this Column in Power Query to calculate the Time (in Hours) taken by each Pipeline to run.

let
    _start = [Pipeline_Run_StartDate],
    _end = [Pipeline_Run_EndDate],
    _duration = _end - _start,
    _days = Duration.Days(_duration),
    _hours = Duration.Hours(_duration),
    _minutes = Duration.Minutes(_duration),
    _totalMinutes = (_days * 1440) + (_hours * 60) + _minutes,
    _totalHours = _totalMinutes / 60
in
    _totalHours
--------------------------------------------------------------------------------------------------------

The complete Power Query logic of the above Sample data (Changes dynamically based on the current date time) is as follows:

let
    _today = DateTime.LocalNow(),

// Create pipeline run stats data sample
_pipelineData = #table(
        {
            "Pipeline_ID",    
            "Pipeline_Name",
            "Pipeline_Region",
            "Pipeline_Run_Status",
            "Data_Load_Status",
            "Pipeline_Run_StartDate",
            "Pipeline_Run_EndDate"
        },
        {
            // Alpha pipeline run stats with latest run as Success
            {123, "Alpha", "US-East", "Success", "Success", _today - #duration(29, 6, 45, 0), _today - #duration(28, 8, 30, 0)},
            {123, "Alpha", "US-East", "Success", "Fail",    _today - #duration(25, 7, 15, 0), _today - #duration(24, 8, 45, 0)},
            {123, "Alpha", "US-East", "Fail",    "Fail",    _today - #duration(20, 8, 0, 0),  _today - #duration(19, 10, 0, 0)},
            {123, "Alpha", "US-East", "Success", "Success", _today - #duration(1, 9, 0, 0),   _today - #duration(0, 8, 15, 0)},

            // Beta pipeline run stats with latest run as Fail
            {234, "Beta",  "US-West", "Success", "Success", _today - #duration(28, 6, 30, 0), _today - #duration(27, 9, 15, 0)},
            {234, "Beta",  "US-West", "Success", "Fail",    _today - #duration(22, 7, 45, 0), _today - #duration(21, 9, 30, 0)},
            {234, "Beta",  "US-West", "Success", "Fail",    _today - #duration(1, 8, 30, 0),  _today - #duration(0, 9, 0, 0)},

            // Gamma pipeline run stats with latest run as Fail
            {345, "Gamma", "EU-Central", "Success", "Success", _today - #duration(27, 9, 15, 0), _today - #duration(26, 10, 0, 0)},
            {345, "Gamma", "EU-Central", "Success", "Fail",    _today - #duration(21, 8, 0, 0),  _today - #duration(20, 10, 30, 0)},
            {345, "Gamma", "EU-Central", "Success", "Fail",    _today - #duration(1, 7, 0, 0),   _today - #duration(0, 9, 45, 0)},

            // Delta pipeline run stats with latest run as Success
            {456, "Delta", "Asia-Pacific", "Success", "Success", _today - #duration(30, 6, 30, 0), _today - #duration(29, 7, 45, 0)},
            {456, "Delta", "Asia-Pacific", "Success", "Fail",    _today - #duration(18, 7, 30, 0), _today - #duration(17, 8, 0, 0)},
            {456, "Delta", "Asia-Pacific", "Success", "Success", _today - #duration(2, 8, 15, 0),  _today - #duration(1, 7, 30, 0)},

            // Omega pipeline run stats with latest run as Success
            {567, "Omega", "India", "Success", "Success", _today - #duration(26, 9, 30, 0), _today - #duration(25, 10, 15, 0)},
            {567, "Omega", "India", "Fail",    "Fail",    _today - #duration(15, 8, 30, 0), _today - #duration(14, 10, 30, 0)},
            {567, "Omega", "India", "Success", "Success", _today - #duration(5, 7, 45, 0),  _today - #duration(4, 10, 0, 0)}
        }
    ),

// Add Is_Pipeline_Latest_Run
_addLatestFlag = Table.AddColumn(
        _pipelineData,
        "Is_Pipeline_Latest_Run",
        each 
            let
                _currentPipeline = [Pipeline_ID],
                _currentEndDate = [Pipeline_Run_EndDate],
                _maxEndDate = List.Max(
                    Table.SelectRows(_pipelineData, each [Pipeline_ID] = _currentPipeline)[Pipeline_Run_EndDate]
                )
            in
                _currentEndDate = _maxEndDate,
        type logical
    ),

// Change types
_changeType = Table.TransformColumnTypes(
        _addLatestFlag,
        {
            {"Pipeline_ID", Int64.Type},
            {"Pipeline_Name", type text},
            {"Pipeline_Region", type text},
            {"Pipeline_Run_Status", type text},
            {"Data_Load_Status", type text},
            {"Pipeline_Run_StartDate", type datetime},
            {"Pipeline_Run_EndDate", type datetime},
            {"Is_Pipeline_Latest_Run", type logical}
        }
    ),

// Add Pipeline_Run_Hours
_addRunHours = Table.AddColumn(
        _changeType,
        "Pipeline_Run_Hours",
        each 
            let
                _start = [Pipeline_Run_StartDate],
                _end = [Pipeline_Run_EndDate],
                _duration = _end - _start,
                _days = Duration.Days(_duration),
                _hours = Duration.Hours(_duration),
                _minutes = Duration.Minutes(_duration),
                _totalMinutes = (_days * 1440) + (_hours * 60) + _minutes,
                _totalHours = _totalMinutes / 60
            in
                _totalHours,
        type number
    ),

// Add Pipeline Run Day of Week
_addDayOfWeek = Table.AddColumn(
        _addRunHours,
        "Pipeline_Run_DayOfWeek",
        each Date.DayOfWeekName([Pipeline_Run_StartDate]),
        type text
    ),

// Add Pipeline Duration Category
_addDurationCategory = Table.AddColumn(
        _addDayOfWeek,
        "Pipeline_Run_Duration_Category",
        each 
            let h = [Pipeline_Run_Hours]
            in 
                if h < 2 then "Short" 
                else if h < 6 then "Medium" 
                else "Long",
        type text
    )
in
    _addDurationCategory
--------------------------------------------------------------------------------------------------------

We define the following calculated Column in Power BI which will return TRUE for the latest Run date of each Pipeline (either Success or Fail), otherwise it returns FALSE.

Is_Latest_Pipeline_Run =
VAR CurrentPipeline = tbl_Pipeline_Run_Stats[Pipeline_Id]
VAR CurrentEndDate = tbl_Pipeline_Run_Stats[Pipeline_Run_EndDate]
VAR MaxEndDate =
    CALCULATE(
        MAX(tbl_Pipeline_Run_Stats[Pipeline_Run_EndDate]),
        FILTER(tbl_Pipeline_Run_Stats, tbl_Pipeline_Run_Stats[Pipeline_Id] = CurrentPipeline)
    )
RETURN
    CurrentEndDate = MaxEndDate
--------------------------------------------------------------------------------------------------------
Next, we can define the following set of Measures needed for the Analysis:

Pipeline Run Hours = SUM(tbl_Pipeline_Run_Stats[Pipeline_Run_Hours])

Pipeline Latest Run Hours =
VAR _LatestRunEndDate =
    CALCULATE(
        MAX(tbl_Pipeline_Run_Stats[Pipeline_Run_EndDate]),
        ALLEXCEPT(tbl_Pipeline_Run_Stats, tbl_Pipeline_Run_Stats[Pipeline_Id])
    )
RETURN
    CALCULATE(
        SUM(tbl_Pipeline_Run_Stats[Pipeline_Run_Hours]),
        tbl_Pipeline_Run_Stats[Pipeline_Run_EndDate] = _LatestRunEndDate
    )

Pipeline Latest Run Date =
VAR _CurrentPipeline = MAX(tbl_Pipeline_Run_Stats[Pipeline_Id])
VAR _CurRunEndDate = MAX(tbl_Pipeline_Run_Stats[Pipeline_Run_EndDate])
VAR _LatestRunEndDate =
    CALCULATE(
        MAX(tbl_Pipeline_Run_Stats[Pipeline_Run_EndDate]),
        ALLEXCEPT(tbl_Pipeline_Run_Stats, tbl_Pipeline_Run_Stats[Pipeline_Id])
    )
RETURN
    IF(_CurRunEndDate = _LatestRunEndDate, _LatestRunEndDate)

Latest Pipeline Run Status =
VAR _CurrentPipeline = MAX(tbl_Pipeline_Run_Stats[Pipeline_Id])
VAR _LatestRunEndDate =
    CALCULATE(
        MAX(tbl_Pipeline_Run_Stats[Pipeline_Run_EndDate]),
        ALLEXCEPT(tbl_Pipeline_Run_Stats, tbl_Pipeline_Run_Stats[Pipeline_Id])
    )
RETURN
    CALCULATE(
        MAX(tbl_Pipeline_Run_Stats[Pipeline_Run_Status]),
        tbl_Pipeline_Run_Stats[Pipeline_Id] = _CurrentPipeline &&
        tbl_Pipeline_Run_Stats[Pipeline_Run_EndDate] = _LatestRunEndDate
    )

Latest Data Load Status =
VAR _CurrentPipeline = MAX(tbl_Pipeline_Run_Stats[Pipeline_Id])
VAR _LatestRunEndDate =
    CALCULATE(
        MAX(tbl_Pipeline_Run_Stats[Pipeline_Run_EndDate]),
        ALLEXCEPT(tbl_Pipeline_Run_Stats, tbl_Pipeline_Run_Stats[Pipeline_Id])
    )
RETURN
    CALCULATE(
        MAX(tbl_Pipeline_Run_Stats[Data_Load_Status]),
        tbl_Pipeline_Run_Stats[Pipeline_Id] = _CurrentPipeline &&
        tbl_Pipeline_Run_Stats[Pipeline_Run_EndDate] = _LatestRunEndDate
    )

The following measure is based on the column Pipeline_Run_EndDate to identify the latest run Date of the Pipeline. It returns 1 for latest run date and 0 for others.
This measure logic and objective is similar to the calculated column Is_Latest_Pipeline_Run created in the beginning.
However, this will not carry forward in Drill through filters like Is_Latest_Pipeline_Run.

Pipeline Latest Run Flag =
VAR _CurRunEndDate = MAX(tbl_Pipeline_Run_Stats[Pipeline_Run_EndDate])
VAR _LatestRunEndDate =
    CALCULATE(
        MAX(tbl_Pipeline_Run_Stats[Pipeline_Run_EndDate]),
        ALLEXCEPT(tbl_Pipeline_Run_Stats, tbl_Pipeline_Run_Stats[Pipeline_Id])
    )
RETURN
    IF(_CurRunEndDate = _LatestRunEndDate, 1, 0)

Results:
Now we can generate the Pipeline Latest Run Summary table using the above measures as shown below:
You can apply required conditional formatting for the Latest Data Load Status if needed.


The Detailed table view of the Pipeline and Data Load Status details as per below:


Please Note:
To establish a relationship between the Pipeline Run Statistics table and the Calendar table based on date, please convert either Pipeline_Run_StartDate or Pipeline_Run_EndDate to Date format. 
Power BI does not support relationships between columns of different data types, specifically, between Date and DateTime columns.
--------------------------------------------------------------------------------------------------------
Drill through from Summary Page to Details Page:
Let's say, you have Summary table in the Power BI page "Pipeline_Run_Summary" and the details table view in the other Page "Pipeline_Run_Details".

Now if you want to Drill through from Summary page to Details page, then make sure there must be one common Column (Eg. Piplie_Id, Pipeline_Region) selected in both Visuals.

Next add the required common Fields in the Drill Through targe page (Pipeline_Run_Details), and add the additional columns if needed as shown below:


Next go back to the Summary page and try drill though from a Record from the Visual:


Now the result of the drill through in Details page is as shown below, will the all the Filters apply from Summary to Details page, except Latest Run Date filter is not passed:



Why Drill Through is not Filtering the Latest Dates in Detail view:
This issue arises as the measures (Eg. Pipeline Latest Run Date) used in the Summary view are calculating the values based on internal logic that determines the latest run date for each pipeline using DAX. 
  • Implicit filters inside measures (like MAX, CALCULATE, or ALLEXCEPT) are local to the measure and don’t become part of the drillthrough filter context.
  • Drillthrough only carries filters from explicit fields used in the visual or slicer like actual columns or selected values.
  • Measures don’t expose their internal logic to the drillthrough engine.
So, when you drill through from a visual that displays the latest run date, Power BI doesn’t automatically pass that date as a filter. It’s simply the output of a measure, not a selected or filtered field. To enable drill through filtering, you need to use actual columns or calculated columns that are part of the model and can participate in the filter context.

Enable Drill Through to pass Latest Date Filter from Summary to Detail view:
To enable filtering by the latest Pipeline Run Date during drill-through, we can apply a Visual Level or Page Level filter using the Is_Latest_Pipeline_Run column in the Summary View. This ensures that only the most recent run for each pipeline is passed to the details page during drill-through.

Output:
Now, the drill-through results on the Details page will correctly reflect the latest pipeline run date filter, as applied using the Is_Latest_Pipeline_Run column.



--------------------------------------------------------------------------------------------------------
Thanks
--------------------------------------------------------------------------------------------------------

Featured Post from this Blog

How to compare Current Snapshot Data with Previous Snapshot in Power BI

How to Dynamically compare two Snapshots Data in Power BI Scenario: Suppose we have a sample Sales data, which is stored with Monthly Snapsh...

Popular Posts from this Blog