PowerQuery - Dynamic Date Parameter - powerquery

I have the following PowerQuery in Excel
let
Source = Excel.CurrentWorkbook(){[Name="Query1"]}[Content],
#"Changed Type" = Table.TransformColumnTypes(Source,{{"Month Year", type date}}),
#"Custom Step" = Table.SelectRows(#"Changed Type", each [Month Year] = #date(2017, 12, 1))
in
#"Custom Step"
I would like to set this up so that #date(2017, 12, 1)) is a parameter. In other words, I want to have a filter/dropdown on a worksheet and whatever the user selects (December 2017, November 2016, etc.) I want the "Custom Step" to filter the table based on the selection.
Is there some sort of syntax that will allow me to use a parameter? (kinda like #variable in SQL)

There are many different ways to get a parameter for a query. You can use a named range and access it directly or with a function, or you can even build a table with several parameters and access them with a function. Ken Puls has a good starting point for parameter tables here
With just one parameter, you can simply use a named range. In the following screenshot, cell F4 has the range name SelectedDate. It is easy to get this parameter with the line
myDate = Excel.CurrentWorkbook(){[Name="SelectedDate"]}[Content]{0}[Column1]
but the result will be text and won't filter the table correctly. You need to convert it into a date, for example by wrapping it into a Date.From function
myDate = Date.From(Excel.CurrentWorkbook(){[Name="SelectedDate"]}[Content]{0}[Column1]),
Now the variable myDate can be used to filter the table. Here is the complete M code:
let
myDate = Date.From(Excel.CurrentWorkbook(){[Name="SelectedDate"]}[Content]{0}[Column1]),
Source = Excel.CurrentWorkbook(){[Name="Query1"]}[Content],
ChangedType = Table.TransformColumnTypes(Source,{{"Month Year", type date}}),
Filtered = Table.SelectRows(ChangedType, each [Month Year] = myDate )
in
Filtered

Related

Power query, iterate over the column records to apply a custom cumulative calculation

Using Power Query in Excel. I am trying to implement a custom column that would iteratively calculate the row based on the previous row's value of the same column.
I have a 3 column table and the 4th column will be the calculation column that I am failing to implement.
The calculation is very easy to apply in Excel which goes as follows:
Formula in cell D3 --> = =IF(A3=1,C3+6.4,IF(C3+D2>=12.8,12.8,IF(C3+D2<=1.28,1.28,C3+D2)))
The same formula is applied to the whole column by dragging.
The idea behind it:
For each category, I have an index column starting from 1,
If Index = 1, then Calculation is Value + 6.4,
else if Value + Value(previous row Custom cumulative) >= 12.8 then 12.8
else if Value + Value(previous row Custom cumulative) <= 1.28 then 1.28
else Value + Value(previous row Custom cumulative)
So, the calculation is a cumulative sum with an upper and lower cap built into it.
How can I implement this in Power Query and M-Language?
I really appreciate your help!
I have tried to use List.Generate and List.Accumulate features, however, I was stuck with creating records that has values from multiple columns in it.
Try this
(edited to make more efficient with single pass process)
let Source = Excel.CurrentWorkbook(){[Name="Table15"]}[Content],
process = (zzz as list) => let x= List.Accumulate( zzz,{0},( state, current ) =>
if List.Last(state) =0 then List.Combine ({state,{6.4+current}}) else
if List.Last(state)+current >=12.8 then List.Combine ({state,{12.8}}) else
if List.Last(state)+current <=1.28 then List.Combine ({state,{1.28}}) else
List.Combine ({state,{List.Last(state)+current}})
) in x,
#"Grouped Rows" = Table.Group(Source, {"Category"}, {{"data", each
let a=process(_[Values])
in Table.AddColumn(_, "Custom Cumulative", each a{[Index]}), type table }}),
#"Expanded data" = Table.ExpandTableColumn(#"Grouped Rows", "data", {"Index", "Values", "Custom Cumulative"}, {"Index", "Values", "Custom Cumulative"})
in #"Expanded data"

Convert Switch True() Dax calculated column to M query custom colum

I am having issues with my calculated column and the multiple tables I am joining. It is not filtering my visuals correctly. After researching it was recommended to use a custom column in the query instead but I do not know where to start to convert the following DAX to M query.
overall =
VAR skills =
CALCULATETABLE (
VALUES ( tsr_skill[ts_skill] ),
ALLEXCEPT ( tsr_skill, tsr_skill[ts_tsr] )
)
RETURN
SWITCH (
TRUE (),
"JMSR" IN skills, "Senior",
"JMOV" IN skills, "Over",
"JMUN" IN skills, "Under",
"JMRH" IN skills, "RHT",
"MNT"
)
Data structure in Query:
How I would like the data to show in the Query instead of showing as a calculated column.
Preferred Output:
Based on your explanation, and the levels assigned in your DAX formula, it would seem that all should be assigned as "under".
In your "Preferred Output" you do show JMXX being assigned as "Over", but that tsr does not include the JMOV skill
If your written explanation is correct, and your Preferred Output screenshot incorrect based on the posted data, then, in PQ you can
Group by tsr
Create a custom aggregation returning the "overall" based on containing one of the skills listed in your DAX formula.
If that is not the case, please clarify how you are assigning "Over" to JMXX.
Edit: M Code simplified
M Code
let
//Source = the data structure you show
Source = Excel.CurrentWorkbook(){[Name="Table13"]}[Content],
#"Changed Type" = Table.TransformColumnTypes(Source,{{"ts_tsr", type text}, {"ts_skill", type text}}),
//Group rows by tsr, then check if it has one of the defined skills
//If so, return the appropriate ranking.
#"Grouped Rows" = Table.Group(#"Changed Type", {"ts_tsr"}, {
{"ALL", each _, type table [ts_tsr=nullable text, ts_skill=nullable text]},
{"overall", each if List.Contains([ts_skill],"JMSR") then "Senior"
else if List.Contains([ts_skill],"JMOV") then "Over"
else if List.Contains([ts_skill],"JMUN") then "Under"
else if List.Contains([ts_skill],"JMRH") >=0 then "RHT"
else "MNT"}
}),
//Then re-expand the table
#"Expanded ALL" = Table.ExpandTableColumn(#"Grouped Rows", "ALL", {"ts_skill"}, {"ts_skill"})
in
#"Expanded ALL"
Data
Output

Parameter options for User Defined Functions in PowerQuery

Hi i have been trying to make a user defined function that allows the user to select the values which the function will use from a list.
I have tried setting the parameter i want as a list to type list in my function but this only seems to accept columns rather than a list of values a user can select from.
let
ListOfDays = {1.1,0.5,2,3,1},
DayOfTheWeek = (Day as list, HoursWorked ) =>
let
Earnings = Day * HoursWorked
in
Earnings
in
DayOfTheWeek
What i would like is for me to allow the user to select a single value from the ListOfDays list. I used typed list within my function parameters so that it can give the user a dropdown list kind of option.
I believe this is the relevant documentation you are looking for:
github.com/microsoft/DataConnectors/docs/function-docs.md: Adding Function Documentation
In particular, look at the definition for Documentation.AllowedValues:
List of valid values for this parameter. Providing this field will change the input from a textbox to a drop down list. Note, this does not prevent a user from manually editing the query to supply alternate values.
This (and other Documentation fields) are part of the meta typing of the function arguments. Scroll down to the code snippet which shows how to use them:
[DataSource.Kind="HelloWorldWithDocs", Publish="HelloWorldWithDocs.Publish"]
shared HelloWorldWithDocs.Contents = Value.ReplaceType(HelloWorldImpl, HelloWorldType);
HelloWorldType = type function (
message as (type text meta [
Documentation.FieldCaption = "Message",
Documentation.FieldDescription = "Text to display",
Documentation.SampleValues = {"Hello world", "Hola mundo"}
]),
optional count as (type number meta [
Documentation.FieldCaption = "Count",
Documentation.FieldDescription = "Number of times to repeat the message",
Documentation.AllowedValues = { 1, 2, 3 }
]))
as table meta [
Documentation.Name = "Hello - Name",
Documentation.LongDescription = "Hello - Long Description",
Documentation.Examples = {[
Description = "Returns a table with 'Hello world' repeated 2 times",
Code = "HelloWorldWithDocs.Contents(""Hello world"", 2)",
Result = "#table({""Column1""}, {{""Hello world""}, {""Hello world""}})"
],[
Description = "Another example, new message, new count!",
Code = "HelloWorldWithDocs.Contents(""Goodbye"", 1)",
Result = "#table({""Column1""}, {{""Goodbye""}})"
]}
];
HelloWorldImpl = (message as text, optional count as number) as table =>
let
_count = if (count <> null) then count else 5,
listOfMessages = List.Repeat({message}, _count),
table = Table.FromList(listOfMessages, Splitter.SplitByNothing())
in
table;
They also provide a screenshot of what this should look like when invoked:
If the user is able to open up the Query Editor, then they can choose a Day parameter from a dropdown list and have this automatically apply to the query.
You would create the parameter from the Manage Parameters > New Parameter menu
The drop-down at the upper right of the image is how the user would select the choice.
Your User Defined Function fn_DayOfTheWeek would be the following:
let
DayOfTheWeek = (Day as number, HoursWorked as number) =>
let
Earnings = Day * HoursWorked
in
Earnings
in
DayOfTheWeek
Note that Day is a number, not a list. You want to choose from a list, not pass a list into the function.
Now you can invoke your function with your parameter to actually produce a result.
let
Source = fn_DayOfTheWeek(Day, <HoursWorked value here>)
in
Source
This result will update when you change the parameter.
As you can see, whether a user has access to the Query Editor is rather a critical question for this approach. I'm not sure if it's possible to somehow set a parameter directly within a custom connector dialog box or not but this should be equivalent in functionality.

Dynamic minimum and maximum dates using M/Power Query

I have a calendar table that is created, on-the-fly, using M.
It starts like this:
let
StartDate = #date(2016, 1, 1),
EndDate = #date(2018, 12, 31),
//Used for 'Offset' Column calculations, you may Hard code CurrentDate for testing e.g. #date(2017,9,1)
CurrentDate = DateTime.Date(DateTime.FixedLocalNow()),
// Specify the last month in your Fiscal Year, e.g. if June is the last month of your Fiscal Year, specify 6
FiscalYearEndMonth = 6,
#"==SET PARAMETERS ABOVE==" = 1,
#"==Build Date Column==" = #"==SET PARAMETERS ABOVE==",
ListDates = List.Dates(StartDate, Number.From(EndDate - StartDate)+1, #duration(1,0,0,0)),
...
...
Is it possible to make the first two lines dynamic so that they pick up minimum and maximum dates from database tables? So these two lines:
let
StartDate = #date(2016, 1, 1),
EndDate = #date(2018, 12, 31),
I have another table being loaded into the model using the following - can I somehow use the Date column from this loads to dynamically set StartDate and EndDate ?
let
Source = Sql.Database("ourServer", "ourDB"),
tb_ModelFact = Source{[Schema="dbo",Item="tb_ModelFact"]}[Data],
#"Changed Type" = Table.TransformColumnTypes(tb_ModelFact,{{"Date", type datetime}}),
#"Filtered Rows" = Table.SelectRows(#"Changed Type", each true),
#"Changed Type1" = Table.TransformColumnTypes(#"Filtered Rows",{{"Amount", type number}})
in
#"Changed Type1"
Edit
So I tried this
StartDate = List.Min(Fact[Date]),
EndDate = List.Max(Fact[Date]),
....and got this mysterious error?
Expression.Error: We cannot convert the value #datetime(2016, 1, 6, 0, 0, 0) to type Date.
Details:
Value=06/01/2016 00:00:00
Type=Type
Is this error in subsequent M code after the declaration?
Yes. You should be able to write something along these lines:
let
StartDate = List.Min(tb_ModelFact[Date]),
EndDate = List.Max(tb_ModelFact[Date]),
where tb_ModelFact[Date] is the column that has the dates you are trying to take the max and min from.
You will need to change tb_ModelFact to whatever the name of that second query is though.

DAX EARLIER() function in Power Query

Is there an an equivalent to EARLIER in M/Power Query?
Say, I have a table with lots of different dates in column DATE and a smaller number of letters in column LETTER. I now want the maximum date for each letter.
In DAX, I would use something like CALCULATE(MAX([Date]),FILTER(ALL(Table),[Letter]=EARLIER([Letter])).
How would I achieve the same in M?
Thanks
2 Solutions in the code below. Notice that each uses "PreviousStep" as basis, so these are separate solutions.
let
Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
PreviousStep = Table.TransformColumnTypes(Source,{{"Date", type date}, {"Letter", type text}}),
// 1. Add a column to the original table with the MaxDate for each letter
// "earlier" is just the name of a function parameter; it could as well have been "x" or "MarcelBeug"
AddedMaxDate = Table.AddColumn(PreviousStep, "MaxDate", (earlier) => List.Max(Table.SelectRows(PreviousStep, each [Letter] = earlier[Letter])[Date])),
// 2. Group by letter and get the MaxDate for each letter
GroupedOnLetter = Table.Group(PreviousStep, {"Letter"}, {{"MaxDate", each List.Max([Date]), type date}})
in
GroupedOnLetter
In short, there is no exact match for this function. Still, you can use other ways that can produce same results.
To reproduce example offered by Microsoft in help for EARLIER function, you can use following code (table1 equals table given in the example before ranking):
let
Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("TVNNaxtBDP0rxuSoivn+uMYlLSUFE4f2YHIYd4d48Xq3rO1C/n01o1mc4+i9kZ6epP1+LcMa1o/94cvuOM3XCz0epHUgnccQ1m+wXytXGae8ekl/TpWhlACvBHrBDL8wdtc0dpWiLTgV0EVm1CrT9Trky4ooq016z5VnI2ij0OjKs402nVePM1XLrMgEcEaj8ZVU9czpxAmcAik1SlcxGSm2SX/5m4eoDVpToSJyc0z9WLEAwXgUrcX6a8hpzDNb4CAEhU5VuIjfzGk8XZoeGSVYpVBwd+X31zynfhjyjRM4A9FZ1NyWFhR7ymPX0hsJ0RuUbJ+s6DSzt96QtR4d96MK9m2Y/uVmfABtNVrWbSj2newc8iEtwjUoS401O2Rh5NQtyq0HZyNGFq4ZHs6Lz1aCjAopXmFV4I9uTtd+GlfbZfyR3IkafTOvJPlBneUPbj1GMCouMFkA6+f+/VhLcKjofp5aNmlBkKQ23JLs53QbrzSoVdkp3iYDWlgIzqBi6VJ9Jj7N6cxMA1ZSE16ga/XLTm3TOPZsPv8uora5SwNLMIIkK1Q8EF02bHs78xZJBS5alK1bCr1Mqbtro7+WfHPRoeZNk2Yh3XVpcNqBjgE9myuLrl3qaHg8GUUr5RYbVKlzP0kdLHhBJ9kOrsjfLQaWndCEWcZK8dfF7wcZIrkRUXNe7Ss6tzN8vR2WxTIQtMLQJl9Y023ux/d7o1JTHVOH0MyQ7hPv3isdh7F01gYFH5Aqvf7KF5akyLEYBYrmVpH0+5jz0C4nADEq+vYf", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type text) meta [Serialized.Text = true]) in type table [ProductSubcategoryKey = _t, EnglishProductSubcategoryName = _t, TotalSubcategorySales = _t]),
table1 = Table.TransformColumnTypes(Source,{{"ProductSubcategoryKey", Int64.Type}, {"EnglishProductSubcategoryName", type text}, {"TotalSubcategorySales", Currency.Type}}, "en-US"),
AddCount = Table.AddColumn(
table1,
"SubcategoryRanking", //(a) is a parameter for function, which equals current record, and function should return value for new cell of "SubcategoryRanking"
(a)=> Table.RowCount(
Table.SelectRows(
table1, //(b) equals whole table1. This function returns table filtered by given criteria
(b) => b[TotalSubcategorySales] < a[TotalSubcategorySales])
) + 1,
Int64.Type)
in
AddCount
I think you can use the GroupBy function to group the data by Letter and find the Max of the date column. So your code should look like.
= Table.Group(#"Previous step", {"Letter"}, {{"Max Date", each List.Max([Date]), type date}})

Resources