Query referencing another query - excel

I can't execute a query in Power Query and the error that throws me is like:
Formula.Firewall: Query XXX references other queries or steps, so it may not directly access a data source. Please rebuild this data combination.
The code within this query is as below:
let
CallToFunction = myFunction,
#"Invoked Function" = CallToFunction(),
Source = Oracle.Database("myServer", [Query="SELECT * FROM myTable WHERE CustomerPK IN (" & #"Invoked Function" & ")"])
in
Source
myFunction is a function that uses a couple of other queries and eventually returns a string of primary keys that I can use to fill in the parenthesis of the WHERE clause of my SQL statement.
When I invoke the function alone it works correctly, so this must be an issue of how to call the function within the last query.
Any ideas?

You need to set the privacy settings of your data sources & workbook.
See https://support.office.com/en-ca/article/Privacy-levels-Power-Query-cc3ede4d-359e-4b28-bc72-9bee7900b540?ui=en-US&rs=en-CA&ad=CA

Related

Getting "Please rebuild this data combination" on a computer but not on another one

This is my first try at using the Power Query... I've build a "dynamic" query in which I can change the retrieved fields as well as the filtering fields and values to be used by the query.
It's working perfectly on my computer but as soon as I try to execute it on another computer, I get the "Please rebuild this data combination" error. I saw some post saying I'll have to kind of split my query but I have not been able to figure it out.
Here is what my 2 tables look like:
Condition and fields selection
and here is my Query with the error:
Query
This might not be very elegant, but it allow me, thru a VBA script, to generate the list of fields to be retrieved and to generate the condition to be used by the SQL.
Any idea why it's not working on the other computers or how to improved the solution I'm using?
Thank you!
Notes:
Hi, all my Privacy Level are already set to 'None'.
I've tried to parametrize my code but I can't figure how. The Where condition is dynamic: it could be Where Number = "1234" but in other condition, the where might be like: 'Where Assignee = "xyz"'.
Here is a simplified example of my code:
let
Source = Sql.Database("xxxx", "yyyy", [Query=
"Select network, testid
from CM3T1M1 "
& paramConditions[Conditions]{0} &
" "])
in
Source
rebuild query, Formula.Firewall
That's a feature to prevent prevent accidentally leaking data. You can change the privacy level to ignore it
See also: docs.microsoft/dataprivacyfirewall
Is the dynamic query inserting those cells into the SQL query ? Report Parameters are nice for letting the user change variables without having to re-edit the query.
Parameterized native SQL queries
from: https://blog.crossjoin.co.uk/2016/12/11/passing-parameters-to-sql-queries-with-value-nativequery-in-power-query-and-power-bi/
let
Source = Sql.Database("localhost", "Adventure Works DW"),
Test = Value.NativeQuery(
Source,
"SELECT * FROM DimDate
WHERE EnglishMonthName=#MonthName AND
EnglishDayNameOfWeek=#DayName",
[
MonthName = "March",
DayName = "Tuesday"
]
)
in
Test
Dynamic Power Query version of SQL Query
To dynamically generate this SQL Query
select NUMBER, REQUESTED_BY from SourceTable
where NUMBER = 404115
Table.SelectRows is your Where.
SelectColumns is your select
let
Source = ...,
filterByNum = 404115,
columnNames = {"NUMBER", "REQUESTED_BY"},
removedColumns = Table.SelectColumns(
Source, columnNames, MissingField.Error
),
// I used 'MissingField.Error' so you know right away
// if there's a typo or bug
// assuming you are comparing Source[NUMBER]
filteredTable = Table.SelectRows(
Source, each [NUMBER] = filterByNum
)
in
filteredTable

Powerquery - reference Excel cell value within SQL query?

I am trying to modify an excel worksheet that was given to me, with a connection to a SQL database that looks as follows:
select * from DB.AccountAssignments where Company_Code = '102'
How can I replace the static 102 value to reference a specific cell in Excel? For example, cell A1? The objective of course, being that when I change the 102 for a different value, the query will re-run without having to enter PowerQuery to edit the query it self each time.
NOTE: I found examples that involve filtering AFTER the query runs and pulls the data. However, the data source is HUGE so I need the Company Code parameter to be embedded within the query, so that the data brought into Powerquery is already filtered. That makes the difference between the query taking 5 seconds to run to 5 minutes to run.
I spent a couple hours attempting instructions on older posts, but have not been successful so far.
I had the same problem and it took me some time to understand how to implement Sharif's answer. So here are more detailed instructions based on the same idea:
Convert your cell into a named range (Formulas > Define Name)
Select the cell and go to Data > Get Data > From Other Sources > From Table/Range, which opens the Power Query Editor
Right-click on your cell value in the little preview table in the editor and select 'Drill Down'
Go to Home/File > Close & Load To (the 'To' is important here!). An 'Import Data' window pops up. Select 'Only create connection'.
Now you have a query parameter, that can be used in any other query.
Open the power query editor for your SQL query (Edit)
User the parameter in your query:
Source = Odbc.Query("dsn=SQLDSN", "select * from DB.AccountAssignments where Company_Code = " & NamedRangeCompanyCodeFromCell )
I got the error Expression.Error: We cannot apply operator & to types Text and Number. I solved that by changing the type of my query parameter: In the query of my parameter under Applied Steps > Changed Type, I changed Int64.Type to type text
Most likely, you will also need to give permission to run this query
Instead of creating a separate query parameter, it can also be created on the fly within the SQL query:
Open the Power Query Editor of your SQL query (Edit)
Open the advanced editor (Home > Advanced Editor)
Do all the necessary steps here, e.g.:
let
Params = Excel.CurrentWorkbook(){[Name="myNamedRange"]}[Content],
#"Changed Type" = Table.TransformColumnTypes(Params,{{"Column1", type text}}),
CodeFromCell = #"Changed Type"{0}[Column1],
Source = Odbc.Query("dsn=SQLDSN", "select * from DB.AccountAssignments where Company_Code = " & CodeFromCell)
in
Source
Convert cell A1 to Table from Range (Insert -> Table)
Name the table as param (or anything you like)
Use the below lines to use this table in your power query:
params = Excel.CurrentWorkbook(){[Name="params"]}[Content],
code = params{0}[value],
Use this in your SQL query:
select * from DB.AccountAssignments where Company_Code = code
Most likely you will have something similar to below line in your power query editor:
Source = Odbc.Query("dsn=SQLDSN", "select * from DB.AccountAssignments where Company_Code = " & code ),

How to pass parameterized value to OData feed in Excel Power Query

I have created a OData service which requires a filter.
This OData service is to be accessed from OData Feed option in Micorsoft Excel - Power Query.
OData URL -:
http://176.0.11.79:8000/sap/opu/odata/sap/Z_SALES_REPORT_TUBES_SRV/et_sales_report_tubesSet?$filter=
Spmon eq '20161101'
Now I need to pass the filter value of Spmon '20161101' as a parameter. This value is present in a different sheet in the same excel.
How to change the Query to allow the data to be passed from sheet rather than changing the URL every time.
Power Query will fold filters for OData, so you can use the autofilter or add the filter step yourself by adding a new step and adding the following formula through the formula bar:
= Table.SelectRows(PreviousStep, each [Spmon] = '20161101')
If it's from a different sheet that you loaded in a query SheetQuery, it will look like:
= Table.SelectRows(PreviousStep, each [Spmon] = SheetQuery{row_index}[column_name])
You will likely need to set the privacy levels for the OData source and worksheet, or you need to disable Privacy Levels through the Options dialog.
You can reference any cell in excel using the Excel.CurrentWorkbook function.
I prefer to name the cells and refer to them like below.
Then I build the uri.
I usually prefer to specify and edit the filters in excel as its easier to change that the mquery code. Technically the below should check if columns_1 and filters_1 are blank and not include in the URI.
company = Excel.CurrentWorkbook(){[Name="company_1"]}[Content][Column1]{0}
, service = Excel.CurrentWorkbook(){[Name="report_1"]}[Content][Column1]{0}
, columns_value = Excel.CurrentWorkbook(){[Name="columns_1"]}[Content][Column1]{0}
, filter_value = Excel.CurrentWorkbook(){[Name="filter_1"]}[Content][Column1]{0}
, selected_columns = "$select=" & columns_value
, filters = "&$filter=" & filter_value
, uri = "https://api.businesscentral.dynamics.com/v2.0/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/Production/ODataV4/Company('" & company & "')/"& service &"?" & selected_columns & filters
, Source = OData.Feed(uri, null, [Implementation="2.0"] )
I have found an alternate way of passing filters in Power Query (not directly from any sheet)
This was achieved by adding one more source in the Query and using this as variable in the OData Feed.
Spmon = "20161001" ,
Source = OData.Feed("http://176.0.11.79:8000/sap/opu/odata/sap/Z_SALES_REPORT_TUBES_SRV/
et_sales_report_tubesSet?$filter= Spmon eq '" & Spmon & "'")
This variable (Spmon) can be edited directly in the Advance Editor (Power Query) directly.

Excel Will not Import a Table From Access

I'm Using Excel/Access 2007. I made an Access query in Access, and I tried to import it into Excel using The Data Tab -> Get External Data Subtab -> From Access. I chose my Database, and chose the Query I wanted to import. However, only the headers of the table as well as 2 blank lines show up (as evidenced by their being formatted as a table). There is definitely data that is returned in the query, that I can verify by checking Access. Any help would be greatly appreciated
As an aside, can this be done programatically?
EDIT: Here is the SQL query in all it's glory
SELECT [Meter#], [LDC#], [ESCO#], [Brand], [LDCName], [RateClass], [RateSubClass], [CustName],
[DemandZone], [Type], dbo_Forecasts.Name AS ForecasForecastType,
Min(IntervalMeterConsumption.[DateFrom]) AS ConsumptionStart, IntermediateLog.[MaxOfDateRead] AS ConsumptionEnd,
Sum([kWh])/(Sum([Interval])/365) AS AverageAnnual
FROM (IntermediateLog
INNER JOIN (
(Premise INNER JOIN Meters ON Premise.PremiseCt = Meters.PremiseCt)
INNER JOIN IntervalMeterConsumption
ON Meters.Meterid = IntervalMeterConsumption.MeterID)
ON IntermediateLog.[LDC#] = Premise.CustomerPremiseNo)
INNER JOIN dbo_Forecasts ON Meters.ForecastID = dbo_Forecasts.ForecastID
WHERE ((([MaxOfDateRead]-[DateFrom])<=380))
GROUP BY IntermediateLog.[Meter#], IntermediateLog.[LDC#], IntermediateLog.[ESCO#],
IntermediateLog.Brand, IntermediateLog.LDCName, IntermediateLog.RateClass,
IntermediateLog.RateSubClass, IntermediateLog.CustName, IntermediateLog.DemandZone,
IntermediateLog.Type, dbo_Forecasts.Name, IntermediateLog.MaxOfDateRead;
You could try removing the where clause just temporarily to see if that makes a difference.
When you run the query in Access does it ask for a parameter?
The code to export a query to Excel from Access is as follows:
DoCmd.TransferSpreadsheet acExport, acSpreadsheetTypeExcel12, "Query/Table Name", "c:\export.xls"
thanks Mark

Calling an SQL function from a Subsonic.Select

I asked the following question on the subsonic forum, but only seemed to get one response, so I thought I'd post up here as well to see if anyone could shed some more light on the problem...
I wish to create the following SQL statement through SubSonic using the Select tool (or Query tool) .. it uses a custom function called "SPLIT()":
SELECT * FROM VwPropertyList
WHERE VwPropertyList.idCreatedBy = 123
AND VwPropertyList.idCounty = 45
AND 29 IN (SELECT Item FROM SPLIT(DistrictGroupList, ','))
(the last part of this SQL uses the SPLIT function)
My subsonic equivalent looks like the following...
Dim mySelect As New SubSonic.Select
mySelect.From(VwPropertyList.Schema)
mySelect.Where(VwPropertyList.Columns.IdCreatedBy).IsEqualTo(123)
mySelect.And(VwPropertyList.Columns.IdCounty).IsEqualTo(45)
mySelect.And(29).In(New SubSonic.Select("Item").From("SPLIT("
&
VwPropertyList.Columns.DistrictGroupList
& ", ',')"))
This doesn't work though due to the last part .. how can I add "AND 29 IN (SELECT Item FROM SPLIT(DistrictGroupList, ','))" into my Subsonic.Select ?
The response I got from the subsonic forum suggested I do away with Subsonic.Select and replace with hard-coded InlineQuery() statements .. like:
Dim SQL as String = "Select " &
VwPropertyList.Columns.Item SQL = SQL
& " From " &
VwPropertyList.Schema.TableName SQL =
SQL & " Where " &
VwPropertyList.Columns.IdCreatedBy & "
= #CreatedBy " SQL = SQL & " And " & VwPropertyList.Columns.IdCounty & " =
#County " SQL = SQL & " And
#DistrictGroup IN (Select Item From
SPLIT(DistrictGroupList,',')"
Items =
SubSonic.InlineQuery().ExecuteTypedList(Of
MyItem)(SQL, 123,45,29)
I would prefer to use SubSonic.Select if possible though so that I can avail of the paging functionality etc.
Any ideas?
You could do John's suggestion or you could write the SQL using our InlineQuery - which allows you to write raw SQL and pass in params:
var qry=new InlineQuery("SELECT * FROM table WHERE column=#param",value)
You could try to use the original query object (pre 2.1) like so (untested, from memory):
Query q = new Query(VwPropertyList.Schema.TableName);
q.WHERE("29 IN (SELECT Item FROM SPLIT(DistrictGroupList, ','))");
// pass q.ExecuteReader() to the Load() method of your view.
I would suggest that you use the original Query object as you are looking to get paging. Inline Query does not have any methods that allow paging.
If you absolutely wanted to use Subsonic.Select you could mesh the two ideas and run an Inline Query to get the list of values and then use a Regular Subsonic.Select and pass the retrieved values to the select case but then you would be making two trips to the db.
On a side note I prefer reading Subsonic.Select statements written using the fluent interface that it is namely
SubSonic.Select.AllColumnsFrom()
.Where(VwPropertyList.Columns.IdCreatedBy).IsEqualTo(123)
.And(VwPropertyList.Columns.IdCounty).IsEqualTo(45)
.ExecuteAsCollection();
thanks for the responses.
I ended up doing InlineQuery and just re-wrote the paging code that's normally produced by Subsonic.Select ... not the best solution but it seems to work.
It would be good if I could have done something like this though:
Dim s As New SubSonic.Select
s.From(VwPropertyList.Schema)
sWhere(VwPropertyList.Columns.IdCreatedBy).IsEqualTo(123)
sAnd(VwPropertyList.Columns.IdCounty).IsEqualTo(45)
s.And(29).In(New
InlineQuery("(SELECT Item FROM
SPLIT(DistrictGroupList, ','))"))

Resources