Pass Parameters to SQL Queries

One of the questions I get quite frequently is how we can pass parameters to SQL queries, allowing us to make them dynamic. Someone asked a question in the forum on the topic, so that finally inspired me to write it up.

Before you do this…

There are a lot of reasons that you should NOT go this route.

Power Query is designed to take advantage of query folding when targeted against a database.  While this isn’t technically correct, you can look at query folding like this… As you connect to the database and start filtering and grouping via the user interface, Power Query passes these commands to the database to execute.  This means that the majority of the heavy lifting is done at the database, not your workstation.  This continues to occur until you hit a command that the database doesn’t know, at which point it returns the data to Power Query, and the rest is done in your instance of Excel.

Why is this an issue?  Dynamic parameters use custom lines of code, which can’t be folder.  In the case of the poster’s question, this means that he just asked to bring 40 million records into Excel, rather than filtering them down on the server.  Take a guess as to which one is faster?

But what if I want to Pass Parameters to SQL Queries dynamically?

Listen, I totally get it.  I love dynamic queries too.  And anyone who knows me is aware that I’m super obsessed with making my data tables as short and narrow as possible.  This almost always involves filtering based on parameters that the user needs to pass at run time.  So are we dead in the water?  No, not by a long shot.

How to Pass Parameters to SQL Queries – Method 1

I’m a big fan of query folding, and would encourage you to use it wherever possible.  In order to do that, you should use the user interface to connect to the database and drive as many filter, sort and group by operations as you possibly can.  The goal is to push as much work to the server as possible, resulting in the narrowest and shortest data table that you can accomplish.  Once you have that, land it in a Connection Only query.  And from there use your dynamic parameters to filter it down further to get just what you need.

I have no idea what was in the 40 million row data set that the user was working with, but let’s assume it was 40 years of data related to 4 divisions and 30 departments.  Assume that our user (let’s call him Mark) wants to bring in the last 2 years data, is focussing only on the Pacific division, and wants to give the user choice over which department they need to work with.  For ease of assumption, we’ll assume that each year is 1/40 of the annual record load and each division provides 1/4 of the total records.  (Yes, I’m aware that nothing is that easy… this is an illustration of concept only!)

The recommended steps would be to do this:

  • Create the Staging query
    • Connect to the raw database table
    • Filter to only the Pacific Division – Server reduces 40m to 10m records
    • Filter to only the 2 years of data – Server reduces 10m to 500k records (10/40*2)
    • Land this output into a staging query – 500k records total
  • Create the parameter table and the fnGetParameter query
  • Create a query that references the Staging query and filters the department to the one pulled via the fnGetParameter query

That should take as much advantage as possible, and means that Power Query only needs to run the processing of 500k records against our dynamic criteria.

Where Method 1 breaks down

But what if the data set is still too big?  What if you need to parameterize the Division, the Date Range and the Department?  In order to avoid issues from the formula firewall, you would have to do this:

  • Create the Staging query
    • Connect to the raw database table
  • Create the parameter table and the fnGetParameter query
  • Create a query that references the Staging query and…
    • Collects the Division, Date and Department variables
    • Filters to only the Pacific Division
    • Filters to only the 2 years of data
    • Filters to only the selected department

Seems about the same, doesn’t it?  Except that this time we can’t take advantage of query folding.  To pass a parameter to the database, we have to separate it from the parameters in order to avoid the formula firewall.  This means that we break query folding.  And this means that Power Query needs to pull in all 40 million records, and process them.  Not the server, your Excel instance.

I don’t know how much RAM you have (and don’t care unless you’re on 64 bit), or how many processor cores you have (as Power Query is single threaded), you’re in for a LOOONNNGGG wait… if it doesn’t just tip over on you.

So how do we fully parameterize this stuff?

How to Pass Parameters to SQL Queries – Method 2

The good news that it can be done, the bad news is that you need:

  1. SQL Skills, and
  2. An adjustment to the default Power Query load behaviour

Let’s talk SQL Skills

The reason you need SQL skills is that you need to be able to write the most efficient query you possibly can, and pass this into the query when you connect to the database. (Thos italics, as you’ll see, are key.)  So, let’s assume that I want to connect to the old AdventureWorks database and pull records from the Sales.SalesOrderHeader table where CustomerID = 29825.  You need to be able to write this:

SELECT * FROM Sales.SalesOrderHeader WHERE CustomerID='29825'

Why?  Because you need to include that query when you’re building/testing your code.  It goes in the advanced options, below:

image

(You may need to trust the Native Database Query to get to the next step.)

So that created the following code for me:

let
Source = Sql.Database("azuredb.powerqueryworkshop.com", "AdventureWorks2012", [Query="SELECT * FROM Sales.SalesOrderHeader WHERE CustomerID='29825'"])
in
Source

Modifying the hard coded query to use dynamic parameters

I then set about fully parameterizing my query using the fnGetParameter query detailed here, and the table shown below:

image

Here’s the code I cobbled together to do this:

let
//Pull in a values from the parameter table
dbURL = fnGetParameter("Database URL"),
dbName = fnGetParameter("Database Name"),
dbTable = fnGetParameter("Database Table"),
sFilterField = fnGetParameter("Filter Field"),
sFieldValue = Text.From(fnGetParameter("Field Value")),

//Create the query
dbQuery = "Select * FROM " & dbTable & " WHERE " & sFilterField & "='" & sFieldValue & "'",

//Get the data
Source = Sql.Database(dbURL,dbName,[Query=dbQuery])
in
Source

 

I won’t go through the fnGetParameter function, as it’s quite well detailed in the blog post, but the key to understand here is that we are pulling a variety of items from the worksheet table, and putting them all together to feed the line starting with dbQuery.  This line dynamically sources the database path, database name and the SQL query.  Wouldn’t this be heaven if it worked?

Here comes the Formula Firewall again…

But no:

image

Now if that’s not enough to make you scream… and here’s the issue…

What I really wanted to do was create a power query that connects to the database without declaring the SQL query up front.  Something like this:

Source = Sql.Database("azuredb.powerqueryworkshop.com", "AdventureWorks2012")

My thought is that I could load this as a connection only query, then reference it and add the dynamic query afterwards.  Unfortunately, I got nowhere.  I’m not saying it can’t be done, but I couldn’t figure this out.  It seems that the only way to pass a query to the database is to pass it during the initial connection.  But doing so, by its very nature, violates the formula firewall.

So how can we get past it?

Bypassing the Formula Firewall

Yes, we can make this work.  But doing so involves turning off Privacy settings.  I’m not generally a big fan of turning off firewalls, but this one just aggravates me to no end. Normally we turn this off to avoid prompting messages.  In this case it just flat out prevents us from running.  So let’s kill it:

  • Go to Query Settings –> Privacy
  • Select Ignore the Privacy Levels and potentially improve performance

And at this point you’ll find out that it works beautifully… for you.  You’ll most likely need to have anyone else who uses the file set the above option as well.

Security Settings

The security setting we changed above is workbook specific.  Now I’m not recommending you do this, but if you get tired of the privacy settings overall, you can turn them off for all workbooks in the Query Options –> Security tab.  The second arrow is pointing at the relevant option:

image

And how about that?  Check out the first option… that one lets you avoid prompting about Native Database Queries.  (I seriously have to question why a SELECT query should trip a warning.)

Sample Workbook

Yes, I’ve got one, which you can download here, but it comes with a catch…

I’ve set it up to talk to a SQL Server in order to demo this, as it’s the big databases with query folding that cause us this issue.  You might notice that not too many people provide free access to a SQL server since they cost money to run.  For this reason, you need a login and password to refresh the solution and really see it work.

As it happens, if you’ve bought a copy of M is for Data Monkey, or attended our PowerQuery.Training workshop, then you’ve already got it the credentials. (They are on page 66 of the book.)  And if you haven’t… what are you waiting for?  🙂

Excel 2016: A fix and a new bug

Well, there’s good news and bad this time.  I just updated my Excel 2016 to 16.0.6868.2048 (First release version) and there is a fix and a new bug evident.

First, the fix

As I reported a couple of weeks ago, there was a new bug introduced in the April 2016 Excel update, as related to Power Query.

… if you are sourcing from a named range that doesn’t have an equal offset of rows/column.  I.e. if your source range doesn’t start in A1, B2, C3, D4, etc… then it pulls the wrong range.  Tables are fine, named ranges are the issue…

The issue was reported, fixed, and build 16.0.6868.2048 (which I finally got today) has fixed the issue.

I have to say that this is pretty cool.  Even though I was frustrated having to wait 2 weeks for a fix, the fact that it was only 2 weeks is pretty darned amazing.  In past cycles, this would have been several years until a new build of Excel came out. So even though we see new bugs, we also need to recognize that the team is working very hard to try and be responsive to them and get the fixes pushed VERY quickly.

Update: As of version 16.0.6868.2067 I can no longer reproduce this bug.  So that's more good news!

Unfortunately, a new bug

I’m not actually sure if this is new in a more recent update, or if it was there in the previous build and I just didn’t notice.  (While I do check updates almost daily, I don’t actually use every feature of Power Query every day.)  While this bug doesn’t prevent you from using your models, it is pretty irritating… Since I know this was a pretty major contention point for many in the past, I figured I should talk about it.

Once again, this is Excel 2016 specific, and doesn’t affect Excel 2010/2013.

So let’s assume you have some data as shown below:

SNAGHTML158303b7

Here’s what’s going on with these tables:

  • The blue table is our raw data.
  • The green table is a simple query that imports the original data, and sets the first column to a Date data type, then loads it to the table.  (Nothing fancy, it’s just that simple.)
  • The orange table was created by right clicking the green table’s query and choosing “Duplicate”, then loading it to the worksheet.  It is an EXACT copy of the query that leads to the green table.

Make sense so far?  Now, let’s add a couple of rows to the blue table, then hit refresh all.  What you should expect to get is this:

SNAGHTML1591b06d

What you actually get is this:

SNAGHTML1590e741[4]

Check out the green table… those dates are pretty impressively formatted as serial numbers, not dates.  But yet, the orange table – an exact duplicate – is fine.  Huh?  Doesn’t this feel like a throw-back to early Power Query days, where tables didn’t hold the formatting properly?

Here’s the best we have from a temporary workaround point of view (courtesy of the Excel/Power Query team):

  • Select the green table –> Table Tools –> Design –> Properties
  • Un-check the Preserve Cell Formatting box

image

When we refresh now, we get…

 

SNAGHTML1591b06d[5]

And let’s just add some more data, then refresh again to make sure it sticks…

SNAGHTML15953acd

Stellar!  The number formats have remained, but the table style formatting has changed to a different one.  Ugh.

Now, it IS still a table.  But unfortunately the style and the number formats all seem to be controlled by that one selection.  So until they fix this, it appears that you can either have your tables pretty, or you can have your number formatting correct.

Or maybe you can create your query, immediately duplicate it for your reports, then delete the original, as the second one seems to behave properly.  (I have no idea why this is.)

Final thoughts on “a fix and a new bug”

The subscription model is a new thing for us, and personally, I’m pretty high on it, despite these kinds of issues.  My hope is that – with the connections I have at Microsoft – that I’m in the first ring of testers, and can get this stuff fixed before it hits you.  I’d highly suggest you also have one person in your company in the “First Release” program for this reason as well.

My understanding of this method is that the fixes we get into the First Release band are implemented before that version is shipped to the General Release band of users.  That’s a good thing, as the last thing we want to see is our end users having to experience two months of the first issue listed here!

With regards to the new bug, I’ve again reported this to the Power Query team.  They’re aware, and we are having some active dialog about it.  I know they are going to fix it, but I’m not sure how soon.  (I really hope it’s as quickly as the last one, as this is pretty visible!)

We need your votes!

Hey everyone, we need your votes to make a difference in Power Query and Power Pivot!  There are a couple of items in the uservoice forums that I’d like to bring your attention to, and hopefully entice you to vote them up.  The more votes we get, the easier it is for the program managers in the design teams to get the support to actually implement these features.  They ARE interested, they just need you to up-vote them to get them done.

Where we need your votes:

#1 - Add Intellisense to the M Editor

So the idea here is simple: Add Intellisense and better general editing capabilities in the Advanced Editor.  This would make a huge difference to those of us writing M code, and I’ve also suggested in the comments that this be extended to the Add Custom Column dialog.

What kills me on this is the signature of the original submitter:  “Software Engineer, Power BI Desktop”.  I don’t think I’ve ever seen a clearer case where they need our help to justify the budget to get this done.  Please go there and throw some votes on this.

Vote up the “Add Intellisense to the M Editor” idea.

#2 – Modification of the Power Pivot Field List experience

Back in November I posted a suggestion to improve the Pivot Table experience  which would benefit everyone, but especially Power Pivot users.  Full details of my suggestion can be found on the blog here, but the basic summary is this:  Allow the fields area to be collapsible in side by side view. This would make it WAY easier to rearrange fields by reducing unused whitespace.

I was really encourage to see Ashvini Sharma’s response which, paraphrased, says: “We want to do this too, so please get enough votes to help us justify it!”

Vote up the “Ability to collapse PivotTable field areas” idea.

My Ask To You

Please, take some time to throw some votes on these ideas, and encourage every other user you know to do the same.  It’s super easy to do, just go there, click the Vote button, assign as many as you want and verify you’re real with your email address.  (The only email I’ve ever received from this is when they confirmed a feature got implemented.)

Again, we need your votes.  Help us out!  I’d like to see both of these hit 500 votes in order to give Microsoft the justification they need to get these done.

Thanks!

April 2016 Power Query Update

The April 2016 Power Query Update was just released for Office 365 subscribers, and I can confirm that it is available to the First Release customers, as I’ve already got it installed.  (If you’re on a later branch it may be a bit longer.)  It’s also available for download for Excel 2010/2013 customers.

WARNING TO EXCEL 2016 USERS!!

If you read Power Query data from named ranges, I HIGHLY recommend that you avoid updating your software to the newest release right now if you can. The latest builds on the insider track have caused a rather large issue if you are sourcing from a named range that doesn’t have an equal offset of rows/column.  I.e. if your source range doesn’t start in A1, B2, C3, D4, etc… then it pulls the wrong range.  Tables are fine, named ranges are the issue.  Microsoft knows, has architected a fix, but it hasn’t been pushed out yet.  I’ll update this as soon as it has.

The problem is not an issue in Excel 2010/2013 running version 2.29.x or the current 2.30.x.  It is only affecting Excel 2016.

UPDATED 2016-04-21: I can confirm that this issue was fixed in Excel 2016 16.0.6868.2048.

What’s in the April 2016 Power Query update?

At first glance it doesn’t seem like a ton – only two that they are calling out – but I think that this will make a few people pretty happy.

ODBC Connectivity Improvement

The first is that they’ve added the ability to easily select from a list of a available DSN’s when you’re setting up a Power Query connection against an ODBC data source.  No new functionality there, but it saves you the headache of having to manually enter the connection string (which you can still do if needed.)

Get and Transform April 1

Image scooped from the Official Excel blog.

Ability to specify CSV column delimiters

We can now specify the type of delimiter for a CSV, including special characters:

image

 

Both small things, but should be quite impactful to the folks who need them.