I have a bunch of sql files with queries that need no be run on my local database each time there are some changes in those files.
We use classic ASP code to run these files in a loop written on VBScript, like this:
Dim arrSqlLines()
i = 0
set t = fso.OpenTextFile(filePath, 1, false)
Do Until t.AtEndOfStream
Redim Preserve arrSqlLines(i)
arrSqlLines(i) = t.ReadLine
i = i + 1
Loop
t.close
For Each sqlLine in arrSqlLines
sqlLine = Trim(sqlLine)
sqlBatch = Trim(sqlBatch & sqlLine) & vbCrLf
Call dbexecConnection(sqlBatch, objDbConnection)
Next
Function dbexecConnection(sql, objConnExec)
dim cmdTemp
on Error Resume Next
set cmdTemp=Server.CreateObject("ADODB.Command")
set cmdTemp.ActiveConnection=objConnExec
cmdTemp.CommandText = sql
cmdTemp.Execute
if err.number<>0 Then
if Session("SkipError") <> -1 Then
response.write "Error in dbexecute: " & sql & "<br/>"
response.write "Error=(" & err.description & ")"
response.End
end If
end If
on error goto 0
End Function
The problem is that if a sql file is ecoded in UTF-8 without BOM it runs it OK, but if any file is encoded in UTF-8 format, it produces an error:
For example, this very sql file starts like this:
SET QUOTED_IDENTIFIER ON
GO
IF OBJECT_ID('RPM_GET_ACTUAL_COST_FOR_EPIC') IS NOT NULL
DROP Function [RPM_GET_ACTUAL_COST_FOR_EPIC]
GO
CREATE Function [RPM_GET_ACTUAL_COST_FOR_EPIC]
(
#EpicID as int,
#RateType as Int, -- 1 for Blended, 2 for CostCenter
#CalcType as Int -- 1 for by Hours or 2 for Points
)
returns float
BEGIN
declare #Cost float
declare #CostX float
declare #ItStorys TABLE (
StorylD int,
State int,
DemoStatus int,
Dfficulty int,
Feature int,
TeamID int,
TrackType int,
Iteration int
)
insert into #tStorys(StoryID, State, DemoStatus, Dfficulty, Feature, TeamID, TrackType, Iteration)
I cannot guarantee that all files will be encoded in UTF-8 without BOM so I have to find the way to make it run correctly files with UTF-8 as well. how can i possibly do that?
It is certain that FileSystemObject does not handle UTF-8 but Unicode and ANSI.
ADODB.Stream can handle a lot of character sets including utf-8 so you can use it instead.
Replace your code up to the first For with the following.
Dim arrSqlLines
With Server.CreateObject("Adodb.Stream")
.Charset = "utf-8"
.Open
.LoadFromFile filePath
If .EOS Then
'an empty array if file is empty
arrSqlLines = Array()
Else
'to obtain an array of lines like you desire
'remove carriage returns (vbCr) if exist
'and split the text by using linefeeds (vbLf) as delimiter
arrSqlLines = Split(Replace(.ReadText, vbCr, ""), vbLf)
End If
.Close
End With
Just need to tell Classic ASP and IIS that you are wanting to process the ASP page using UTF-8.
First save the ASP page making sure it is saved using Code Page 65001 (if using Visual Studio this can be done through the Advanced Save Options menu option).
Then modify the ASP page to include the following initial lines.
<%#Language="VBScript" CodePage = 65001 %>
<%
'Assuming this is the whole script the above line MUST always
'be the very first line in the source file.
'Tell ASP strings should be returned UTF-8 encoded.
Response.CodePage = 65001
'Tell the Browser to expect UTF-8 encoded data.
Response.Charset = "UTF-8"
Dim arrSqlLines()
i = 0
set t = fso.OpenTextFile(filePath, 1, false)
Do Until t.AtEndOfStream
Redim Preserve arrSqlLines(i)
arrSqlLines(i) = t.ReadLine
i = i + 1
Loop
t.close
For Each sqlLine in arrSqlLines
sqlLine = Trim(sqlLine)
sqlBatch = Trim(sqlBatch & sqlLine) & vbCrLf
Call dbexecConnection(sqlBatch, objDbConnection)
Next
Function dbexecConnection(sql, objConnExec)
dim cmdTemp
on Error Resume Next
set cmdTemp=Server.CreateObject("ADODB.Command")
set cmdTemp.ActiveConnection=objConnExec
cmdTemp.CommandText = sql
cmdTemp.Execute
if err.number<>0 Then
if Session("SkipError") <> -1 Then
response.write "Error in dbexecute: " & sql & "<br/>"
response.write "Error=(" & err.description & ")"
response.End
end If
end If
on error goto 0
End Function
%>
Useful Links
Convert UTF-8 String Classic ASP to SQL Database
Related
I have a comma delimited csv file, the problem is that there is a comma also within coloumns.
TEXT,["pn","service"]
TEXT is in one column and ["pn","service"] the other column.
How can I use text qualifier to separate columns correctly?
I recently had to deal with this and offer wrapping the fields with an identifier. I modified the macro below to separate by pipe instead of quote. The superuser post has more options.
Check out https://support.microsoft.com/en-us/help/291296/procedure-to-export-a-text-file-with-both-comma-and-quote-delimiters-i and https://superuser.com/questions/130592/how-do-you-force-excel-to-quote-all-columns-of-a-csv-file
Updated per #Larnu's suggestions - what I'm using to rewrap some data files prior to importing them for one off loads. Just be careful opening the CSV in excel directly... you might see values changes.
Sub PipeCommaExport()
' Dimension all variables.
Dim DestFile As String
Dim FileNum As Integer
Dim ColumnCount As Integer
Dim RowCount As Integer
' Prompt user for destination file name.
DestFile = InputBox("Enter the destination filename" _
& Chr(10) & "(with complete path):", "Quote-Comma Exporter")
' Obtain next free file handle number.
FileNum = FreeFile()
' Turn error checking off.
On Error Resume Next
' Attempt to open destination file for output.
Open DestFile For Output As #FileNum
' If an error occurs report it and end.
If Err <> 0 Then
MsgBox "Cannot open filename " & DestFile
End
End If
' Turn error checking on.
On Error GoTo 0
' Loop for each row in selection.
For RowCount = 1 To Selection.Rows.Count
' Loop for each column in selection.
For ColumnCount = 1 To Selection.Columns.Count
' Write current cell's text to file with quotation marks.
Print #FileNum, "|" & Selection.Cells(RowCount, _
ColumnCount).Text & "|";
' Check if cell is in last column.
If ColumnCount = Selection.Columns.Count Then
' If so, then write a blank line.
Print #FileNum,
Else
' Otherwise, write a comma.
Print #FileNum, ",";
End If
' Start next iteration of ColumnCount loop.
Next ColumnCount
' Start next iteration of RowCount loop.
Next RowCount
' Close destination file.
Close #FileNum
End Sub
I learned a couple of weeks ago how to update an Excel file via ADO. At that time the value was already given before changing it.
Now I want to add the procedure of reading the current value in the same cell and assign the value to a variable before changing it!
The current procedure looks as follows:
Public Sub ChangeNum()
Dim con As ADODB.Connection, rec As ADODB.Recordset
Dim sqlstr As String, datasource As String
Set con = New ADODB.Connection: Set rec = New ADODB.Recordset
datasource = "D:\DropBox\TraderShare\TraderNum.xlsx"
Dim sconnect As String
sconnect = "Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=" & datasource & ";" & _
"Extended Properties=""Excel 12.0 Xml;HDR=YES"";"
con.Open sconnect
sqlstr = "UPDATE [Sheet1$] SET [Number] = """ & gsvDocNum & """ WHERE [ID] = """ & svNumRng & """"
rec.Open sqlstr, con ', adOpenUnspecified, adLockUnspecified 'adLockOptimistic , adOpenStatic, adLockReadOnly
con.Close
Set rec = Nothing: Set con = Nothing
End Sub
gsvDocNum is a global string variable declared in the beginning of the initial startup routine, hence after reading the current value into the variable, the UPDATE one will write gsvDocNum + 1 to the file.
svNumRng is one of the following named ranges, PNum, SNum, TNum or INum declared in the beginning of the main routine and determined which one to look for in an earlier stage (if it’s an Purchase, SalesOrder, TradeOrder or an Invoice).
I’m not so familiar with ADO and SQL strings and I can’t find the proper syntax for SELECT for reading the current cell value and assign it to a variable before changing it with the UPDATE.
Grateful for any help!
OK, the background as follows: We have an administrative program I’ve written myself in Excel vba for registering purchases, orders and invoices, etc. It works pretty well for our requirements but has one issue, keeping order numbers synced between the users! We are 3 users using the program locally, each one registering orders and such, but we share the serial number file via a shared DropBox folder. I have the idea that using ADO/SQL without opening the Excel file would be faster than open, change and save the file in Excel. The reason is of course to minimize the time updating the file thus the delay before syncing to the cloud Dropfox location and to the other users computers is minimized. It’s a simple 2 column Excel file, TraderNum.xlsx:
ID Number
PNum 16000
SNum 16000
TNum 16132
INum 16173
I learned a couple of weeks ago how to change one of the numbers from Excel without opening the file using ADO/SQL, (see above). But I discovered that a constant update of the Excel link to a closed file for having the current number available before changing it doesn’t work as expected. Accordingly I want to use ADO/SQL also to read/assign the specific current number to a variable in the Excel procedure, before changing it with the ADO/SQL procedure above.
So somewhere between the 2 commands, rec.Open sconnect and con.Close there should be a SQL-string similar to:
sqlread = "SELECT """ & DocNumOld & """ = [Number] FROM [Sheet1$] Where [ID] = """ & svNumRng & """"
where the DocNumOld variable is assigned the current number from the chosen ID variable svNumRng.
Then the DocNumNew variable is and assigned with the DocNumOld variable incremented with 1 followed by the
sqlUpdate sequence. It should look similar to the following:
Public Sub ChangeNum()
Dim con As ADODB.Connection, rec As ADODB.Recordset
Dim sqlRead as String, sqlUpdate As String, datasource As String, sconnect As String
Set con = New ADODB.Connection: Set rec = New ADODB.Recordset
datasource = "D:\DropBox\TraderShare\TraderNum.xlsx"
sconnect = "Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=" & datasource & ";" & _
"Extended Properties=""Excel 12.0 Xml;HDR=YES"";"
con.Open sconnect
sqlRead = "SELECT """ & DocNumOld & """ = [Number] FROM [Sheet1$] Where [ID] = """ & svNumRng & """"
sqlUpdate = "UPDATE [Sheet1$] SET [Number] = """ & DocNumNew & """ WHERE [ID] = """ & svNumRng & """"
rec.Open ???????, con
????? sqlRead
DocNumNew = DocNumOld + 1
UNION
????? sqlUpdate
con.Close
Set rec = Nothing: Set con = Nothing
End Sub
Can you solve this, please?
Can anyone give me a solution to how to use ADO/SQL also to read/assign one specific current number to a variable in an Excel procedure, before changing it with the ADO/SQL procedure?
I am concerned I don't understand your question, because the resulting comments don't make sense to me.
To restate your problem: you need to know the value of some cell, and be able to feed it into your code.
You can already connect to a worksheet with SQL, you already know what SELECT statements are, and you probably already know how to run them. Humor me.
sqlRead = "SELECT * FROM [Sheet1$A12:F48]"
Set rec = con.Execute(sqlRead)
Now you have a recordset rec that contains the whole table. Say you wanted to put every value of the entire table in your immediate window:
Do While Not rec.EOF
For i = 0 To rec.Fields.Count - 1
Debug.Print rec.Fields(i).Name, rec.Fields(i).Value
Next
rec.MoveNext
Loop
Don't forget to close it, and I suggest using a second variable name anyway as the name of the recordset for the update statement.
rec.Close
Say you knew the cell would always be in the 3rd row, 8th column of the table you are selecting from, you might:
For j = 0 to myRowNum-1 'you have set myRowNum equal to 3 earlier'
rec.MoveNext
Next
myOldCellValue = rec.Fields(myColNum-1).value 'you have set myColNum to 8 earlier'
rec.Close
Now, say you don't know exactly which row you will find myOldCellValue, but you know it will be found in the 4th column of the row that has the unique [ID] 1234, you might:
sqlRead = "SELECT * FROM [Sheet1$A12:F48] Where [ID] = """ & myIDNum & """" 'you have set myIDNum to 1234 earlier
Set rec = con.Execute(sqlRead)
myOldCellValue = rec.Fields(myColNum-1).value 'you have set myColNum to 4 earlier'
rec.Close
Say you wanted to UPDATE every row that had that value (I don't read that you, but for completeness), you might:
sqlUpdate="UPDATE [Sheet1$] SET [Number] = """ & DocNumNew & """ WHERE [DocNum] = """ & myOldCellValue & """"
I have quite a conundrum which I have been trying to troubleshoot. I have a stored procedure in a MySql database, which I call through an Excel VBA application. The VBA application passes the recordset into an Array, and then I use a For Loop to place each of the items in the Array onto a worksheet.
Here's the problem: two of the values in the recordset keep coming back blank in Excel. Oddly, the two are in the middle of the Array, not the beginning or end. However, if I call the stored procedure through another query program such as HeidiSql, I receive ALL values back. I'm at a loss as to why I'm not receiving all of the values through Excel... or why the Array isn't receiving them all, at any rate.
Thanks in advance for your help.
Here is my code:
Sub StartHereFlexFunderCust()
On Error GoTo ErrorHandler
Dim Password As String
Dim SQLStr As String
'OMIT Dim Cn statement. Cn stands for Database Connection
Dim Server_Name As String
Dim User_ID As String
Dim Database_Name As String
Dim custID As String
Dim myArray()
'OMIT Dim rs statement. rs stands for Database Recordset and is the Recordset of what is returned
Set RS = CreateObject("ADODB.Recordset")
Server_Name = Range("O10").Value
Database_Name = Range("O11").Value ' Name of database
'id user or username. We need to write code to insert the current user into this variable (Application.Username) if possible. But they may not be consistent across all machines.
'For example mine is "Ryan Willging" and we would have to shorten it to rwillging but others may be rwillging.
'This is important because if we do not do this all queries will come from the same person and that is not good for debugging.
User_ID = Range("O12").Value
Password = Range("O13").Value
custID = Range("C4").Value 'Deal Number from Start here that we are passing into the stored procedure
'This is the storedprocedure call and it passes in the value of the DealId to the Stored Procedure
SQLStr = "call flexFundByCustomer(" + custID + ")"
Set cn = CreateObject("ADODB.Connection") 'NEW STATEMENT
'This statement takes the variables from the checklist and passes them into a connection string
cn.Open "Driver={MySQL ODBC 5.1 Driver};Server=" & _
Server_Name & ";Database=" & Database_Name & _
";Uid=" & User_ID & ";Pwd=" & Password & ";"
'This statement queries the database using the SQL string and the connection string.
'The adOpenStatic variable returns a static copy of a set of records that you can use to find data or generate reports. There are other variables that
'could be used but I think this one will suffice.
RS.Open SQLStr, cn, adOpenForwardOnly
Debug.Print msg 'or MsgBox msg
'Take all of the info from the queries and put them into the spreadsheet
myArray = RS.getrows()
Dim Fld_Name As String
Dim Val_of_Field As String
Dim starthere As Worksheet
Fld_Name = UBound(myArray, 1)
Val_of_Field = UBound(myArray, 2)
Set starthere = ThisWorkbook.Sheets("Start Here")
MsgBox "No error yet defined Start Here!"
'This little loop works well to dump the recordset into excel. We can then map the correct fields 'k inputs the headers and R inputs the rows returned in the Recordset
For K = 0 To Fld_Name ' By using a For loop the data is inputed into excel one row at a time
starthere.Range("U4").Offset(0, K).Value = RS.fields(K).Name
For R = 0 To Val_of_Field
starthere.Range("U4").Offset(R + 1, K).Value = myArray(K, R)
Next
Next
RS.Close
Set RS = Nothing
cn.Close
Set cn = Nothing
ErrorHandler:
MsgBox "There's been an error!"
Exit Sub
End Sub
Consider using Range.CopyFromRecordset method to avoid any use of arrays. Or if memory does not allow, use a Do While Loop across Recordset columns:
' COLUMN HEADERS
For i = 1 To RS.Fields.Count
starthere.("Results").Range("U4").Offset(0, i) = RS.Fields(i - 1).Name
Next i
' DATA ROWS
' COPYFROMRECORDSET APPROACH
starthere.Range("U5").CopyFromRecordset RS
' DO WHILE LOOP APPROACH
starthere.Activate
starthere.Range("U5").Activate
row = 5
Do While Not RS.EOF
For i = 0 To RS.Fields.Count - 1
ActiveCell.Offset(0, i) = RS.Fields(i)
Next i
row = row + 1
ActiveCell.Offset(row, 21)
RS.MoveNext
Loop
As for the values returning empty that may be a MySQL and Excel incompatibility of data types. For instance, you may have a table field set to MySQL's maximum decimal (65, 30) which denotes max digits of 65 and max 30 decimal points which cannot be reflected on a spreadsheet. Current precision limit of a cell value is 15 decimal points.
Alternatively, you may have a VARCHAR(65535) which is the 65,535 byte limit or the open-ended TEXT column of no limit that also cannot be displayed on spreadsheet. Current limit of characters in one cell is 32,767.
Try modifiying column to a smaller type:
ALTER TABLE `tableName` MODIFY COLUMN `largenumberfield` DECIMAL(10,7);
ALTER TABLE `tableName` MODIFY COLUMN `largetextfield` VARCHAR(255);
Why the other programs such as HeidiSQL retrieve values? It might be due to their internal conversion features forcing data values into a specific format (i.e., removing whitespaces, truncating values) which then renders adequately in Excel.
In classic ASP I need to extract data out of a MSSQL database, passing the results to a two dimensional array (rows, columns) and display the data in various formats.
For each such format I need to build functions to display data. So, in order to be as modular as possible I need to separate (i) extraction and passing data to the array from (ii) displaying the results.
My code does currently the extraction of data using a class, but also displays (within the same class) the data in a primitive way (just to test that the data is extracted and correct).
How can I pass such array to a function? You can imagine how flexible would be to have an array as data input into a function and then manipulate it (creating many functions) when trying to display it in a table (example: function 1 will be based on template no. 1 of a table that is red with background black and no borderline, function 2 is built on the template 2, the table is green, with borderline and yellow background, etc etc).
Here is my code and at the end of the main function (within the class) you will see a portion that displays results, i.e. the one that I need to do it separately from / outside the class (i.e. in the functions to be created).
<!--#include file ="../functions/fctGetnrofrecords.asp"-->
<%
Dim db : Set db = New GetRowsFromAdatabase
db.strTable="Customers"
strDisplay=db.FindOutRecordsAndPassThemToAnArray()
Response.Write strDisplay
Class GetRowsFromAdatabase
Public strTable
Public numberOfRecords
Public numberOfFields
Public Function FindOutRecordsAndPassThemToAnArray()
Set conn = CreateObject("ADODB.Connection")
Set rs = CreateObject("ADODB.Recordset")
'Find out connecting credentials
strSERVERspecific=Coli(0)
strDATABASENAMEspecific=Coli(1)
strUIDspecific=Coli(2)
strPWDspecific=Coli(3)
conn.Open "Provider=SQLOLEDB;server=" & strSERVERspecific & ";database=" & strDATABASENAMEspecific & ";uid=" & strUIDspecific & ";pwd=" & strPWDspecific & ";"
rs.Open strTable, conn
if rs.EOF and rs.BOF then
strError = "There is no record in the table " & strTable
else
'Assign the Number Of Fields to the variable “counter”
counter = rs.fields.count
numberOfFields=counter
Dim matrix(25, 10) ' these exceed by far the values of numberOfRecords and numberOfFields
for j=0 to counter-1
matrix(0,j)= rs.Fields(j).Name ' The first dimension of the array, when is zero,
' is populated with the names of fields
next
rs.movefirst
i=1
do until rs.EOF
for j=0 to counter-1
matrix(i,j)=rs(j)
next
i=i+1
rs.movenext
loop
end if
' Now, I need this class not to include the displaying section that follows
' (i.e. see the portion until the end of this function), although this section works fine
numberOfRecords=fctGetNumberOfRowsOfaTable(strTable)
'see the include directive at the beginning of this code (there is a function there that does this)
'====begin section that displays the arrays values
for m = 0 to numberOfRecords
for n=0 to counter-1
strDisplay = strDisplay & m & "," & n & "=" & matrix(m,n) & "<br>"
next
next
'====end section that displays the array values
FindOutRecordsAndPassThemToAnArray = strDisplay
End Function
Public Function Coli(x)
'This function read a line of a txt file located on the server side (hidden from public / httpdocs)
' where x = the relevant line out of the following
' 1 means the 1st line = name / IP server
' 2 means the 2nd line = database name
' 3 means the 3rd line = user name available in the database
' 4 means the 4th line = user’s password
if x<0 or x> 3 then
Coli="Erorr"
Exit Function
else
serv=Server.MapPath("../../../")
path=serv & "\somehiddenfolder\"
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objTextFile = objFSO.OpenTextFile(path & "configuration.txt")
J=0
Dim Datele(3)
Do Until objTextFile.AtEndOfStream
strNextLine = objTextFile.Readline
if x=J then
Coli=strNextLine
exit function
else
J=J+1
end if
Loop
end if
End Function
End Class
%>
Any hints will be highly appreciated.
Use .GetRows to get an array of a table/resultset. To pass such an array to a function (why function? what should be its return value?) you write its name in the argument list of the function call.
Update wrt comment:
Sample of calling a function that expects an array:
>> Function sum(a)
>> sum = Join(a, "+")
>> End Function
>> a = Split("1 2 3")
>> WScript.Echo sum(a)
>>
1+2+3
Instead of Split() - which returns a one dimensional array - you'd use .GetRows() on a valid recordset (and keep in mind that .GetRows() returns a two dimensional array).
I'm converting a database from access to a sql backend access front end. The database has embedded pdf documents which end up getting stored as [image] data by SQL server's data import tools.
My problem is that I want the users to be able to open the pdf file by clicking the pdf icon in a report created in access.
Can this be done with VBA or is there an easier way? I'm at a total loss on how to make this happen.
Thanks for the answer!
I edited the BlobToFile function to strip out the ole header since adobe couldn't read the file (evince could and so could mac preview)
I was able to do what I wanted like this:
Private Sub PDFDocument_Click()
Call BlobToFile("C:\db\MyPDFFile.pdf", Me.PDFDocument)
If Dir("C:\db\MyPDFFile.pdf") <> "" Then
FollowHyperlink ("C:\db\MyPDFFile.pdf")
End If
End Sub
'Function: BlobToFile - Extracts the data in a binary field to a disk file.
'Parameter: strFile - Full path and filename of the destination file.
'Parameter: Field - The field containing the blob.
'Return: The length of the data extracted.
Public Function BlobToFile(strFile As String, ByRef Field As Object) As Long
On Error GoTo BlobToFileError
Dim nFileNum As Integer
Dim abytData() As Byte
Dim abytParsedData() As Byte
Dim copyOn As Boolean
Dim copyIndex As Long
BlobToFile = 0
nFileNum = FreeFile
copyOn = False
copyIndex = 0
Open strFile For Binary Access Write As nFileNum
abytData = Field
ReDim abytParsedData(UBound(abytData))
For i = LBound(abytData) To UBound(abytData) - 1
If copyOn = False Then
If Chr(abytData(i)) = "%" And Chr(abytData(i + 1)) = "P" And Chr(abytData(i + 2)) = "D" And Chr(abytData(i + 3)) = "F" Then
copyOn = True
End If
End If
If copyOn = True Then
abytParsedData(copyIndex) = abytData(i)
copyIndex = copyIndex + 1
End If
Next
Put #nFileNum, , abytParsedData
BlobToFile = LOF(nFileNum)
BlobToFileExit:
If nFileNum > 0 Then Close nFileNum
Exit Function
BlobToFileError:
MsgBox "Error " & Err.Number & ": " & Err.Description, vbCritical, _
"Error writing file in BlobToFile"
BlobToFile = 0
Resume BlobToFileExit
End Function
If I understand what you are trying to do, you basically want Adobe Reader to open an in-memory pdf file "object". This isn't possible. You'll need to write the pdf file out to the system hard drive and then open it from there. You can somewhat achieve what you're asking by either using the computers Temp folder or else managing the files/folder yourself. For example, you could possibly cleanup your PDF file folder every time the application opens.
Here's some code to help you do what you're trying to do. This code does not handle anything to do with creating folders, generating file names, checking to see if the file already exists, etc. I'm assuming that you'll be able to handle that. My code in Command1_Click assumes that you're using SQL Server with ODBC linked tables.
I'm using FollowHyperlink here but I highly recommend that you use Allen Browne's GoHyperlink function instead to open files. You'll probably have security errors with FollowHyperlink.
Private Sub Command1_Click()
Dim r As DAO.Recordset, sSQL As String
sSQL = "SELECT ID, BlobField FROM MyTable"
Set r = CurrentDb.OpenRecordset(sSQL, dbOpenDynaset, dbSeeChanges)
If Not (r.EOF And r.BOF) Then
Call BlobToFile("C:\MyPDFFile.pdf", r("BlobField"))
If Dir("C:\MyPDFFile.pdf") <> "" Then
FollowHyperlink("C:\MyPDFFile.pdf")
End If
End If
r.Close
Set r = Nothing
End Sub
'Function: BlobToFile - Extracts the data in a binary field to a disk file.
'Parameter: strFile - Full path and filename of the destination file.
'Parameter: Field - The field containing the blob.
'Return: The length of the data extracted.
Public Function BlobToFile(strFile As String, ByRef Field As Object) As Long
On Error GoTo BlobToFileError
Dim nFileNum As Integer
Dim abytData() As Byte
BlobToFile = 0
nFileNum = FreeFile
Open strFile For Binary Access Write As nFileNum
abytData = Field
Put #nFileNum, , abytData
BlobToFile = LOF(nFileNum)
BlobToFileExit:
If nFileNum > 0 Then Close nFileNum
Exit Function
BlobToFileError:
MsgBox "Error " & Err.Number & ": " & Err.Description, vbCritical, _
"Error writing file in BlobToFile"
BlobToFile = 0
Resume BlobToFileExit
End Function