{"id":2090,"date":"2015-09-21T00:00:00","date_gmt":"2015-09-18T00:00:00","guid":{"rendered":"https:\/\/test.simple-talk.com\/uncategorized\/power-query-formula-language-in-power-bi-desktop\/"},"modified":"2021-05-17T18:33:14","modified_gmt":"2021-05-17T18:33:14","slug":"power-query-formula-language-in-power-bi-desktop","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/bi-sql-server\/power-query-formula-language-in-power-bi-desktop\/","title":{"rendered":"Power Query Formula Language in Power BI Desktop"},"content":{"rendered":"<div id=\"pretty\">\n<p>Power BI  Desktop provides a robust adjunct to the Power BI services by offering access to  a wider range of data sources and transformations, in addition to its offline  capabilities. At the heart of Power BI Desktop is the Power Query Formula  Language (PQFL), a dynamically typed, case-sensitive functional language for  creating mashup queries, similar to the F# language. Although Power BI Desktop  has a number of UI features for simplifying transformations, PQFL underpins  those transformations, and the better you understand the language, the more you  can take advantage of the capabilities inherent in Power BI Desktop.<\/p>\n<p>Formerly  known as M, PQFL has its roots in Power Query for Excel, and as such, much of  the information you find about the language is specific to Excel. Although this  will likely change as Power BI Desktop catches on, currently it can be difficult  to find examples specific to Power BI Desktop that will help you resolve a  particular issue. That said, PQFL follows a very specific formula that is  uniform enough to make it relatively painless, at least in some cases, to come  up with solutions on your own, as long as you know the basics.<\/p>\n<p>In this  article, I introduce you to PQFL so you can see for yourself how to use the  language to create mashup queries from scratch. If you&#8217;re not familiar with  Power BI Desktop, I suggest you first review my article <a href=\"https:\/\/www.simple-talk.com\/sql\/reporting-services\/working-with-sql-server-data-in-power-bi-desktop\/\"> Working with SQL Server data in Power BI Desktop<\/a>, which provides details on  how to get started using the application to retrieve and transform data.<\/p>\n<p>Each  transformed data set in Power BI Desktop is essentially a PQFL query that  retrieves data from a source and modifies the data in various ways. To create a  PQFL query, you start with a <b>let<\/b>  expression that contains a series of expression steps.<\/p>\n<p>Each step is  in essence its own expression that assigns a value to a variable, based on a  combination of functions, operators, and value types. The value types can  include simple primitive scalar values such as numbers or strings<i>,<\/i>  or include objects such as lists, records, or tables. The variable can then be  used in one or more subsequent expression steps, which themselves define  additional variables. This will all become clearer as we work through a few  examples. <\/p>\n<p>For this  article, we&#8217;ll be retrieving data from a SQL Server database, so we&#8217;ll be  restricting ourselves primarily to table objects and primitive values; however,  the concepts we cover apply to all object types. To demonstrate these concepts,  I created a set of examples based on data from the <b>AdventureWorks2014<\/b> database on a  local instance of SQL Server 2014. Each example builds in the previous one in  order to demonstrate how to create a PQFL query one expression step at a time.  With that in mind, let&#8217;s get started.<\/p>\n<h1>Retrieving data from SQL  Server<\/h1>\n<p>In Power BI  Desktop, you can create a PQFL query without first connecting to a data source.  Instead, you define the connection within the \t<b>let<\/b> expression, as the first expression step. To begin, open Query  Editor and take the following steps:<\/p>\n<ol>\n<li>Click the <b>New Source<\/b> button on the  \t<b>Home<\/b>  ribbon, and then click <b>Blank Query<\/b>. A  new query is added to the <b>Queries<\/b>  pane (named <b>Query1<\/b>, by default).<\/li>\n<li>In the <b>Properties<\/b> section of the  \t<b>Query  Settings<\/b> pane, type a new name for the query. I used \t<b>RepSales<\/b> because we&#8217;re going to  create a data set based on the annual sales of the Adventure Works sales reps.<\/li>\n<li>In the <b>Queries<\/b> pane, right-click the new query, and then click \t<b>Advanced Editor<\/b>.<\/li>\n<\/ol>\n<p>The last  step launches the advanced editor, with the start of a \t<b>let<\/b> expression already defined, as shown in the following figure.<\/p>\n<p> \t \t<img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/2285-img3C.jpg\" alt=\"2285-img3C.jpg\" \/><\/p>\n<p>The \t<b>let<\/b> expression includes two parts,  the <b>let<\/b> statement and the \t<b>in<\/b> statement. The \t<b>let<\/b> statement defines the expression steps, and the \t<b>in<\/b> statement specifies variable,  which determines what data the query returns.<\/p>\n<p>The first  expression step in the <b>let<\/b> statement  defines the <b>Source<\/b> variable as an  empty string. Ultimately, the variable will be used to identify the initial data  source. <\/p>\n<p>You don&#8217;t  need to stick with naming the variable <b>Source<\/b>. You can use just about any name you want within reason. PQFL even  supports variable names that contain spaces, but then you must enclose the  variable name in quotes and precede it with a hash mark, a rather messy coding  convention that I try to avoid. Whatever you name your variables, keep in mind  that PQFL is a case-sensitive language, which applies to all elements.<\/p>\n<p>The first  step, then, is to change the variable definition to something other than an  empty string. For that, we can turn to one of the many PQFL functions available  to the various types of objects and primitive values. Because we will be  retrieving data from a SQL Server database, we&#8217;ll use the \t<b>Sql.Database<\/b> function to specify the SQL Server instance, database,  and a T-SQL query, as shown in the following \t<b>let<\/b> expression:<\/p>\n<pre>\tlet\n\t&#160; Source = Sql.Database(\"localhost\\sqlsrv2014\", \"adventureworks2014\", \n\t&#160;&#160;&#160; [Query=\"SELECT BusinessEntityID, FirstName, MiddleName, LastName, \n\t&#160;&#160;&#160;&#160;&#160; JobTitle, City, StateProvinceName, CountryRegionName, TerritoryName, \n\t&#160;&#160;&#160;&#160;&#160; TerritoryGroup, SalesYTD, SalesLastYear\n\t&#160;&#160;&#160;&#160;&#160; FROM Sales.vSalesPerson;\"])\n\tin\n\t&#160; Source\n\t&#160;\n<\/pre>\n<p>As you can  see, the <b>Source<\/b> definition uses the \t<b>Sql.Database<\/b> function to call the \t<b>AdventureWorks2014<\/b> database on a  named SQL Server instance (<b>localhost\\sqlsrv2014<\/b>).  The <b>Query<\/b> option, which is enclosed  in brackets, defines a straightforward T-SQL query that retrieves data from the \t<b>Sales.vSalesPerson<\/b> view. Of course,  we can make our T-SQL query far more complex, or we can keep it simple and do  the work in our <b>let<\/b> expression, which  is what we&#8217;ll do here.<\/p>\n<p>That&#8217;s all  there is to defining the initial data source. The following figure show what the \t<b>Advanced Editor<\/b> window should look  like after we&#8217;ve updated the <b>Source<\/b>  definition.<\/p>\n<p> \t \t<img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/2285-img3D.jpg\" alt=\"2285-img3D.jpg\" \/><\/p>\n<p>Before we  continue, it&#8217;s worth pointing out that the advanced editor in Power BI Desktop  is anything but <i>advanced<\/i>. It is barely  a step above Notepad, providing only a rudimentary syntax checker. If it does  find a problem, the checker often fails to point to the correct part of the  syntax, or worse still, the checker misses the error altogether, and it&#8217;s not  until you run your query that you discover something is wrong. <\/p>\n<p>You also  cannot save your changes as you go along within the advanced editor. If the  syntax checker fails to find an error, and running the code crashes the system,  you will lose all your changes, which has happened to me. One way to protect  yourself again this particular scenario is to copy your work over to Notepad or  some other text editor as you&#8217;re working so you don&#8217;t risk losing what you&#8217;re  doing. You should also run your query whenever you add or modify an expression  step. If the query runs successfully, you can apply and save your work.<\/p>\n<p>The advanced  editor is not particularly conducive to trial-and-error development. Whenever  you want to check the results of a code change, you must exit the environment to  run the query (and save it) and then re-launch the editor to continue, usually  having to resize the window with each cycle. With luck, Microsoft will improve  this feature at some point in the future, if enough of us whine about it.<\/p>\n<p>Now that  you&#8217;ve been warned, we can move forward. If you&#8217;re satisfied with your \t<b>let<\/b> expression as it stands, click \t<b>Done<\/b> to close the advanced editor and  run the query. Your results should now look similar to the Query Editor window  shown in the following figure.<\/p>\n<p> \t \t<img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/2285-img3E.jpg\" alt=\"2285-img3E.jpg\" \/><\/p>\n<p>If you&#8217;ve  gotten this far, you have now successfully retrieved the data from SQL Server  and you can start transforming it.<\/p>\n<h1>Filtering rows<\/h1>\n<p>Often, when  you&#8217;re developing your <b>let<\/b>  expression, you will use the variable you define in one step as part of the  expression in the next step. In this way, you create your query as well defined  and delineated steps that build on each other.<\/p>\n<p>To  demonstrate how this works, we&#8217;ll filter out all rows whose \t<b>TerritoryName<\/b> value is null. The idea  is to remove all managers from our list so we end up with only the sales reps.  We can instead filter the data based on the \t<b>JobTitle<\/b> column, retrieving only those rows that contain the \t<b>Sales Representative<\/b> value. I went  with the nullability factor because strings are easier to mess up when you&#8217;re  typing, but you can do whatever works best in your particular circumstances.  Given that this is just an example, I went for the low-hanging fruit.<\/p>\n<p>To filter  the data, we&#8217;ll use the <b>Table.SelectRows<\/b>  function. As you might have noticed with the \t<b>Sql.Database<\/b> function, PQFL functions are usually specified as  two-part names. The first part is the object type, and the second is the actual  function name. For the <b>Sql.Database<\/b>  function, the object type is <b>Sql<\/b>, and  for the <b>Table.SelectRows<\/b> function,  the object type is <b>Table<\/b>.<\/p>\n<p>The \t<b>Table<\/b> object type supports a wide  range of functions, as do all the object types. You can find a comprehensive  list of the PQFL functions in the \t<a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/mt211003.aspx\">Power Query  Formula Reference<\/a>. In the meantime, let&#8217;s return to our \t<b>let<\/b> expression and add a second  variable definition, which we&#8217;ll name <b>SalesRep<\/b>:<\/p>\n<pre class=\"mark:7,9\">\tlet\n\t&#160; Source = Sql.Database(\"localhost\\sqlsrv2014\", \"adventureworks2014\", \n\t&#160;&#160;&#160; [Query=\"SELECT BusinessEntityID, FirstName, MiddleName, LastName, \n\t&#160;&#160;&#160;&#160;&#160; JobTitle, City, StateProvinceName, CountryRegionName, TerritoryName, \n\t&#160;&#160;&#160;&#160;&#160; TerritoryGroup, SalesYTD, SalesLastYear\n\t&#160;&#160;&#160;&#160;&#160; FROM Sales.vSalesPerson;\"]),\n\t&#160; SalesRep = Table.SelectRows(Source, each([TerritoryName] &lt;&gt; null))\n\tin\n\t&#160; SalesRep\n\t<\/pre>\n<p>To add a new  variable definition, we add a comma after the first expression step and start  the new definition with the variable name, as we did with the first one. There  is no need to specifically declare or type variables in PQFL. The typing occurs  as part of the variable definition. <\/p>\n<p>In this  case, we&#8217;re creating a table object, as determined by the the \t<b>Table.SelectRows<\/b> function, which returns a table object, just like  the <b>Sql.Database<\/b> function. The \t<b>Table.SelectRows<\/b> function takes two  arguments. The first is the input table, the \t<b>Source<\/b> variable, and the second is the expression that determines  which rows to filter out. In this case, the expression uses the \t<b>each<\/b> function to iterate through the rows. For each row, the \t<b>TerritoryName<\/b> column must not be  null.<\/p>\n<p>That&#8217;s all  there is to filtering rows, but also notice that the \t<b>in<\/b> statement now calls the <b>SalesRep<\/b> variable, rather than  \t<b>Source<\/b>.  The <b>SalesRep<\/b> variable contains the  transformed data, which is the data we want to return. Generally, you should  define your <b>if<\/b> statement to call the  last variable defined in the <b>let<\/b>  statement.<\/p>\n<h1>Creating a custom column<\/h1>\n<p>Next we&#8217;ll  create a custom column named <b>FullName<\/b>  that concatenates each sales rep name. However, we&#8217;ll need to include  conditional logic in our variable definition to account for those reps with a  null <b>MiddleName<\/b> value. For that,  we&#8217;ll include an <b>if<\/b> expression in the  variable definition, as shown in the following \t<b>let<\/b> statement:<\/p>\n<pre class=\"mark:8,9,10,11\">\tlet\n\t&#160; Source = Sql.Database(\"localhost\\sqlsrv2014\", \"adventureworks2014\", \n\t&#160;&#160;&#160; [Query=\"SELECT BusinessEntityID, FirstName, MiddleName, LastName, \n\t&#160;&#160;&#160;&#160;&#160; JobTitle, City, StateProvinceName, CountryRegionName, TerritoryName, \n\t&#160;&#160;&#160;&#160;&#160; TerritoryGroup, SalesYTD, SalesLastYear\n\t&#160;&#160;&#160;&#160;&#160; FROM Sales.vSalesPerson;\"]),\n\t&#160; SalesRep = Table.SelectRows(Source, each([TerritoryName] &lt;&gt; null)),\n\t&#160; RepName = Table.AddColumn(SalesRep, \"FullName\", each \n\t&#160;&#160;&#160; if [MiddleName] &lt;&gt; null \n\t&#160;&#160;&#160; then [FirstName]&#160; &amp; \" \" &amp;&#160; [MiddleName]&#160; &amp; \" \" &amp; [LastName]\n\t&#160;&#160;&#160; else [FirstName] &amp; \" \" &amp; [LastName])\n\tin\n\t&#160; RepName\n\t&#160;\n<\/pre>\n<p>We start by  adding the <b>RepName<\/b> variable,  following by the definition. The definition uses the the \t<b>Table.AddColumn<\/b> function, which takes three arguments. The first  argument specifies the table source, <b>SalesRep<\/b>, and the second argument specifies the name of the new column, \t<b>FullName<\/b>. <\/p>\n<p>The third  argument is the column expression that does all the work. The expression begins  with the <b>each<\/b> function, a special  function used to iterate through a collection of data. This is followed an the \t<b>if<\/b> expression, which defines the  conditional logic needed to know how to concatenate the names. If the \t<b>MiddleName<\/b> value is not null, the \t<b>FirstName<\/b>, \t<b>MiddleName<\/b>, and <b>LastName<\/b>  values are concatenated, with spaces in between. Otherwise, only the \t<b>FirstName<\/b> and \t<b>LastName<\/b> values are concatenated, again, with a space.<\/p>\n<p>Although  this is only a simple example of using an <b>if<\/b> expression, it demonstrates what a powerful tool you have available for  transforming your data. You can create a data set that accounts for different  types of values. Notice also that this time our \t<b>in <\/b>statement calls the <b>RepName<\/b> variable to ensure that our  \t<b>let<\/b> expression returns the fully transformed data.<\/p>\n<h1>Removing columns<\/h1>\n<p>Now let&#8217;s do  a few more transformations, starting with removing the columns we no longer  need: <b>FirstName<\/b>, \t<b>MiddleName<\/b>, \t<b>LastName<\/b>, and <b>JobTitle<\/b>.  Because the expression steps build on each other, we can remove these columns  without affecting previous operations. In other words, the most recent variable, \t<b>RepName<\/b>, represents our data set&#8217;s  current reality, allowing us to act on the variable however we need to without  looking back.<\/p>\n<p>To remove  the columns, we&#8217;ll create a variable named \t<b>RemoveCols<\/b> and invoke the \t<b>Table.RemoveColumns<\/b> function, as  shown in the following example:<\/p>\n<pre class=\"mark:12\">\tlet\n\t&#160; Source = Sql.Database(\"localhost\\sqlsrv2014\", \"adventureworks2014\", \n\t&#160;&#160;&#160; [Query=\"SELECT BusinessEntityID, FirstName, MiddleName, LastName, \n\t&#160;&#160;&#160;&#160;&#160; JobTitle, City, StateProvinceName, CountryRegionName, TerritoryName, \n\t&#160;&#160;&#160;&#160;&#160; TerritoryGroup, SalesYTD, SalesLastYear\n\t&#160;&#160;&#160;&#160;&#160; FROM Sales.vSalesPerson;\"]),\n\t&#160; SalesRep = Table.SelectRows(Source, each([TerritoryName] &lt;&gt; null)),\n\t&#160; RepName = Table.AddColumn(SalesRep, \"FullName\", each \n\t&#160;&#160;&#160; if [MiddleName] &lt;&gt; null \n\t&#160;&#160;&#160; then [FirstName]&#160; &amp; \" \" &amp;&#160; [MiddleName]&#160; &amp; \" \" &amp; [LastName]\n\t&#160;&#160;&#160; else [FirstName] &amp; \" \" &amp; [LastName]),\n\t&#160; RemoveCols = Table.RemoveColumns(RepName,{\"FirstName\", \"MiddleName\", \"LastName\", \"JobTitle\"})\n\tin\n\t&#160; RemoveCols\n\t<\/pre>\n<p> \tThe \t<b>Table.RemoveColumns<\/b> function takes  only two arguments, the table source, <b>RepName<\/b>, and the list of columns to delete. For such a list, the column  names must be enclosed in quotes and separated by commas, and the entire list  must be enclosed in curly braces.<\/p>\n<h1>Renaming columns<\/h1>\n<p>At this  point, you should have a good sense of how the expression steps build on each  other as you continue to apply transformations to your data. We can take this  same approach to rename columns:<\/p>\n<pre class=\"mark:13,14,15,16\">\tlet\n\t&#160; Source = Sql.Database(\"localhost\\sqlsrv2014\", \"adventureworks2014\", \n\t&#160;&#160;&#160; [Query=\"SELECT BusinessEntityID, FirstName, MiddleName, LastName, \n\t&#160;&#160;&#160;&#160;&#160; JobTitle, City, StateProvinceName, CountryRegionName, TerritoryName, \n\t&#160;&#160;&#160;&#160;&#160; TerritoryGroup, SalesYTD, SalesLastYear\n\t&#160;&#160;&#160;&#160;&#160; FROM Sales.vSalesPerson;\"]),\n\t&#160; SalesRep = Table.SelectRows(Source, each([TerritoryName] &lt;&gt; null)),\n\t&#160; RepName = Table.AddColumn(SalesRep, \"FullName\", each \n\t&#160;&#160;&#160; if [MiddleName] &lt;&gt; null \n\t&#160;&#160;&#160; then [FirstName]&#160; &amp; \" \" &amp;&#160; [MiddleName]&#160; &amp; \" \" &amp; [LastName]\n\t&#160;&#160;&#160; else [FirstName] &amp; \" \" &amp; [LastName]),\n\t&#160; RemoveCols = Table.RemoveColumns(RepName,{\"FirstName\", \"MiddleName\", \"LastName\", \"JobTitle\"}),\n\t&#160; RenameCols = Table.RenameColumns(RemoveCols,{{\"BusinessEntityID\", \"RepID\"}, \n\t&#160;&#160;&#160; {\"StateProvinceName\", \"StateProvince\"}, \n\t&#160;&#160;&#160; {\"CountryRegionName\", \"CountryRegion\"}, \n\t&#160;&#160;&#160; {\"TerritoryName\", \"Territory\"}})\n\tin\n\t&#160; RenameCols\n\t<\/pre>\n<p>This time  around, we create a variable named <b>RenameCols<\/b> and include in the definition the the \t<b>Table.RenameColumns<\/b> function, which  takes two arguments. The first argument, as you&#8217;ve seen with other functions,  identifies our source table, <b>RemoveCols<\/b>, and the second argument provides a list of columns to  rename. Each element in the list specifies the column&#8217;s original name and its  new name, with the names enclosed in their own set of curly braces. For example,  we&#8217;re changing the name of the <b>BusinessEntityID<\/b> column to  \t<b>RepID<\/b>,  and we&#8217;re change the name of the <b>StateProvinceName<\/b> column to  \t<b>StateProvince<\/b>.<\/p>\n<h1>Rounding numerical data<\/h1>\n<p>We can also  add expression steps that round the numerical columns, \t<b>SalesYTD<\/b> and <b>SalesLastYear<\/b>,  but we have to take a slightly different approach from the previous two  examples. We&#8217;ll still call a <b>Table<\/b>  function, <b>Table.TransformColumns<\/b>,  which also takes two arguments, but the second argument includes a couple other  functions, as shown in the following <b>let<\/b>  expression:<\/p>\n<pre class=\"mark:17,18,19,20\">\tlet\n\t&#160; Source = Sql.Database(\"localhost\\sqlsrv2014\", \"adventureworks2014\", \n\t&#160;&#160;&#160; [Query=\"SELECT BusinessEntityID, FirstName, MiddleName, LastName, \n\t&#160;&#160;&#160;&#160;&#160; JobTitle, City, StateProvinceName, CountryRegionName, TerritoryName, \n\t&#160;&#160;&#160;&#160;&#160; TerritoryGroup, SalesYTD, SalesLastYear\n\t&#160;&#160;&#160;&#160;&#160; FROM Sales.vSalesPerson;\"]),\n\t&#160; SalesRep = Table.SelectRows(Source, each([TerritoryName] &lt;&gt; null)),\n\t&#160; RepName = Table.AddColumn(SalesRep, \"FullName\", each \n\t&#160;&#160;&#160; if [MiddleName] &lt;&gt; null \n\t&#160;&#160;&#160; then [FirstName]&#160; &amp; \" \" &amp;&#160; [MiddleName]&#160; &amp; \" \" &amp; [LastName]\n\t&#160;&#160;&#160; else [FirstName] &amp; \" \" &amp; [LastName]),\n\t&#160; RemoveCols = Table.RemoveColumns(RepName,{\"FirstName\", \"MiddleName\", \"LastName\", \"JobTitle\"}),\n\t&#160; RenameCols = Table.RenameColumns(RemoveCols,{{\"BusinessEntityID\", \"RepID\"}, \n\t&#160;&#160;&#160; {\"StateProvinceName\", \"StateProvince\"}, \n\t&#160;&#160;&#160; {\"CountryRegionName\", \"CountryRegion\"}, \n\t&#160;&#160;&#160; {\"TerritoryName\", \"Territory\"}}),\n\t&#160; RoundSalesYTD = Table.TransformColumns(RenameCols,\n\t&#160;&#160;&#160; {{\"SalesYTD\", each Number.Round(_, 0), type number}}),\n\t&#160; RoundSalesLastYear = Table.TransformColumns(RoundSalesYTD,\n\t&#160;&#160;&#160; {{\"SalesLastYear\", each Number.Round(_, 0), type number}})\n\tin\n\t&#160; RoundSalesLastYear\n\t<\/pre>\n<p>I&#8217;ve created  two expression steps to more easily explain the components, but we could have  instead used one step. The first step defines the \t<b>RoundSalesYTD<\/b> variable. The \t<b>Table.TransformColumns<\/b> function takes  two arguments: the source table, <b>RenameCols<\/b>, and the transform operation. The transform operation is  made up of three components. The name of the target column, \t<b>SalesYTD<\/b>, an expression that performs  the actual transformation, and the return type, \t<b>number<\/b>. <\/p>\n<p>The  transformation expression starts with the <b>each<\/b> function, followed by the  \t<b>Number.Round<\/b> function, which does the actually rounding. This function also  takes two arguments: the value to round and the number of decimal points to  apply. In this case, the value to round is an underscore (_), which simply  represents the column&#8217;s current value, and the 0 indicates that the outputted  value should contain no decimal points.<\/p>\n<p>The next  expression step, which defines the <b>RoundSalesLastYear<\/b> variable works this same way, just with a different  column. If you want to instead turn these two steps into one, you can do  something like the following:<\/p>\n<pre>\tRoundCols = Table.TransformColumns(RenameCols, {\n\t&#160; {\"SalesYTD\", each Number.Round(_, 0), type number}, \n\t&#160; {\"SalesLastYear\", each Number.Round(_, 0), type number}})\n<\/pre>\n<p>For now,  however, we&#8217;ll keep the expression steps separate and move on to our next  transformation.<\/p>\n<h1>Reordering columns<\/h1>\n<p>You might  find that you don&#8217;t like the order of the columns in a data set, in which case  you can use the <b>Table.ReorderColumns<\/b>  function, which also takes only two arguments: the source table, \t<b>RoundSalesLastYear<\/b>, and the list of  columns in the order you want them:<\/p>\n<pre class=\"mark:21,22,23\">\tlet\n\t&#160; Source = Sql.Database(\"localhost\\sqlsrv2014\", \"adventureworks2014\", \n\t&#160;&#160;&#160; [Query=\"SELECT BusinessEntityID, FirstName, MiddleName, LastName, \n\t&#160;&#160;&#160;&#160;&#160; JobTitle, City, StateProvinceName, CountryRegionName, TerritoryName, \n\t&#160;&#160;&#160;&#160;&#160; TerritoryGroup, SalesYTD, SalesLastYear\n\t&#160;&#160;&#160;&#160;&#160; FROM Sales.vSalesPerson;\"]),\n\t&#160; SalesRep = Table.SelectRows(Source, each([TerritoryName] &lt;&gt; null)),\n\t&#160; RepName = Table.AddColumn(SalesRep, \"FullName\", each \n\t&#160;&#160;&#160; if [MiddleName] &lt;&gt; null \n\t&#160;&#160;&#160; then [FirstName]&#160; &amp; \" \" &amp;&#160; [MiddleName]&#160; &amp; \" \" &amp; [LastName]\n\t&#160;&#160;&#160; else [FirstName] &amp; \" \" &amp; [LastName]),\n\t&#160; RemoveCols = Table.RemoveColumns(RepName,{\"FirstName\", \"MiddleName\", \"LastName\", \"JobTitle\"}),\n\t&#160; RenameCols = Table.RenameColumns(RemoveCols,{{\"BusinessEntityID\", \"RepID\"}, \n\t&#160;&#160;&#160; {\"StateProvinceName\", \"StateProvince\"}, \n\t&#160;&#160;&#160; {\"CountryRegionName\", \"CountryRegion\"}, \n\t&#160;&#160;&#160; {\"TerritoryName\", \"Territory\"}}),\n\t&#160; RoundSalesYTD = Table.TransformColumns(RenameCols,\n\t&#160;&#160;&#160; {{\"SalesYTD\", each Number.Round(_, 0), type number}}),\n\t&#160; RoundSalesLastYear = Table.TransformColumns(RoundSalesYTD,\n\t&#160;&#160;&#160; {{\"SalesLastYear\", each Number.Round(_, 0), type number}}),\n\t&#160; ReorderCols = Table.ReorderColumns(RoundSalesLastYear,{\"RepID\", \"FullName\", \"City\", \n\t&#160;&#160;&#160; \"StateProvince\", \"CountryRegion\", \"Territory\", \n\t&#160;&#160;&#160; \"TerritoryGroup\", \"SalesLastYear\", \"SalesYTD\"})&#160;&#160;&#160; \n\tin\n\t&#160; ReorderCols\n\t<\/pre>\n<p>All very  straightforward. The only trick is to enclose the list of columns in curly  braces, separate the column names with commas, and enclose each column name in  quotes.<\/p>\n<h1>Building on multiple  variables<\/h1>\n<p>Now let&#8217;s  look at one final transformation in which we add another custom column. This  time, however, we&#8217;re going to use a series of embedded \t<b>if<\/b> expressions to set the value. We&#8217;re also going to do something  else-introduce a second variable to add to the mix. Let&#8217;s start by looking at  the updated <b>let<\/b> expression:<\/p>\n<pre class=\"mark:25,26,27,28,29,30,31,32,33,34,35,36\">\tlet\n\t&#160; Source = Sql.Database(\"localhost\\sqlsrv2014\", \"adventureworks2014\", \n\t&#160;&#160;&#160; [Query=\"SELECT BusinessEntityID, FirstName, MiddleName, LastName, \n\t&#160;&#160;&#160;&#160;&#160; JobTitle, City, StateProvinceName, CountryRegionName, TerritoryName, \n\t&#160;&#160;&#160;&#160;&#160; TerritoryGroup, SalesYTD, SalesLastYear\n\t&#160;&#160;&#160;&#160;&#160; FROM Sales.vSalesPerson;\"]),\n\t&#160; SalesRep = Table.SelectRows(Source, each([TerritoryName] &lt;&gt; null)),\n\t&#160; RepName = Table.AddColumn(SalesRep, \"FullName\", each \n\t&#160;&#160;&#160; if [MiddleName] &lt;&gt; null \n\t&#160;&#160;&#160; then [FirstName]&#160; &amp; \" \" &amp;&#160; [MiddleName]&#160; &amp; \" \" &amp; [LastName]\n\t&#160;&#160;&#160; else [FirstName] &amp; \" \" &amp; [LastName]),\n\t&#160; RemoveCols = Table.RemoveColumns(RepName,{\"FirstName\", \"MiddleName\", \"LastName\", \"JobTitle\"}),\n\t&#160; RenameCols = Table.RenameColumns(RemoveCols,{{\"BusinessEntityID\", \"RepID\"}, \n\t&#160;&#160;&#160; {\"StateProvinceName\", \"StateProvince\"}, \n\t&#160;&#160;&#160; {\"CountryRegionName\", \"CountryRegion\"}, \n\t&#160;&#160;&#160; {\"TerritoryName\", \"Territory\"}}),\n\t&#160; RoundSalesYTD = Table.TransformColumns(RenameCols,\n\t&#160;&#160;&#160; {{\"SalesYTD\", each Number.Round(_, 0), type number}}),\n\t&#160; RoundSalesLastYear = Table.TransformColumns(RoundSalesYTD,\n\t&#160;&#160;&#160; {{\"SalesLastYear\", each Number.Round(_, 0), type number}}),\n\t&#160; ReorderCols = Table.ReorderColumns(RoundSalesLastYear,{\"RepID\", \"FullName\", \"City\", \n\t&#160;&#160;&#160; \"StateProvince\", \"CountryRegion\", \"Territory\", \n\t&#160;&#160;&#160; \"TerritoryGroup\", \"SalesLastYear\", \"SalesYTD\"}),\n\t&#160;\n\t&#160; \/\/calculate last year's average\n\t&#160; LastYearAvg = List.Average(Table.Column(ReorderCols, \"SalesLastYear\")),\n\t&#160; SalesStatus = Table.AddColumn(ReorderCols, \"SalesStatus\", each \n\t&#160;&#160;&#160; if [SalesLastYear] = 0 and [SalesYTD] &gt; LastYearAvg\/2\n\t&#160;&#160;&#160;&#160;&#160; then \"Great start\"\n\t&#160;&#160;&#160; else if [SalesYTD] &gt; LastYearAvg * 3\n\t&#160;&#160;&#160;&#160;&#160; then \"Top performer\"\n\t&#160;&#160;&#160; else if [SalesYTD] &gt; LastYearAvg * 2\n\t&#160;&#160;&#160;&#160;&#160; then \"Excellent showing\"\n\t&#160;&#160;&#160; else if [SalesYTD] &gt; LastYearAvg\n\t&#160;&#160;&#160;&#160;&#160; then \"Better than average\"\n\t&#160;&#160;&#160; else \"Slacker\")\n\tin\n\t&#160;&#160;&#160; SalesStatus\n\t<\/pre>\n<p>The new  section of code starts with a comment, as indicated by the double forward  slashes. I threw this in just to show you that PQFL supports one-line comments  like this and multi-line comments that follow T-SQL conventions (\/* and *\/).  <\/p>\n<p>Next, we  define a variable whose sole purpose is to capture the average amount of sales  from the <b>SalesLastYear<\/b> column. To get  the average, we add the <b>LastYearAvg<\/b>  variable and start the definition with the \t<b>List.Average<\/b> function. We&#8217;re using a \t<b>List<\/b> function because the \t<b>Table<\/b> object doesn&#8217;t support an \t<b>Average<\/b> function. The \t<b>List.Average<\/b> function takes one  argument, which itself calls the <b>Table.Column<\/b> function. The  \t<b>Table.Column<\/b> function merely retrieves the \t<b>SalesLastYear<\/b> values from the \t<b>ReorderCols<\/b> table so that the \t<b>List.Average<\/b> function can calculate  the average of those values. On my system, the average comes to 1691854.5446.<\/p>\n<p>We can now  use the <b>LastYearAvg<\/b> variable in  subsequent expression steps as part of our calculations. The next step, then,  defines the <b>SalesStatus<\/b> variable,  which uses the <b>Table.AddColumn<\/b>  function to insert a custom column into the data set. Notice that the function&#8217;s  first argument refers to the <b>ReorderCols<\/b>  variable, not the <b>LastYearAvg<\/b>  variable. We want to return to our main data set and work from there.<\/p>\n<p>The \t<b>SalesStatus<\/b> definition is similar to  the <b>RepName<\/b> definition, except that  the embedded <b>if<\/b> expressions are  somewhat more involved with <b>SalesStatus<\/b>.  What we&#8217;re trying to do here is assign values to the new column based on how  each <b>SalesYTD<\/b> value compares to the  average sales for the previous year, as determined by the \t<b>LastYearAvg<\/b> variable. (PQFL doesn&#8217;t support anything comparable to a \t<b>CASE<\/b> expression.) Essentially, we&#8217;re  just adding an <b>if<\/b> expression to the \t<b>else<\/b> clause of the previous \t<b>if<\/b> expression.<\/p>\n<p>The logic in  these <b>if <\/b>expressions is fairly  straightforward. A value is assigned to the column based on how it evaluates  against last year&#8217;s average. Your final data set should now look similar to  what&#8217;s shown in the following figure.<\/p>\n<p> \t \t<img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/2285-img3F.jpg\" alt=\"2285-img3F.jpg\" \/><\/p>\n<p>The main  point to take out of this last transformation is that you don&#8217;t have to stick  strictly to the formula of defining one variable after the other and using that  variable in the next step. The steps must still proceed lineally, so you have to  account for that, but you also have room to play.<\/p>\n<h1>Creating visualizations<\/h1>\n<p>Once you  have applied and saved your data set, you can use it just like any other data  set. For example, I created the following visualization based on the \t<b>FullName<\/b>, \t<b>SalesYTD<\/b>, and <b>SalesStatus<\/b>  columns.<\/p>\n<p> \t \t<img loading=\"lazy\" decoding=\"async\" height=\"419\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/2285-clip_image010.gif\" width=\"623\" alt=\"2285-clip_image010.gif\" \/><\/p>\n<p>A simple,  yet telling visualization, which is exactly what a visualization is supposed to  achieve. And this is only the beginning of what you can do with PQFL in Power BI  Desktop. You can retrieve data from a wide range of data sources, work with any  of the object types in a single query, and apply an assortment of  transformations. You can even use the UI component to do some of your  transformations and fill in the rest directly in your PQFL query.  <\/p>\n<p>The biggest  stumbling blocks you&#8217;ll likely run into are the lack of concise documentation in  many areas and the inadequate script editor. However, once you have a solid  foundation of how the PQFL pieces fit together, you&#8217;ll be amazed at what you can  do with what you&#8217;ve got.<\/p>\n<\/p><\/div>\n","protected":false},"excerpt":{"rendered":"<p>The Power Query Formula Language (PQFL) is a functional language that drives the Power BI transformations, and allows you to create mashup queries from scratch. Rob demonstrates how to use it in Power BI Desktop to extract data from its source, filter rows, specify the columns, clean the data, and create visualisations.&hellip;<\/p>\n","protected":false},"author":221841,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143528],"tags":[4178,4379,4179,4150],"coauthors":[],"class_list":["post-2090","post","type-post","status-publish","format-standard","hentry","category-bi-sql-server","tag-bi","tag-reporting-services","tag-source-control","tag-sql"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/2090","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/221841"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=2090"}],"version-history":[{"count":6,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/2090\/revisions"}],"predecessor-version":[{"id":91005,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/2090\/revisions\/91005"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=2090"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=2090"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=2090"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=2090"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}