Showing posts with label Analysis Services. Show all posts
Showing posts with label Analysis Services. Show all posts

Friday, 4 September 2015

SSIS - SSAS as Data Source - MDX Query Slow (Because it’s Executed Twice!)

Anyone that’s tried using SSAS as a data source (whether in an ETL tool or a data dump using TSQL openrowset and/or linked servers) might have noticed that longer queries seem to run for twice as long when compared to MDX executed directly against the cube in SSMS.

The reason for this is the database engine and SSIS need to know the ‘shape’ of the dataset. This is fine for most datasets because a relational query has its schema defined by the underlying data and a ‘get schema’ command to the data source returns the dataset metadata only.

The nature of mdx is such that the schema of the resultset could be driven by the data itself (eg NON EMPTY could result in columns being excluded) and is not known until the query is returned. So when the SSAS server gets a connection and query that’s only requesting the metadata it executes the query (to define the metadata) and returns that. A split second later the command for the data comes in and SSAS obliges and runs the query again.

Using a linked server or SSIS (with an oledb datasource pointing to an SSAS cube) to execute a query against SSAS results in the query running twice. Once to return the metadata header and again to return the data.

From the Profiler:

ssis two query

This is totally unnecessary for SSIS because the data flow already has the schema defined, and if the returned dataset is different then the Data Flow Task will fail during execution anyway. Setting Delay Validation = true and ValidateExternalMetadata=false makes no difference. The MDX query is still executed twice.

In most cases this is fine as queries are answered fairly quickly, and caching etc would probably help with the same query running in quick succession. But in some cases (like mine! - when the query is a very complex calculation driven leaf level query that can take minutes to hours to execute), the query taking twice as long just isn’t going to cut it.

Connecting to SSAS

There are two ways to connect to SSAS from SSIS using the built in data flow source connectors - OLEDB and ADO.NET

What’s Actually Happening?

OLEDB

The ‘Query Start’ event of SSIS executing a query against SSAS has some interesting additional properties. Comparing the two query executions (metadata and data) shows these differences:

olap oledb ssis

<Content>MetaData</Content> and <ExecutionMode>Prepare</ExecutionMode> are the culprits here.

Is there any way to stop this first one from running or forcing it to behave like the second one (so that it also returns the data)? No, none that I could find :(

I tried to use the Extended Properties area of the connection in SSIS to modify these two properties but had no luck. Setting Content to SchemaData resulted in an error because data isn’t compatible with execution mode prepare. There is no doco on these two connection properties anywhere and I’m not a good enough coder to dive into the DLLs to figure out what the valid options are.

Maybe ADO.NET will work better?

ADO.NET

The slower and less functional cousin of OLEDB (no fast load, native query from variable support) also suffers from firing the same query twice. Probably because it’s simply the same OLEDB SSAS driver wrapped up in ADO.NET (whatever that means!)

It however sends slightly different properties:

olap ado.net ssis

Notice how the metadata query is has fewer properties. The data query adds on <Content>SchemaData</Content> and <Format>Tabular</Format>.

Could simply adding Content=SchemaData and Format=Tabular to the extended properties of the ADO.NET connection be enough?

Yes! It is!

ssis single query

Conclusion

Unfortunately the easier to use OLEDB data flow connection double dips when extracting data. ADO.NET is the only other option if time is a factor.

Simply add the following to your ADO.NET Connection Manager under All|Advanced|Extended Properties to stop it from querying the cube twice

Content=SchemaData;Format=Tabular

ADO.NET Connection Properties

Enjoy!

Monday, 4 November 2013

How To: Change the Connection String for a Single Table in Tabular SSAS

 

Imagine you're building a small cube using the new tabular model in SQL Server 2012 Analysis Services based on adventure works. You want to add the factInternetSales, dimProduct and dimDate tables. You create a single sql server connection to myserver.AdventureWorksDW2012 and select those three tables.

You build up your model; add relationships, hierarchies, custom DAX and so on but then for some reason you need to change the source for just the DimProduct table to be from a different server and/or database. FactInternetSales and dimDate must remain linked to the existing "Original" data source. This is not possible through the GUI in SSDT at the moment. Changing the existing connection string by clicking edit will modify the data source for all 3 tables. The GUI option is to add the new table by creating a new connection, then deleting the existing dimProduct table (and all related objects such as relationships/measures/dax etc), then adding the new table and rebuilding all the deleted content. Not very modular or developer friendly! Imagine doing this a few years down the line on some existing 'legacy' tabular project without breaking existing functionality. And the table properties screen offers no option to change the connection used to source the data.

The connection Name is greyed out. Changing it unfortunately involves right clicking on model.bim and selecting 'view code' :(

Step 0:
Backup your .bim file!

Step 1:
Add a new data source connection that points to the new database, unfortunately you need to add at least one table to actually add the data source so do something simple like "select top 0 1 as col"

image

Step 2:
Right click on model.bim (or whatever you've called your cube) and choose View Code. Find the section for the dummy 'Query' table (it should be right at the end of the XML

<xs:element name="Query_896ee325-f4a2-406d-9ad4-c102f04775c6" msdata:Locale="" msprop:DataSourceID="6effa2f9-0319-4b8a-a25e-ffa4d8e64a59" msprop:IsLogical="True" msprop:FriendlyName="Query" msprop:DbTableName="Query" msprop:TableType="View" msprop:Description="Query" msprop:QueryDefinition="select top 0 1 col">

find and take note of the section in red. This is what links the table in the GUI to the "New" data source.

Step 3:
Find the equivalent section of XML for the table you want to redirect to the new connection.

<xs:element name="DimProduct_885addae-df05-4c3d-97ca-c234ca5553bc" msdata:Locale="" msprop:IsLogical="True" msprop:FriendlyName="DimProduct" msprop:DbSchemaName="dbo" msprop:DbTableName="DimProduct" msprop:TableType="View" msprop:Description="DimProduct" msprop:QueryDefinition="      SELECT [dbo].[DimProduct].*   FROM [dbo].[DimProduct] ">

You'll notice that this part doesn't have an msprop:DataSourceID section, this is because (I think) the first connection added to the model is the 'default' connection and the DataSourceID is inherited from a parent XML tag :

<DataSourceViews>
  <DataSourceView>
    <ID>Sandbox</ID>
    <Name>Sandbox</Name>
    <DataSourceID>4fa7958c-7b29-404e-a89c-bdcb75dbedd9</DataSourceID>
    <Schema>
      <xs:schema id="NewDataSet" xmlns="" xmlns:xs="
http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:msprop="urn:schemas-microsoft-com:xml-msprop">
        <xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:Locale="en-AU">
          <xs:complexType>
            <xs:choice minOccurs="0" maxOccurs="unbounded">

But that doesn't matter! If you modify

<xs:element name="DimProduct_885addae-df05-4c3d-97ca-c234ca5553bc" msdata:Locale="" msprop:IsLogical="True" msprop:FriendlyName="DimProduct" msprop:DbSchemaName="dbo" msprop:DbTableName="DimProduct" msprop:TableType="View" msprop:Description="DimProduct" msprop:QueryDefinition="      SELECT [dbo].[DimProduct].*   FROM [dbo].[DimProduct] ">

by adding the new DataSourceID

<xs:element name="DimProduct_885addae-df05-4c3d-97ca-c234ca5553bc" msdata:Locale="" msprop:DataSourceID="6effa2f9-0319-4b8a-a25e-ffa4d8e64a59" msprop:IsLogical="True" msprop:FriendlyName="DimProduct" msprop:DbSchemaName="dbo" msprop:DbTableName="DimProduct" msprop:TableType="View" msprop:Description="DimProduct" msprop:QueryDefinition="      SELECT [dbo].[DimProduct].*   FROM [dbo].[DimProduct] ">

then the table will use the new connection. Save the XML, open the model and open up the table properties

And there it is. The DimProduct table is now coming from the new connection.