How to dynamically change server url withing http connection manager SSIS? - sql-server

I am fairly new to SSIS, Visual studio. Thought that might be good to mention in the beginning.
What I wanted to achieve was to download a certain xls file from http://www.ads-slo.org/statistika/ website and store it in a certain folder on my computer. I have achieved that, but the problem is that I know how to do it one file at a time. I did it by opening new connection, going to http connection and in the manager typing the server url: which in my case if lets say we start with January 2016 was this:http://www.ads-slo.org/media/xls/2016/Januar-2016.xls. After doing so I've constructed a script task or more or less copied it from a website that downloads the file given a certain url based on the connection manager.
My problem is that I would like to download all of the files on this site, so starting with January 2007 and ending with January 2016 with a single package and by not changing my connection manager server url settings 100 times.
Is there any way you might help me. I would be forever grateful.
Thank you in advance.
Kind regards, Domen

Here is one very simple example (it can be improved - see comments after code block) of changing a connection string dynamically by using a Script Task. You can also dynamically change connection strings using expressions and the Connection Manager's expressions property. However, since you are using a Script Task to handle the downloads, I have demonstrated it using one.
As you haven't tagged the Script language (VB or C#) you are using, I have written a rough draft in VB.
I have added comments, but stackoverflow syntax highlighting interprets it strangely; apologies.
Public Sub Main()
' Get the HTTP Connection
Dim connObj As Object = Dts.Connections("HTTP Connection Manager").AcquireConnection(Nothing)
Dim connection As New HttpClientConnection(connObj)
' Static list of month names for dynamic connection string (obviously add as many as needed)
Dim monthNames As String() = New String() {"Januar", "February", "March"}
' Nested loop - for each year and month, try to download the Excel file
For Y As Integer = 2007 To 2016 Step 1
For M As Integer = 0 To monthNames.Length - 1 Step 1
' Set the assumed name of the remote file
Dim remoteFileName As String = monthNames(M) + "-" + Y.ToString() + ".xls"
' Change the connection string a.k.a dynamic connection string
connection.ServerURL = "http://www.ads-slo.org/media/xls/" + Y.ToString() + "/" + remoteFileName
' Set where to download the file to
Dim localFileName As String = "C:\Temp\" + remoteFileName
Try
connection.DownloadFile(localFileName, True)
Dim buffer As Byte() = connection.DownloadData()
Dim data As String = Encoding.ASCII.GetString(buffer)
Catch E As Exception
' File may not exist on remote server, delete the blank copy it attempted to create
File.Delete(localFileName)
End Try
Next
Next
Dts.TaskResult = DTSExecResult.Success
End Sub
How can this be improved?
One potential improvement is to parse the remote server for the folders and directory contents (to save having static lists of month names, hardcoded start and end years and building file names) using a HttpWebRequest.
However, there might be an issue with the remote server permissions in allowing such requests to be made so you would have to investigate further and speak with the server administrator.
Testing the above code, it successfully downloaded the Januar-2015 and Januar-2016 Excel files from the website.

Related

With only the Select and View Definitions permission on a view, can SQL queries be sent from Excel without needing to type the query each time?

I have views that my users often only need to check for one particular person at a time. To do this, they do the following in Excel 365 desktop:
Open a blank workbook
Click on the 'Data' ribbon
Click 'Get Data'
Click 'From Database'
Click 'From SQL Server Database'
Fill in the 'Server' and 'Database' fields
In the advanced options, type SELECT * FROM [VEIWS].[VIEW_NAME] WHERE [EMP.ID] = '123456'
Click OK.
This is tedious for my users. If they want to check another person, they have to repeat the entire process. I'd love for them to just be able to use the query editor and change the only line that matters (see step 7), but they've only got the Select and View Definitions permission, which causes the query editor to complain. I'm afraid that I don't have the specific error message, but it's certainly to do with permissions.
Is there a less-repetitive way to do this from Excel? In an ideal world, I'd just make a sheet that lets them type in the EMP.ID immediately and then fetches the info. I think that it can be done with macros, but they're never my first choice and seem to require that I save passwords in the workbook.
Note that my users can't just fetch the entire view and filter it down in Excel. There are too many rows for Excel to handle.
I have no idea what permissions error you’re hitting, but people commonly use Windows credentials instead of Database credentials and get stuck. Power Query saves credentials on each computer, so you are relying on them signing in correctly. The first time someone connects to a data source, they are prompted for credentials. The default is for a Windows credential, and likely they need to enter a Database credential. If they get this wrong, they have to go into the Data Source settings to edit or clear the credential to fix it.
As far as changing the value in the SQL, you can easily have a parameter in Excel that changes the EMP.ID value in your query. Ken Puls has a nice write up on the process here. Reply back if you’re stuck.
You could use a SSAS Cube with a PivotTable in Excel with a filter on EMP.ID.
I guess it is not possible to change the query in Excel without Power Query Editor and I think it was not intended to do so (regulary).
If it does not need to be Excel you cloud just use SSMS or any similar alternative.
did you try to Un-tick the box that says "Require user approval for new native database queries" ?
you can set the ID as a parameter as suggested above... check my sample file for running an SQL query with a parameter. Sample File
also you can automatically refresh the worksheet with something like :
Private Sub Worksheet_Change(ByVal Target As Range)
If Intersect(Target, Me.Range("datachange")) Is Nothing Then Exit Sub
Application.EnableEvents = False 'to prevent endless loop
'Application.Goto Reference:="Tum_Santiyelerin_Satinalinan_Malzemeleri"
Range("EMP_ID").ListObject.QueryTable.Refresh BackgroundQuery:=False
ActiveWorkbook.RefreshAll
Application.EnableEvents = True
End Sub
I had a similar requirement in the past. My solution was to use the QueryTables object to send a query to the database using user-supplied data from a cell on the worksheet. It does use a macro, but I didn't have to save the credentials in the workbook.
This solution requires an ODBC driver for SQL Server.
(I seem to recall that I also had to check the references in Visual Basic - in toolbar Tools>References - but it was a while ago and I don't remember the exact details.)
Add the vb code below to a new workbook. Then if you enter the [EMP.ID] value in cell A1 of Sheet1 and run the macro 'ReadData', it will pull out the records and display them starting in cell A3.
Save the workbook as macro-enabled .xlsm and it can be shared with your users. (You could also assign the macro to a keyboard shortcut or command button to speed things up for your users.)
(This approach attempts to connect to the database using a trusted connection, i.e. using windows log-in credentials. I also use another database which requires separate credentials. I have another example below for that scenario.)
The vb code for the macro is below. Check the connection string that it has the correct driver and server IP address etc. and that the query string it reading the correct table.
Sub ReadData()
''' read database using filter supplied in cell A1
Dim ConnectionString As String
Dim QueryString As String
' Create connection string using credentials
ConnectionString = "ODBC; DRIVER={SQL Server}; SERVER=XX.XX.X.XXX; DATABASE=XXXXXXXXX; SCHEMA=dbo; REGION=yes;"
' Create query string to read data using value of cell A1
QueryString = "SELECT * FROM [VEIWS].[VIEW_NAME] WHERE [EMP.ID] = '" & Range("Sheet1!A1").Value & "'"
' The lines below can be un-commented if you get errors - it might help with debugging
'Range("Sheet1!C1").Value = ConnectionString
'Range("Sheet1!C2").Value = QueryString
' This code sends the query to the database and drops the results starting at cell A3
With Sheets("Sheet1").QueryTables.Add(Connection:=ConnectionString, _
Destination:=Range("Sheet1!A3"), Sql:=QueryString)
.RefreshStyle = xlOverwriteCells ' this stops excel from inserting new columns when the query is re-run
.Refresh False
End With
' Remove connections to avoid wasting memory
For Each con In Sheets("Sheet1").QueryTables
con.Delete
Next
End Sub
When the database requires different credentials
For this I created a user form to get the username and password, which I then incorporated into the connection string.
The steps I followed were:
In a new workbook, go to Visual Basic and create a new user form. Re-name it LoginForm
Create 2 text boxes on the form, named Username and Password. (You can also add labels and set the PasswordChar to '*' to make it look more like a login window.)
Create a command button ('OK' or 'Done'). Right click on it and select View Code. Enter the line Me.Hide in the code window so it looks like:
Private Sub CommandButton1_Click()
Me.Hide
End Sub
The vb code for the macro changes to :
Sub ReadData()
''' read database using filter supplied in cell A1
Dim ConnectionString As String
Dim QueryString As String
' First time you run, need to show form to get credentials
If LoginForm.Username = "" Or LoginForm.Password = "" Then LoginForm.Show
' Create connection string using credentials
ConnectionString = "ODBC; DRIVER={SQL Server}; SERVER=XX.XX.X.XXX; DATABASE=XXXXXXXXX; SCHEMA=dbo; REGION=yes; uid=" _
& LoginForm.Username & "; pwd=" & LoginForm.Password
' Create query string to read data
QueryString = "SELECT * FROM [VEIWS].[VIEW_NAME] WHERE [EMP.ID] = '" & Range("Sheet1!A1").Value & "'"
' The lines below can be un-commented if you get errors - it might help with debugging
'Range("Sheet1!C1").Value = ConnectionString
'Range("Sheet1!C2").Value = QueryString
' This code sends the query to the database and drops the results starting at cell A3
With Sheets("Sheet1").QueryTables.Add(Connection:=ConnectionString, _
Destination:=Range("Sheet1!A3"), Sql:=QueryString)
.RefreshStyle = xlOverwriteCells ' this stops excel from inserting new columns when the query is re-run
.Refresh False
End With
' Remove connections to avoid wasting memory
For Each con In Sheets("Sheet1").QueryTables
con.Delete
Next
End Sub
Now, the first time the user runs the code, it will prompt them for username and password, but for the rest of their session it will keep using these values. They will not be saved when the workbook is closed. (If the macro hits an error they will probably be asked for credentials again next time it is run).
Hopefully this helps you. I did this work some time ago and I may have forgotten if there were any other set-up requirements needed.

Excel data connection to SQL dB error

I crafted a macro in an Excel workbook to extract a subset of data from a SQL database based on user input.
The macro prompts the user for a parameter input and inserts that parameter into a ready-made stored procedure configured into a an Excel data connection - see below for my vba:
Sub RefreshDBQuery()
Dim Val As Integer
Application.ScreenUpdating = False
Worksheets("Adjustable CF").Select
Val = InputBox("Enter valid 4 digit number", , 1907)
Sheets("TestData").Visible = True
Worksheets("TestData").Select
Worksheets("TestData").Range("A1").Select
ActiveCell.Value = Val
With ActiveWorkbook.Connections("MacroExtraction 2Server").OLEDBConnection
.CommandText = "EXEC dbo.prV_FlowExtract '" & Range("A1").Value & "'"
End With
ActiveWorkbook.Connections("MacroExtraction 2Server").Refresh
Sheets("TestData").Visible = False
End Sub
When I run it - it works fine and additionally, since it's modifying an existing data connection ( the one I previously configured), I notice a odc file in a folder called "My Data Sources" under My Documents:
However, when I send this workbook over to a colleague to run the macro and to extract data - the macro is able to run up to a point, and she receives an error:
I ask her to open up the folder "My Data Sources" and I don't see an odc file:
My question is: what am I missing? Or rather what is my colleague missing in order to get her macro to work on her local machine?
I checked with the dB administrator who said that she has the permissions necessary to access the server, so that's why I am picking on the lack of the odc as a cause for my concern. Should I copy my odc file and send it to her to copy into her Data Sources folder? Should I rewrite the macro and re-setup the data connection on her local machine? Anyone with experience to comment would be much appreciated! Thanks!
The macro alone does not contain all the necessary info (server name, for example?). Try running "NewSQLServerConnection.odc" in you colleague's my data sources location, complete the necessary data, make sure the connection name is the same as in your macro, and then the macro should work.
Hope this helps!

DSN-less Connection MS Access front end and SQL server backend

I don't normally program in MS Access VBA so forgive my question if it's stupid.
So I'm using MS Access 2010 as a front end and SQL Server 2014 as a backend. (I don't have a choice in frontend interface so please no suggestions on alternate options).
I'd like to programatically link SQL server's backend to my MS Access frontend. I read here at DJ Steele's DSN-less connection page that I can use the code he provided here to make a DSN-less connection to SQL server as a backend.
So I copied that into a VBA Access module and opened another module and ran this code to run the DJ Steele code in an attempt to connect to a small SQL Server database I made:
Option Compare Database
Sub runThis()
FixConnections "AServerNameHere", "MS_Access_BackEnd_Test"
End Sub
As far as I can tell from the VBA debugger it gets to
Set dbCurrent = DBEngine.Workspaces(0).Databases(0)
then that value seems to be empty. I'm not sure how else to proceed with this since as far as I was able to find this is one of the few full examples of a DSN-less connection I could find.
I'd like to not use the DSN method of linking a SQL server to a database since that would require me to go visit people and their computers in order to make the links. (And who'd want to to that? LOL)
I've also looked at similar questions that were linked to me while writing this question and this was close to what I wanted, but it kept giving me "Compile error: Constant expression required" for input of:
LinkTable "MS_Access_BackEnd_Test", "Table_1"
and
LinkTable "MS_Access_BackEnd_Test", "Table_1", , "AServerNameHere"
Again I'm not familiar with MS Access VBA so forgive the question if it's lame.
Looking at DJ Steele's code, I got it working apart from the line
' Unfortunately, I'm current unable to test this code,
' but I've been told trying this line of code is failing for most people...
' If it doesn't work for you, just leave it out.
tdfCurrent.Attributes = typNewTables(intLoop).Attributes
which I had to comment out.
Using Set dbCurrent = CurrentDb() does essentially the same thing as Set dbCurrent = DBEngine.Workspaces(0).Databases(0) but the latter is meant to be a lot faster ... which these days means it takes 10 microseconds instead of 100 :-o
You still need dbCurrent as a reference to the current Access front end which is where the linked table objects live, even if the data is coming from elsewhere.
Edit: working for me
I added a debug.print line to monitor what's going on
' Build a list of all of the connected TableDefs and
' the tables to which they're connected.
For Each tdfCurrent In dbCurrent.TableDefs
Debug.Print tdfCurrent.Name, tdfCurrent.Connect
If Len(tdfCurrent.Connect) > 0 Then
If UCase$(Left$(tdfCurrent.Connect, 5)) = "ODBC;" Then
...
and then
? currentdb().TableDefs("dbo_Person").Connect
ODBC;DSN=TacsData;APP=Microsoft Office 2003;WSID=TESTXP;DATABASE=TacsData;Trusted_Connection=Yes;QuotedId=No
FixConnections "TESTXP\SQLEXPRESS", "TacsData"
MSysAccessObjects
MSysAccessXML
...
MSysRelationships
Table1
dbo_Person ODBC;DSN=TacsData;APP=Microsoft Office 2003;WSID=TESTXP;DATABASE=TacsData;Trusted_Connection=Yes;QuotedId=No
? currentdb().TableDefs("dbo_Person").Connect
ODBC;DRIVER=sql server;SERVER=TESTXP\SQLEXPRESS;APP=Microsoft Office 2003;WSID=TESTXP;DATABASE=TacsData;Trusted_Connection=Yes

SSIS Package works in IDE but does not after deployment

So I have this SSIS package that works flawlessly when I start it within Visual Studio Integration 2012. However when I deploy this package via the SQL Catalog it neither works or breaks. It just runs with no output.
I have no idea why this is. My package is simple.
Query a DB table
Place data into a Recordset Destination using a local variable
Pass the local variable along with some project parameters into a script that is embedded into a sequence > foreach container. The script then updates the same db table with a new value based on the logic within the script and it also sends out an email using the data pulled from the query.
I know that my query in #1 is working. I can see it through my job execution reports... it writes in all rows from the DB table. Then it just sits in my script doing God knows what... So I am guessing since it is not my query it must be one of the project params/connections (that I set using an environment). Here is my syntax for these. Do you see anything wrong with them? Please ignore the code I left out as I am sure it is not breaking there. Also I am 100% positive I am setting the values correctly in my environment and that they are properly assigned in the package.
Here is my relevant code snippets (do not mind order):
Dim oleDA As New OleDbDataAdapter
oleDA.Fill(dt, Dts.Variables("User::CustomerRows").Value)
ScriptMain.Wrapper = New ET_AutoRenewal.WrapperClass(Dts.Variables("$Project::etUserName").Value.ToString(), Dts.Variables("$Project::etPassword").Value.ToString())
Dim cm As ConnectionManager = Dts.Connections("test")
Dim sqlConn As SqlClient.SqlConnection
Dim sqlCmdText As String
sqlConn = DirectCast(Dts.Connections("test").AcquireConnection(Dts.Transaction), SqlClient.SqlConnection)
sqlCmdText = "UPDATE [" + Dts.Variables("$Project::sDatabaseName").Value.ToString() + "].[dbo].[temp_AutoRenewal_listpull] SET [process_flag] = 1 WHERE [confirmation_num] = '" + xmlDict.Item("confirmation_num") + "'"
So if that all looks good, which to me it does. How could this happen, it works fine in VS but not when deployed. What is more frustrating is nothing fails, it simply runs and there is no output (ie no table updates or emails sent out).
Are there any techniques to debug a deployed package?

I need to pull large backup files from my clients' servers to my server every month

I have clients out there running an SQL Server Express 2005 and each of these needs a backup each month and that backup needs to be moved to our server in case they lose their backup. Our software automatically backs up the database each month but we have to manually go in and copy it across. Is there any way to automate the copying of files up to 800 megs from their machine to ours each month perhaps using FTP? Also, if using FTP it has to support resume in case we lose the connection three querters through which happens quite often. I would like to write this functionality into our VB.net application that only requires the .net framework and not use any third party controls.
I think this is where you'd be better off if you used something like RSync rather than a home-grown solution.
Another alternative would be to use a program like SyncBack, that supports syncronization across FTP, can be scheduled to run daily/weekly/monthly and has worked for me to do what you're talking about, for years. I'm not sure about Resuming an FTP transfer - but it does a great job of backing things up across FTP.
It's certainly doable with either the WebClient classes, or (Ftp)WebRequest/WebResponse series of classes - and I can give you some sample code if required - but unless you have some specific business case for rolling your own something like RSync may be the better choice.
EDIT;
The WebClient route is the simplest, but it doesn't give you much control;
Imports System.Net
...
Dim Client As New WebClient
Client.DownloadFile("ftp://ftp.example.com/Database.bak", "D:\Backups\Database.bak")
If you want a bit more control, and to manage FTP resumes then something like this will do the trick;
Public Sub TransferFile(ByVal SourcePath As String, ByVal DestinationPath As String)
Dim SourceRequest As FtpWebRequest
Dim Buffer(4095) As Byte
Dim BytesRead As Integer
' Assumes source is on FTP server...
SourceRequest = DirectCast(WebRequest.Create(SourcePath), FtpWebRequest)
SourceRequest.Method = WebRequestMethods.Ftp.DownloadFile
' If we already have a local file, then resume from the end of it...
SourceRequest.ContentOffset = If(File.Exists(DestinationPath), New FileInfo(DestinationPath).Length, 0)
' Assume destination file is local/UNC file. FileMode.Append will create a new file if one doesn't exist.
Using DestinationFile As New FileStream(DestinationPath, FileMode.Append, FileAccess.Write, FileShare.None)
Using SourceResponse As WebResponse = SourceRequest.GetResponse()
Using SourceStream As Stream = SourceResponse.GetResponseStream()
Do
BytesRead = SourceStream.Read(Buffer, 0, Buffer.Length)
DestinationFile.Write(Buffer, 0, BytesRead)
' Calculate speed, progress, show to user/log, etc...
Loop While BytesRead > 0
End Using
End Using
End Using
End Sub
This assumes you're transferring from FTP -> local. Username/password can be supplied as in the SourcePath as so; ftp://username:password#ftp.mysite.co.uk
Hope this helps.

Resources