Create Graphs automatically out of the excel generated via SSIS? - sql-server

I have the following requirement,
A SQL Server 2008 scheduled job that runs at 9 am in the morning. This job should send a spreadsheet with data in a workbook(workbook1) and the chart attached in the next workbook(workbook2). The data must have the values from a SQL table. The chart must reflect the values present in the workbook1. This sheet should be mailed across to n number of users. The n number of users are not even aware of the sql server and dont know anything about the username and password of the server. They must just have the spreadsheet with the two workbooks.
I have decided to do the below
1) Create a stored procedure that formats the data into a table 2) Invoke a SSIS , to copy the data into the workbook1 of excel 3) Create the Graph in the workbook2 of the same excel
The point 1 and 2, I have completed already.
After activity 1 and 2, I will have something like below in the excel
Workbook1:
Date | Column A | Column B | Column C |
10-7-14 | 0983883 | 09433344 | 4443333 |
11-7-14 | 0986444 | 06875544 | 4689073 |
I am not really able to do the activity 3. Activity 3 must have take in the values from the above table and create graphs in workbook2
I know it can be done with SSRS, but I would like to know other ways as we have only SSIS with us and SSRS is totally out of scope. For some reasons SSRS is restricted to use in our systems.
I have tried various ways to do this. (created a dynamic graph in workbook2 of the excel template for SSIS and let the values transferred to workbook1 renders the graph but it does not work sadly.). I am not able to create dynamic graphs with the empty excel templates that I feed to SSIS.
Excel is the problem here, it does not allows any pre-defined graphs in it. It always expects a value to create a graphs.
I can also go for a VBA macro option,(like creating a button and let the user click the button to generate graphs) but I am not really sure if it is a feasible one?
Excel experts please help !
I might like sound like an amateur but please pardon me as I am new to SSIS and none of my colleagues have an idea on how to do it and I could not get anything with the research I have done, which is a bit frustrating though.

As I don't know the specifics of your workbook structure you might need to adapt the following vba code (as the sub name suggests this should be placed in the Open event of the workbook):
Private Sub Workbook_Open()
Dim wb As Workbook
Dim ws As Worksheet
Dim ws2 As Worksheet
Dim LastRow As Long
Dim LastColumn As Long
Dim LastColumnLetter As String
Set wb = ThisWorkbook
Set ws = wb.Worksheets("Sheet1")
Set ws2 = wb.Worksheets("Sheet2")
Application.ScreenUpdating = False
LastRow = ws.Cells(Rows.Count, "A").End(xlUp).Row
LastColumn = ws.Cells(2, Columns.Count).End(xlToLeft).Column
LastColumnLetter = Chr(LastColumn + 64)
ws2.Activate
ws2.ChartObjects("Chart 1").Activate
ActiveChart.SetSourceData Source:=ws.Range("A1:" & LastColumnLetter & LastRow)
ws2.Range("A1").Select
Application.ScreenUpdating = True
End Sub
This code executes when the workbook is opened and first tries to find the range your data table is occupying and then updates the Data Source for the Chart. I have assumed that your data are in Sheet1 and start at cell A1 while your chart (Chart 1) is in Sheet2.

I do something similar for work but am using excel as a front end to SSAS but the concept is similar for sql server.
It sounds like you would find a pivot chart in excel useful. This is due to the fact that excels dynamically renders the chart based on the results of the pivot table.
Note that the pivot table allows for filtering so this means that the user can filter out parts of the data they do not need. Or if the user wants to alter how the chart looks, e.g. sales by month instead of sales by quarter it is extremely easy for them to do. So basically the presentaton of the results are being done dynamically in excel.
To create one based off external data you do the following:
Data --> from other sources
It is possible for excel to query sql server, though you probably want to limit the result set to a small read-only table for reporting.
Note that the data source information is saved into the file, with excel being able to use both user name and password hard coded in or the window's domain logins. Ideally you want to use the domain login over a general username and password
So the solution becomes more like the following:
SP or SSIS to populate a table for excel to query. This means excel is just writing simple selects and group bys
Using Data --> from other sources --> from SQL server
Create the stock pivot table, which will handle the presentation of the data
Give file to users. The file would have the defined datasource in it
They then just hit refresh which will requery the server for the results.
User is able to drag and drop fields, which updates the pivot table and redraws the excel chart
The only downside being that the user can only see a copy of the current dataset.

Related

Control Database form Excel through VBA

I am trying to create an excel addin which has set of Functions to pull value from the database (I use MS SQL Server). So my query will return only one record set. I use something like below in my vba code.
Using Excel VBA to run SQL query
But the problem with this is if I have my custom function in 100 cells, the macro makes connection to the DB everytime and retrive data from the DB.
Is there a way where I can make one connection and use that connection to write as many queries as I want?
Simple, run all your 100 function/ loops to access the database. One you've finished then close the connection. Look at your modified code below...
Option Explicit
Sub ConnectSqlServer()
Dim conn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim sConnString As String
' Create the connection string.
sConnString = "Provider=SQLOLEDB;Data Source=INSTANCE\SQLEXPRESS;" & _
"Initial Catalog=MyDatabaseName;" & _
"Integrated Security=SSPI;"
' Create the Connection and Recordset objects.
Set conn = New ADODB.Connection
Set rs = New ADODB.Recordset
' Open the connection and execute.
conn.Open sConnString
'>>>> run 100 loops
Dim i As Integer
For i = 1 To 100
Set rs = conn.Execute("SELECT * FROM Table" + 1 + ";")
' Check we have data.
If Not rs.EOF Then
' Transfer result.
' I assume that you've 100 Sheets
Sheets(i).Range("A1").CopyFromRecordset rs
' Close the recordset
rs.Close
Else
MsgBox "Error: No records returned.", vbCritical
End If
Next
' Clean up
If CBool(conn.State And adStateOpen) Then conn.Close
Set conn = Nothing
Set rs = Nothing
End Sub
I've add 100 loops and run it before the connection to database is closed.
Hope it useful.
In this kind of cases do not do db operations in loops. This is time consuming and not proper useage. Instead, in a loop create select, insert or whatsoever statement and then complate the loop operations and calculations then open the connection just one time and send request (created sql script) to db and get response from db then close connection. Thats all. db operations must be sparated from dailiy and recursive opeations.(antipattern) best regards.
Disclaimer: while this is not a direct solution to the problem described in the post I would like to add this approach as a much faster and easier solution to the problem described.
Step 1: create a (possibly hidden) sheet where you pull all the SQL data that you need in this Excel file. Pull the data into one table with all the necessary columns / dimensions to get the data afterwards from this table.
Here is what I mean by that. Let's assume that you need in this Excel file some data from the table Users on the SQL server as well as some information from a StockMarket table on the server. From the table Users you want the UserID, the first name, last name, and the job title. From the table StockMarket you will need the Stockmarket ID and the price for this particular share. As these prices are by date you also need the the quote date for the price.
Now, since you want all in one table you must think of a smart way to combine all this data into one table. One approach could be the following:
Afterwards you can get all the data from the above table with lookup functions like these:
=INDEX(SQLdata,MATCH(1,(SQLdata[Table]="Users")*(SQLdata[UserID]=25),0),4)
Note, that I named the table SQLdata to make it easier when looking at the formula and understanding it. Also, like this you can easily scan your Excel file for any reference to this table.
Another approach could be the following to make the table more concise:
Note, that this time I am mixing Strings with Numbers and also Strings with Dates (which is very poor design and for some people here even impossible to think of). Also, the column headers are now less descriptive. Yet, this works too:
=INDEX(SQLrev,MATCH(1,(SQLrev[Table]="Users")*(SQLrev[Dimension1]=25),0),5)
Note, that I called the table this time SQLrev.
Both solutions allow you also to aggregate data from the table. So, if you want (for example) the average price for Apple in 2017 then you can use the following formula to sum up the the quotes for this year and divide them by 3:
=SUM(IF("StockMarket"=SQLrev[Table];1;0)*IF("AAPL"=SQLrev[Dimension1];1;0)*SQLdata[Price])/3
The most notable advantage for this approach is that you merely have to update one table in the entire Excel file which means that there is only one SQL pull from the server.
The most notable disadvantage (apart from the SQL select which might get pretty complicated to write) is that you need to know of all the data that needs to reside in this table. If the data is not pulled into this table then none of the above formulas will be able to retrieve these values.
While this approach certainly has its downsides this is much easier to implement than the Excel AddIn you are aiming for.
All above formulas are array formulas and must be entered pressing Ctrl + Shift + Enter. For more information on array formulas please read the following: https://support.office.com/en-us/article/Guidelines-and-examples-of-array-formulas-7D94A64E-3FF3-4686-9372-ECFD5CAA57C7

VBA clear just pivot table cache, but leaving pivot table structure

How can I clear pivot table cache with VBA, but not destroy pivot table structure? My pivot table is connected to external data source. The SQL source determines which user should see which portion of the data. The source populates the table as the table is refreshed. I want to save the Excel file and distribute it with clean pivot table (no data inside).
As a result I want to get exactly this:
I have experimented around this code with no success. There is no such thing like PivotCaches.Clear in VBA.
Sub PT_cache_clear()
For Each pc In ActiveWorkbook.PivotCaches
pc.Clear
Next pc
End Sub
The only good solution I found is to refresh the table with a user which has access to SQL server source but is not allowed to see any single record of the data.
The idea:
ActiveSheet.PivotTables("PivotTable1").SaveData = False
seems not to lead to desired results.
The way I do it is to refresh with a query that will return the table structure but with 0 records. So if selecting from a view something like:
select top 0 *
from vw_MyPivotData
If using a stored procedure, you can send a parameter that ensures that no records will be returned such as a filter that you know doesn't exist in the data or a special parameter devised for the purpose of returning no records.
You cannot just clear the PivotCache without affecting the pivot table, they are inexorably linked. You can trick the PivotCache into loading and empty result set with the same structure/schema. Use the PivotTable.ChangeConnection function to switch the connection before closing the document.
If you have two external connections defined in your excel file. One that returns the correct data and another that returns the same structure but no rows. You can switch the connection to the no rows version and flush the cache that way. If there are any differences between the structure/schema of the connection resultset then excel will throw an error message.
Change the connection on demand, just before you distribute your file.
Sub PT_cache_clear()
'change connection'
Dim con as vartype
Set con = ActiveWorkbook.Connections("MyNoResultConnection")
Worksheets(1).PivotTables(1).ChangeConnection (con)
'refresh the pivot table cache'
Worksheets(1).PivotTables(1).PivotCache.Refresh
'clear the cache of any orphaned items'
Dim pc As PivotCache
Dim ws As Worksheet
With ActiveWorkbook
For Each pc In .PivotCaches
pc.MissingItemsLimit = xlMissingItemsNone
Next pc
End With
End Sub
Change to the good connection every time the sheet opens
Private Sub Workbook_Open()
Dim con as vartype
Set con = ActiveWorkbook.Connections("MyGoodResultConnection")
Worksheets(1).PivotTables(1).ChangeConnection (con)
Worksheets(1).PivotTables(1).PivotCache.Refresh
End Sub
You should only need to setup the MyNoResultConnection connection on your local PC since users will not be calling PT_cache_clear().
Update:
Instead of always changing the pivot table connection every time you could set it conditionally by getting the current connection name from the PivotCache.WorkbookConnection property and comparing the names.
Worksheets(1).PivotTables(1).PivotCache.WorkbookConnection
Note:
It is not possible to just flush or empty the pivot cache. You can call a refresh method or set it to refresh automatically when the file opens. You can clear the cache of orphaned items.
PivotTable.ChangeConnection and PivotCache.WorkbookConnection can only be used with external data sources.
Setting PivotTable.SaveData to false will not clear the pivot cache. This stops the data source from being saved it does not affect the PivotCache. The PivotCache lies between the data source and pivot table. When using external data sources this property will have no effect and for OLAP data sources it is always false.
See posts here and here for getting the current state of your PivotCache.
Alternative ways to implement this:
Create the connection object and pivot table with a VBA macro when the workbook opens. Delete the connection, pivot table before the workbook closes. The cache will then be automatically deleted when the workbook is saved. This approach can be difficult depending on how your external datasource is setup. It may require your to store username and passwords inside your VBA code.

SSAS (2008R2) & Excel Filter Issue When Refreshing Data

We have an instance of 2008 R2 which is running approx 6 cubes. The situation I am finding is as follows.
A user connects to the cube via MS Excel 2010 using the normal Data Sources method.
Lets say they create a very simple pivot table...Accounts on the rows & Months on the columns with a measure giving us the total value for each Account. At the time of the user creating the report there were 11 months of data in available in the cube (Jan > Nov) so the pivot table reflects this in Excel. The user has applies a few filters (say 5) on the row to select some Accounts ad hoc. The user then saves this file to his desktop and leaves it until Decembers data has become available in the cube. December is upon us and we refresh the cube so that it now contains Decembers data as well. The user reopens the Excel file and right clicks on the pivot and refreshes the data with the expectation of seeing a full years data for the five account selected but this is not the case. What in actual fact happens is that all the data which is there before is lost and the pivot table is completely blank. Can anyone help with why this is happening and a potential solution?
I have an idea in my mind that Excel is storing the accounts being filtered using some unique identifier and when the cube is reprocessed for the new month new unique IDs are being assigned to the accounts and therefore a match is not happening. There must be a work around to this as otherwise every report which is generated is effectively a throw away effort. Cheers!
In my case this indeed was caused by the poor cube design: the generated MDX referenced an ID that changed every time the cube was rebuilt:
WHERE [Log Author].[PM PRIMARYKEY].&[29872574],
I agree the best way to fix this is to change the ETL.
PS The way I viewed the generated MDX (thanks to Marco Russo) was by selecting the pivot and then running this macro (open VBA, paste it, hit F5):
Sub DisplayMDX()
Dim mdxQuery As String
Dim pvt As PivotTable
Dim ws As Worksheet
Set pvt = ActiveCell.PivotTable
mdxQuery = pvt.MDX
' Add a new worksheet.
Set ws = Worksheets.Add
ws.Range("A1") = mdxQuery
End Sub

Updating Access from Data yielded in Excel

I am trying to find the best way to extract report from Access into Excel and updating Access, once data has been yieled by Questionnaire participants.
The data in Access is quite complex and large (contained in a single master table - 50+ columns & 200K+ rows) - which doesn't make it easy on the eyes.
I would like to extract reports from Access (essentially a subset of data - M columns out of N). The report will be updated by individuals (i.e. values updated) and will need to be fed back into Access.
I'm looking for the most user - friendly approach to achieve the above. Does anyone have any ideas?
Ps obvious answers welcome, I am a complete Access/Excel noob - but have a background in MySQL, SQL Server.
As far as i'm aware, there is no single tool to do this, if you want to create your own, i recommend you to use any .NET language, ADO.NET and Interop to read data from and into Excel. If you don't feel you want to do some .NET programming, there is a reporting tool called DBxtra that take Excel, Access and many databases as input and let you export data to Excel (either static or dynamic) and other formats. The route will be like this:
1.- Create your query from your Access database in DBxtra
2.- Export the results into Excel (either linked or static sheet)
3.- Let yout users update the data in Excel
4.- Use Excel macros to update the data in the Access database.
To update the access database you will need to put a macro like this into in your Excel file:
Sub appProb()
On Error GoTo 1
Dim XLTable As TableDef
Set Db = OpenDatabase(mdbFile)
Set XLTable = Db.CreateTableDef("tblProbTemp")
XLTable.Connect = "Excel 5.0;DATABASE=" & xlsFile
XLTable.SourceTableName = "tblXLProb"
Db.TableDefs.Append XLTable
strSQL = "Insert into tblProb Select * FROM tblProbTemp"
Db.Execute strSQL
1:
Err.Clear
On Error Resume Next
Db.TableDefs.Delete "tblProbTemp"
Db.Close
End Sub
The only thing worth to note is that tblXLProb is named range.
Note: This macro was copied from here: http://www.mrexcel.com/forum/microsoft-access/51157-update-access-records-excel.html

Excel communicate with SQL Server to identify user.

Excel queries SQL Server. Trying to query using current user name. I have an Excel file that queries a view in SQL Server. The view is set to return the results based on the user querying the view.
I setup a file on a network server for others to access, hoping that when they opened the file, it would show only the information relating to them.
But the information still only refers to myself. I want it to show information the user using the file, as if they were querying the view directly.
Here is the sample query for the view:
Select * from dbo.WorkEstimate where Estimator = SUSER_NAME()
But the Excel sheet only returns the information relating to myself. I think it may have something to do with the connectstring that always specifies my workstation name. Is there a way to change that to reflect the individual (or their workstation) that is using the file?
The SUSER_NAME() (transact-sql) in the SQL statment refers to the current user within the context of the currently executing SQL statement. In your case, it is the userID you specified in your connection string. The sql server uses that user specified in your connectionstring to run that statement and thus your SUSER_NAME() reflect that user.
You will need to specify the user that the Excel is running under (Windows environment) with the following Excel functions:
Application.Username is the name of the User set in Excel Tools>Options (under the User Name box)
Environ("Username") is the name you registered for Windows see Control Panel >System
So, your SQL statement will have have to be something like:
vsSQL = "SELECT * FROM dbo.WorkEstimate WHERE (Estimator = '" & Environ("Username") & "');"
Hope this helps.
You don't need to have Excel communicate with SQL Server to figure that out. Excel needs to communicate with the user's machine.
Use this code to discover all of the environment variables that pertain to the user logged in to the machine. Look through the results to figure out what you need to pass in to your query.
Add a module to your Excel file and paste in the following:
Sub ListAllEnvironmentVariables()
Dim ws As Worksheet
Dim rng As Range
Set ws = Sheets("Sheet1")
Set rng = ws.Range("A1")
Dim i As Integer
i = 1
Do Until Environ$(i) = ""
rng.Offset(i - 1).Value = Environ(i)
i = i + 1
Loop
End Sub
I'm using Sheet1 in my example, but you can change that and set ws to any blank sheet in your workbook to see how it works. Obviously don't use Sheet1 if you have data on the sheet.

Resources