March 31 - April 2, 2025, in Las Vegas, Nevada. Use code MSCUST for a $150 discount! Early bird discount ends December 31.
Register NowBe one of the first to start using Fabric Databases. View on-demand sessions with database experts and the Microsoft product team to learn just how easy it is to get started. Watch now
Hi Community,
I have a table which contains snapshot of users which are having a specific license on a specific date. A user can have license for a month and then never have it again, or the user can have the license for few months then it is removed and assigned again in future. Below is a table that represent simplified version of the original table. When a user has a license he/she will appear in the daily snapshot, if not then the user is not in the snapshot. In reality these are snapshots of members of AD security group in form of excel files loaded into a PBI dataflow and appended. Each daily file contains around 1500 users (records).
User ID | Snapshot Date |
1 | 01/01/2023 |
2 | 01/01/2023 |
3 | 01/01/2023 |
1 | 02/01/2023 |
2 | 02/01/2023 |
3 | 02/01/2023 |
1 | 03/01/2023 |
2 | 03/01/2023 |
1 | 04/01/2023 |
2 | 04/01/2023 |
1 | 05/01/2023 |
2 | 05/01/2023 |
3 | 05/01/2023 |
4 | 05/01/2023 |
3 | 06/01/2023 |
4 | 06/01/2023 |
let
Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("dY7BCcAwEMN28TvQxHfpMkf2XyOhrxZR8EtI4CoNNfVxnbk7tFrJREH0hGZohmYYDINW0kpak9bkiS/KH+um9UJrAw==", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [#"User ID" = _t, #"Snapshot Date" = _t]),
#"Changed Type" = Table.TransformColumnTypes(Source,{{"User ID", Int64.Type}, {"Snapshot Date", type date}})
in
#"Changed Type"
User ID | License Start On Date | License End On Date | Is Currently Active |
1 | 01/01/2023 | 05/01/2023 | No |
2 | 01/01/2023 | 05/01/2023 | No |
3 | 01/01/2023 | 02/01/2023 | No |
3 | 05/01/2023 | 06/01/2023 | Yes |
4 | 05/01/2023 | 06/01/2023 | Yes |
Initially I started with creating a cross-join between all User IDs and Snapshotdates and then joining it with the original table. This would tell me the dates on which the user did not have a license. This approach was not successful and it will not be optimal considering the daily snapshot has around 1500 records resulting in approx 550k records per year.
Also, for inspiration I have looked into the SQLBI's Data Modelling course, section Using Snapshots.
Related posts which I tried and failed to apply to my scenario:
Thank you beforehand for your suggestions.
Solved! Go to Solution.
Hi
1. Group by ID, sort by Date and add Index
2. LocalGroup if DateMax-DateMin = IndexMax-IndexMin then same group, else new group
let
Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("dY7BCcAwEMN28TvQxHfpMkf2XyOhrxZR8EtI4CoNNfVxnbk7tFrJREH0hGZohmYYDINW0kpak9bkiS/KH+um9UJrAw==", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [#"User ID" = _t, #"Snapshot Date" = _t]),
#"Changed Type" = Table.TransformColumnTypes(Source,{{"User ID", Int64.Type}, {"Snapshot Date", type date}}),
#"Group1-Sort+Index" = Table.Group(
#"Changed Type",
{"User ID"},
{{"Data", each Table.AddIndexColumn(Table.Sort(_,{{"Snapshot Date", Order.Ascending}}), "Index"),
type table [User ID=nullable number, Snapshot Date=nullable date, Index=Int64.Type]}}),
#"Group2-LocalGroup" = Table.AddColumn(
#"Group1-Sort+Index",
"Data2",
each Table.Group(
[Data],
{"User ID", "Snapshot Date", "Index"},
{{"Start", each List.Min([Snapshot Date]), type nullable date},
{"End", each List.Max([Snapshot Date]), type nullable date}},
GroupKind.Local,
(x,y)=> if(y[Index]-x[Index])=Duration.Days(y[Snapshot Date]-x[Snapshot Date]) then 0 else 1
)[[User ID],[Start],[End]]),
Data2 = Table.Combine(#"Group2-LocalGroup"[Data2])
in
Data2
Stéphane
Hi
I have 5 records.
I use french version, date = dd/MM/yyyy
Stéphane
I have not noticed that in the sample JSON file the month and day were switched around and that is why it did not work. After I have corrected the date and it worked as expected. Additionally I have connected your solution to my full dataset and it works.
Thank you very much for the solution.
Martin.
Hi
1. Group by ID, sort by Date and add Index
2. LocalGroup if DateMax-DateMin = IndexMax-IndexMin then same group, else new group
let
Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("dY7BCcAwEMN28TvQxHfpMkf2XyOhrxZR8EtI4CoNNfVxnbk7tFrJREH0hGZohmYYDINW0kpak9bkiS/KH+um9UJrAw==", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [#"User ID" = _t, #"Snapshot Date" = _t]),
#"Changed Type" = Table.TransformColumnTypes(Source,{{"User ID", Int64.Type}, {"Snapshot Date", type date}}),
#"Group1-Sort+Index" = Table.Group(
#"Changed Type",
{"User ID"},
{{"Data", each Table.AddIndexColumn(Table.Sort(_,{{"Snapshot Date", Order.Ascending}}), "Index"),
type table [User ID=nullable number, Snapshot Date=nullable date, Index=Int64.Type]}}),
#"Group2-LocalGroup" = Table.AddColumn(
#"Group1-Sort+Index",
"Data2",
each Table.Group(
[Data],
{"User ID", "Snapshot Date", "Index"},
{{"Start", each List.Min([Snapshot Date]), type nullable date},
{"End", each List.Max([Snapshot Date]), type nullable date}},
GroupKind.Local,
(x,y)=> if(y[Index]-x[Index])=Duration.Days(y[Snapshot Date]-x[Snapshot Date]) then 0 else 1
)[[User ID],[Start],[End]]),
Data2 = Table.Combine(#"Group2-LocalGroup"[Data2])
in
Data2
Stéphane
Hi Stéphane, I have pasted your m-code but I am not getting expected result. Each user has 1 record with duration of 1 day. The expected output is 5 records as shown in table in my initial post.
Could you share screenshot of the output on your side?
March 31 - April 2, 2025, in Las Vegas, Nevada. Use code MSCUST for a $150 discount!
Your insights matter. That’s why we created a quick survey to learn about your experience finding answers to technical questions.
Arun Ulag shares exciting details about the Microsoft Fabric Conference 2025, which will be held in Las Vegas, NV.