I am working with some data from Jira. In Jira there are "Issues" and each issue can go through states such as New, In Progress, Review, etc. I want to measure the time something stays in each state. It is possible for things to move back and forth and return to a state multiple times. Jira produces a log table which logs the event of the issue moving from one state to another. To measure how long it’s been in a state you need to have the entry from the log for that state (end) and the previous change of state (start).
I can’t return one entry for each state change. For Issues that moved between a state multiple times I get multiple rows.
I tried the gaps and islands approach. Also a select within the top select. Min or Max in the join was atrociously slow.
The desired result would be a column added to the table in the select which gives the duration for the State in the column ItemFromString. The date difference is between this entry’s Created date and the previous state change entry’s created date, which shows when the issue moved to this state. In the example data below the first entry for Assessment, History ID 436260, would be a duration of 9/19–9/14. When I join I get multiple entries for this History ID since there are multiple Assessments. I filter the join by Issue key and Item from/to String where they match; however, I need to also add a filter where it looks at any entries created before the current items created date and selects the most recent, or largest, one. This is where I am hung up.
Fields:
- Created - This is when the log entry was created which is the date time it changed state from ItemFromString to ItemToString.
- IssueCreated - This is when the issue the log is about, was created. For example, they start in the new state so we need this date to figure out how long it sat in new as the first log entry will be it moving from New to something else.
- IssueKey and IssueID are almost the same thing, they are key ID's for the issue in a different table.
- HistoryID is the key for each log entry in this table.
Assessment
IssueKey | HistoryID | IssueId | Created | IssueCreatedDate | ItemFromString | ItemToString |
---|---|---|---|---|---|---|
TPP-16 | 434905 | 208965 | 9/14/2022 14:33 | 9/14/2022 8:56 | New | Assessment |
TPP-16 | 436260 | 208965 | 9/19/2022 8:32 | 9/14/2022 8:56 | Assessment | Internal Review |
TPP-16 | 437795 | 208965 | 9/19/2022 16:11 | 9/14/2022 8:56 | Internal Review | New |
TPP-16 | 437796 | 208965 | 9/19/2022 16:11 | 9/14/2022 8:56 | New | Assessment |
TPP-16 | 439006 | 208965 | 9/20/2022 15:08 | 9/14/2022 8:56 | Assessment | New |
TPP-16 | 457786 | 208965 | 10/17/2022 11:02 | 9/14/2022 8:56 | New | Assessment |
TPP-16 | 457789 | 208965 | 10/17/2022 11:03 | 9/14/2022 8:56 | Assessment | Internal Review |
TPP-16 | 490205 | 208965 | 10/27/2022 15:15 | 9/14/2022 8:56 | Internal Review | On Hold |
TPP-16 | 539391 | 208965 | 1/11/2023 15:24 | 9/14/2022 8:56 | On Hold | Backlog |
This query does not get a duration as the last column in the query. The query creates a table that is then published and utilized by BI products for graphing and analysis.
SELECT
IssueChangelogs.IssueKey IssueKey,
IssueChangelogs.HistoryId HistoryId,
IssueChangelogs.IssueId IssueId,
IssueChangelogs.IssueCreatedDate IssueCreatedDate,
IssueChangelogs.ItemFromString ItemFromString,
IssueChangelogs.ItemToString ItemToString,
ICLPrev.Created PrevCreated, --For Testing
IssueChangelogs.Created Created,
ICLPrev.HistoryID PrevHistoryID, --For Testing
CASE
-- If the join found a match for a previous status, then we can calculate the Duration it was in that state.
WHEN ICLPrev.HistoryID IS NOT NULL
THEN DATEDIFF(hour, ICLPrev.Created, IssueChangeLogs.Created)/24
-- If the state was new then we need to use the IssueCreatedDate as the start date as the default state is New for each issue.
WHEN IssueChangeLogs.ItemFromString LIKE '%New%'
THEN Round(DATEDIFF(hour, IssueChangeLogs.IssueCreatedDate, IssueChangeLogs.Created), 2)/24
-- Else, let's add something easy to identify so when we test and look at the table we know what occured.
ELSE 0.01
END AS Duration
FROM
TableNameRedacted AS IssueChangelogs
LEFT JOIN
TableNameRedacted AS ICLPrev
ON ICLPrev.IssueKey = IssueChangeLogs.IssueKey AND
ICLPrev.ItemToString = IssueChangeLogs.ItemFromString
WHERE
IssueChangelogs.IssueKey LIKE '%TPP%'