Missing Leading Zeros in SQL Export to Excel Loop - sql

New VB programmer here. I am exporting a SQL table to an Excel file using the following method below. However, when I create the file in excel, my leading zeros for my primary key are missing due to being converted to numbers instead of text. This is due to the information coming from the datatable to excel. I am wondering what I can do to keep my leading zeros.
FYI - my primary key is 6 digits with only a few that are missing a single 0 in the beginning of them. There are many other columns and rows that get put into the excel file after the first column which all work perfectly. It is only the first column primary keys which i need to change the format somehow.
Also, I am using this excel file to then upload into SQL and the missing 0 on some of the primary keys maks my program create a new record.
I was thinking the main change could take place here but I cannot figure out how to do so:
'Export the Columns to excel file
For Each dc In datatableMain.Columns
colIndex = colIndex + 1
oSheet.Cells(1, colIndex) = dc.ColumnName
Next
For Each dr In datatableMain.Rows
rowIndex = rowIndex + 1
colIndex = 1
For Each dc In datatableMain.Columns
colIndex = colIndex + 1
oSheet.Cells(rowIndex + 1, colIndex) = dr(dc.ColumnName)
Next
Next
Full Code Below:
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Dim dataAdapter As New SqlClient.SqlDataAdapter()
Dim dataSet As New DataSet
Dim command As New SqlClient.SqlCommand
Dim datatableMain As New System.Data.DataTable()
Dim connection As New SqlClient.SqlConnection
connection.ConnectionString = "Server=myserver;Database=mydatabase;User Id=xxxx;Password=xxxxx"
command.Connection = connection
command.CommandType = CommandType.Text
'You can use any command select
command.CommandText = "Select * from MYTABLE"
dataAdapter.SelectCommand = command
Dim f As FolderBrowserDialog = New FolderBrowserDialog
Try
If f.ShowDialog() = DialogResult.OK Then
'This section help you if your language is not English.
System.Threading.Thread.CurrentThread.CurrentCulture = _
System.Globalization.CultureInfo.CreateSpecificCulture("en-US")
Dim oExcel As Excel.Application
Dim oBook As Excel.Workbook
Dim oSheet As Excel.Worksheet
oExcel = CreateObject("Excel.Application")
oBook = oExcel.Workbooks.Add(Type.Missing)
oSheet = oBook.Worksheets(1)
Dim dc As System.Data.DataColumn
Dim dr As System.Data.DataRow
Dim colIndex As Integer = 0
Dim rowIndex As Integer = 0
'Fill data to datatable
connection.Open()
dataAdapter.Fill(datatableMain)
connection.Close()
'Export the Columns to excel file
For Each dc In datatableMain.Columns
colIndex = colIndex + 1
oSheet.Cells(1, colIndex) = dc.ColumnName
Next
For Each dr In datatableMain.Rows
rowIndex = rowIndex + 1
colIndex = 1
For Each dc In datatableMain.Columns
colIndex = colIndex + 1
oSheet.Cells(rowIndex + 1, colIndex) = dr(dc.ColumnName)
Next
Next
'Set final path
Dim fileName As String = "\" + fname.Text + ".xlsx"
Dim finalPath = f.SelectedPath + fileName
txtPath.Text = finalPath
oSheet.Columns.AutoFit()
'Save file in final path
oBook.SaveAs(finalPath, Excel.XlFileFormat.xlOpenXMLWorkbook, Type.Missing, _
Type.Missing, Type.Missing, Type.Missing, Excel.XlSaveAsAccessMode.xlExclusive, _
Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing)
'Release the objects
ReleaseObject(oSheet)
oBook.Close(False, Type.Missing, Type.Missing)
ReleaseObject(oBook)
oExcel.Quit()
ReleaseObject(oExcel)
'Some time Office application does not quit after automation:
'so i am calling GC.Collect method.
GC.Collect()
MessageBox.Show("Exported!")
End If
Catch ex As Exception
MessageBox.Show(ex.Message, "Warning", MessageBoxButtons.OK)
End Try
End If
End Sub
Private Sub ReleaseObject(ByVal o As Object)
Try
While (System.Runtime.InteropServices.Marshal.ReleaseComObject(o) > 0)
End While
Catch
Finally
o = Nothing
End Try
End Sub

I actually just had a similar problem about 10 minutes ago! I needed to get a 30-something digit from one book to another and it was overflowing everything. Try setting the formatting of the column before writing to the cells. My code was Worksheets(i).Range("D:D").NumberFormat = "#" This will tell Excel to interperate the data "as is" instead of trying to guess what you want.

I found this question looking to fix this same issue in a generic function I have, that's used by a couple programs I've created. Due to that variety of data sources I couldn't hard code which columns to set the NumberFormat on. In order to get around it I leveraged the loop I have to output column headers. My code is below for those needing a little more of a dynamic solution. Note, there are a couple references to 'EL' that's an instance of a custom error logging object in the same solution, that can just be ignored/modified:
''' <summary>
''' Function to take a data table and output its contents to an Excel spreadsheet. Returns a string with any errors (Nothing if successful)
''' </summary>
''' <param name="D">The datatable to be output</param>
''' <param name="epath">The full file path to log errors to</param>
''' <param name="SAName">The full file path to save the created Excel workbook to</param>
''' <param name="Parent">The function calling for data to be output</param>
''' <returns></returns>
''' <remarks></remarks>
Public Function ResOut(ByVal D As DataTable, ByVal epath As String, ByVal SAName As String, ByVal Parent As String) As String
'
Dim res As String = ""
Dim E As New Microsoft.Office.Interop.Excel.Application
Dim wb As Microsoft.Office.Interop.Excel.Workbook = Nothing
Dim ws As Microsoft.Office.Interop.Excel.Worksheet = Nothing
Dim x As Long = 0
Dim f As Long = 1
Dim Rng As Microsoft.Office.Interop.Excel.Range = Nothing
Dim q As String
Dim Str_Columns As New List(Of String) 'Holds the list of column letters that need forced to Text format in order to retain leading zeroes in the data
'that will be placed there
'Check that the passed in table has data
If D.Rows.Count = 0 Then
res = "No data was returned by " & Parent
End If
If res = "" Then
'Create a workbook for the data and capture the workbook and sheet for ease of reference
Try
wb = E.Workbooks.Add
ws = wb.Worksheets(1)
'Define the range
q = ColNumToStr(D.Columns.Count, epath)
Rng = ws.Range("A2:" & q & D.Rows.Count + 1)
Catch ex As Exception
res = "Encountered an error while creating the new workbook to export the results to. No data can be returned."
EL.AddErr(res & " ResOut was called by " & Parent & ". Error Details: " & ex.Message, epath)
End Try
'Fill in headers
If res = "" Then
Try
For Each c As DataColumn In D.Columns
ws.Range("A1").Offset(0, x).Value = c.ColumnName
x = x + 1
Next
Catch ex As Exception
res = "Encountered an error while filling in the column headers. This will prevent any data from being returned."
EL.AddErr(res & " ResOut was called by " & Parent & ". Error Details: " & ex.Message, epath)
End Try
End If
'Setup the step & frequency for the Step Progress bar
'Dim t() As Long = s.StatSetup(QR.Rows.Count, 58, "Query Runner\ResOut\" & QName, Replace(My.Settings.EPath, "<user>", Environment.UserName) & DStamp() & " Query Scheduler Log.txt")
'f = t(0)
'SProg.Step = t(1)
'Create the array
Dim OArr(D.Rows.Count, x) As Object
'Convert the datatable to a 2D array
If res = "" Then
Try
'Fill it
x = 0
For r As Long = 0 To D.Rows.Count - 1 Step 1
Dim dr As DataRow = D.Rows(r)
For c As Integer = 0 To D.Columns.Count - 1 Step 1
OArr(r, c) = dr.Item(c).ToString
'Check if this item is a # with leading zeroes (making sure we haven't already added the column to the list of such columns)
If Not (Str_Columns.Contains(ColNumToStr(c + 1, epath))) And Strings.Left(dr.Item(c), 1) = "0" Then
Str_Columns.Add(ColNumToStr(c + 1, epath))
End If 'else the column is in the list already or the item does not dictate it's inclusion
Next
x = x + 1
Next
Catch ex As Exception
res = "Encountered an error while outputing the " & x + 1 & "-th record of " & D.Rows.Count & ". No data will be output."
EL.AddErr(res & " ResOut was called by " & Parent & ". Error Details: " & ex.Message, epath)
End Try
End If
'output the array to the target range
If res = "" Then
'First force Text format where needed to retain leading zeroes
Try
For Each c As String In Str_Columns
q = c
ws.Range(c & ":" & c).NumberFormat = "#"
Next
Catch ex As Exception
res = "Encountered an error while changing column " & q & " to TEXT in order to retain leading zeroes in the " & ws.Range(q & 1).Value & "data."
E.Visible = True
wb.Activate()
EL.AddErr(res & " ResOut was called by " & Parent & ". Error Details: " & ex.Message & Chr(10) & Chr(10) & "Inner Exception: " & ex.InnerException.Message _
, epath)
End Try
Try
Rng.Value = OArr
'Save the workbook
wb.SaveAs(SAName)
wb.Close(SaveChanges:=False)
Catch ex As Exception
res = "Encountered an error during the export of the results. Some data may have been exported. Review the contents of the Excel workbook that will " _
& "be visible following this message for more details."
E.Visible = True
wb.Activate()
EL.AddErr(res & " ResOut was called by " & Parent & ". Error Details: " & ex.Message, epath)
End Try
Else
'Close the workbook without saving
wb.Close(SaveChanges:=False)
End If
'Cleanup the application
Try
E.Quit()
System.Runtime.InteropServices.Marshal.ReleaseComObject(E)
E = Nothing
wb = Nothing
ws = Nothing
Rng = Nothing
OArr = Nothing
f = Nothing
x = Nothing
Catch ex As Exception
EL.AddErr("Encountered an error while cleaning up the resources used in JMLib\ResOut. ResOut was called by " & Parent & ". Error Details: " & ex.Message, epath)
End Try
End If
Return res
End Function

Related

How to Transfer VBA UserForm Data To Access Database?

I have created a user form in excel to save my records in a sheets like sheet1.
But after few days working with this UserForm, it is now goes slower, because of heavy data saving in sheet1.
Now I want to save all records to a database and want to keep clean my sheet1.
So I can work on my UserForm easily or without any delay. Also wants updates my record by calling it via serial numbers.
but I don't want to keep any record in my sheet1.
my little code is below: -
Sub cmdAdd_Click()
On Error GoTo ErrOccured
BlnVal = 0
If BlnVal = 0 Then Exit Sub
With Application
.ScreenUpdating = False
.EnableEvents = False
End With
Dim txtId, txtName, GenderValue, txtLocation, txtCNum, txtEAddr, txtRemarks
Dim iCnt As Integer
iCnt = fn_LastRow(Sheets("Data")) + 1
If frmData.obMale = True Then
GenderValue = "Male"
Else
GenderValue = "Female"
End If
With Sheets("Data")
.Cells(iCnt, 1) = iCnt - 1
.Cells(iCnt, 2) = frmData.txtName
.Cells(iCnt, 3) = GenderValue
.Cells(iCnt, 4) = frmData.txtLocation.Value
.Cells(iCnt, 5) = frmData.txtEAddr
.Cells(iCnt, 6) = frmData.txtCNum
.Cells(iCnt, 7) = frmData.txtRemarks
.Columns("A:G").Columns.AutoFit
.Range("A1:G1").Font.Bold = True
.Range("A1:G1").LineStyle = xlDash
End If
End With
Dim IdVal As Integer
IdVal = fn_LastRow(Sheets("Data"))
frmData.txtId = IdVal
ErrOccured:
'TurnOn screen updating
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub
I will always be grateful to you.
Then, please try the next way. I will try creating of the necessary DB, table and fields using Excel VBA, too:
Copy the next piece of code which will create an empty DB, on the path you want:
Sub CreateEmptyDB()
Dim strPath As String, objAccess As Object
strPath = "C:\Your path\testDB"
Set objAccess = CreateObject("Access.Application")
Call objAccess.NewCurrentDatabase(strPath)
objAccess.Quit
End Sub
Programatically create the necessary table with its fields (`Start Date' added only to see how this type of data is handled...):
Sub createTableFields()
'It needs a reference to `Microsoft ActiveX Data Objects 2.x Library` (x = 2 to 9)
Dim Catalog As Object, cn As ADODB.Connection
Dim dbPath As String, scn As String, strTable As String
dbPath = "C:\Teste VBA Excel\testAccess\testDB.accdb"
strTable = "MySpecial_Table"
scn = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & dbPath & ";"
Set Catalog = CreateObject("ADOX.Catalog")
Set cn = New ADODB.Connection
With cn
.Open scn
.Execute "CREATE TABLE " & strTable & " ([Name] text(255) WITH " & _
"Compression, " & "[Gender] text(255) WITH Compression, " & _
"[Location] text(255) WITH Compression, " & _
"[Address] text(255) WITH Compression, " & _
"[Number] number, " & _
"[Remarks] text(255) WITH Compression, " & _
"[Start Date] datetime)"
End With
cn.Close
End Sub
Add records to the newly created DB/Table:
Sub FillDataInDB()
'It needs a reference to `Microsoft ActiveX Data Objects 2.x Library` (x = 2 to 9)
Dim AccessDB As String, strTable As String, sql As String
Dim con As ADODB.Connection, rs As ADODB.Recordset, lastNo As Long
AccessDB = "C:\Teste VBA Excel\testAccess\testDB.accdb"
strTable = "MySpecial_Table"
Set con = CreateObject("ADODB.connection")
con.Open "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & AccessDB
sql = "SELECT * FROM " & strTable
Set rs = CreateObject("ADODB.Recordset")
rs.CursorType = 1 'adOpenKeyset on early binding
rs.LockType = 3 'adLockOptimistic on early binding
rs.Open sql, con
If rs.RecordCount = 0 Then
lastNo = 0 'when no records in the table
Else
rs.MoveLast: lastNo = rs("Number") 'the last recorded value
End If
rs.AddNew
rs("Name") = "Test name" 'frmData.txtName
rs("Gender") = "Test gender" 'GenderValue
rs("Location") = "Test Location" 'frmData.txtLocation.Value
rs("Address") = "Test Address" 'frmData.txtEAddr
rs("Number") = IIf(lastNo = 0, 100, lastNo + 1) 'auto incrementing against the last value
'but starting from 100
'you can use frmData.txtCNum
rs("Remarks") = "Remarkable table..." 'frmData.txtRemarks
rs("Start Date") = Date
rs.Update
rs.Close: con.Close
Set rs = Nothing: Set con = Nothing
End Sub
Run the first two pieces of code in consecutive order (only once) and then start playing with the third one...
You can read the newly created DB Table (returning in an Excel sheet) in this way:
Sub ADO_Connection_ReadTable()
Dim conn As New Connection, rec As New Recordset, sh As Worksheet
Dim AccessDB As String, connString, query As String, strTable As String
AccessDB = "C:\Teste VBA Excel\testAccess\testDB.accdb"
strTable = "MySpecial_Table"
Set sh = ActiveSheet 'use here the sheet you want
connString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & AccessDB
conn.Open connString
query = "SELECT * from " & strTable & ";"
rec.Open query, conn
'return in the sheet
sh.cells.ClearContents
'getting data from the recordset if any and returning some in columns A:B:
If (rec.RecordCount <> 0) Then
Do While Not rec.EOF
With sh.Range("A" & sh.cells(Rows.count, 1).End(xlUp).row).Offset(1, 0)
.Value2 = rec.fields(0).Value
.Offset(0, 1).Value2 = rec.fields(3)
End With
rec.MoveNext
Loop
End If
rec.Close: conn.Close
End Sub
You can use a query to return specific data according to a specific table field. You can find plenty of examples on the internet.
I tried also showing how to handle an automate recording for the 'Number' field. Of course, if you are able to keep track of it in a different way, you can record it as you need/wont.
Please, test the above code(s) and send some feedback. You can use the DB path as a Private constant at the module level and much other ways to optimize the code. It is just a minimum workable solution only showing the way... :)

VBA - Executing ADODB.CommandText

I promised myself I would not post this because I have this delusional thought that I am too good of a programmer, yet here we are.
I have altered what I posted earlier last week trying to figure out how to write a VBA function that would write data from an Excel Range to an MS SQL Table. That worked.
Towards the end of the program, I do not know how to construct the final execution of the code; I have tried everything from using the Command.Text in the upper levels, setting it to a Recordset, then executing the recordset, but nothing will make the little VBA troll happy. Here is what I currently have written:
Sub Connection()
Dim Tbl As String
Dim InsertQuery As New ADODB.Command
InsertQuery.CommandType = adCmdText
Dim xlRow As Long, xlCol As Integer
Dim DBconnection As New ADODB.Connection
Dim ConnString As String
Dim rst As New ADODB.Recordset
Dim a As Integer, sFieldName As String
Dim db As DAO.Database
Dim CurrentDb As Database
Dim ConnectionStr
ConnectionStr = "Provider=sqloledb;Server="";Inital Catalog="";Integrated Security=SSPI;User ID="";Password="""
DBconnection.Open ConnectionStr
xlRow = 1 'only one row being used *as of now*, and that is the top row in the excel sheet
xlCol = 119 'First column of misc. data
While Cells(xlRow, xlCol) <> ""
If LH = True Then
Tbl = "Info.CaseBLH"
InsertQuery.CommandText = "INSERT INTO " & Tbl & " VALUES('"
ElseIf RH = True Then
Tbl = "Info.CaseBRH"
InsertQuery.CommandText = "INSERT INTO " & Tbl & " VALUES('"
Else
MsgBox ("No available sheets")
'Application.Quit
End If
NK21Data.TableDefs(Tbl).Fields.Count
For a = 1 To Fields.Count - 1
'For xlCol = 119 To 230 'columns DO1 to HV1
Fields.Item(a) = Replace(Cells(xlRow, xlCol), "'", "''") & "', '" 'Includes mitigation for apostrophes in the data
If Cells(xlRow, xlCol) = "" Then
rst.Fields.Item(a) = "NULL"
End If
xlCol = xlCol + 1
Next a
a = a + 1
Fields.Item(a) = (Format(Now(), "M/D/YYYY") & "')" & vbCrLf)
Wend
'On Error GoTo ErrorHandler
DBconnection.Execute (InsertQuery.CommandText)
DBconnection.Close
Set DBconnection = Nothing
ErrorHandler:
If Err.Number <> 0 Then
Msg = "Error # " & Str(Err.Number) & " was generated by " _
& Err.Source & Chr(13) & "Error Line: " & Erl & Chr(13) & Err.Description
MsgBox Msg, , "Error", Err.HelpFile, Err.HelpContext
End If
End Sub
The error I get is:
Command text was not set for the command object.
This error occurs at:
DBconnection.Execute (InsertQuery.CommandText)
If I try using the following:
InsertQuery = DBconnection.Execute
I will get the following error:
Argument not optional
I've been at this for about (give or take) three days and I'm now having nightmares about it so if someone can help me figure out what to do for this I would greatly appreciate it.
I fixed up and cleaned the code from my earlier answer, tested it to work:
Here's the code:
Option Explicit
Sub DoItThen()
Dim i As Integer, sqlIns As String, sqlVals As String
Dim InsertQuery As New ADODB.Command
Dim firstRow As Long, firstCol As Integer, lastCol As Integer, currRow As Integer
Dim DBconnection As New ADODB.Connection
Dim ConnString As String
ConnString = "Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=Example;Data Source=MYMACHINENAME"
DBconnection.Open ConnString
InsertQuery.ActiveConnection = DBconnection
InsertQuery.CommandType = adCmdText
''build the command text side by side, named columns and values with param placeholders
sqlIns = "INSERT INTO person("
sqlVals = " VALUES("
''i could work these out by scanning the sheet i suppose. hardcoded for now
firstRow = 2
firstCol = 3
lastCol = 5
''generate the SQL - its this that lets the column names come in any order in the sheet
For i = firstCol To lastCol
sqlIns = sqlIns & Cells(firstRow, i) & ","
sqlVals = sqlVals & "?,"
InsertQuery.Parameters.Append InsertQuery.CreateParameter("p" & i - firstCol, adVarChar, adParamInput, 255)
Next i
''chop off the extra trailing commas and form a syntax correct command
InsertQuery.CommandText = Left$(sqlIns, Len(sqlIns) - 1) & ")" & Left$(sqlVals, Len(sqlVals) - 1) & ")"
''iterate the data part of the sheet and execute the query repeatedlty
currRow = firstRow + 1
While Cells(currRow, firstCol) <> ""
For i = firstCol To lastCol
InsertQuery.Parameters("p" & i - firstCol).Value = Cells(currRow, i)
Next i
InsertQuery.Execute , , adExecuteNoRecords ''dont return a resultset
currRow = currRow + 1
Wend
DBconnection.Close
Set DBconnection = Nothing
ErrorHandler:
If Err.Number <> 0 Then
MsgBox Err.Description
End If
End Sub
It takes the first row as the names of the columns in the db table - any order is fine
It builds a command and populates the parameters
It repeatedly fills the values and executes the query, populating the table
OK; don't shoot me - I'm no VBA whizz but I'm saying you should strive to make your code more like this:
Sub DoItThen()
Dim a As Integer, sql as String
Dim InsertQuery As New ADODB.Command
Dim xlRow As Long, xlCol As Integer
Dim DBconnection As New ADODB.Connection
Dim ConnString As String
ConnString = "Provider=sqloledb;Server="";Inital Catalog="";Integrated Security=SSPI;User ID="";Password="""
DBconnection.Open ConnString
InsertQuery.ActiveConnection = conn
InsertQuery.CommandType = adCmdText
If LH = True Then
sql = "INSERT INTO Info.CaseBLH VALUES(#p1"
ElseIf RH = True Then
sql = "INSERT INTO Info.CaseBRH VALUES(#p1"
Else
MsgBox ("No available sheets")
'Application.Quit
End If
''does this do anything? I don't know
NK21Data.TableDefs(Tbl).Fields.Count
''let us add some placeholders to the command: we add count-2 because we already have one ? in the command
''ps; don't know where you got fields.count from
For a = 2 To Fields.Count - 1
sql = sql & ",#p" & a
Next a
''finish off our command
InsertQuery.CommandText = sql & ")"
''now we have a command like INSERT INTO tbl VALUES(#p1, #p2, #p3.."
''and setting the command text might pre-populate the parameters collection
''with the same number of parameters as are in the command, so let's clear it and
''add the parameters again ourselves so we can control the type
InsertQuery.Parameters.Clear
''create a load of parameters
For a = 1 To Fields.Count - 1
InsertQuery.Parameters.Append InsertQuery.CreateParameter("#p" & a, adVarChar, adParamInput, 255) 'adjust if you have strings longer than 255
Next a
''Now all the parameters are set etc, we just go through all the rows,
''and all the columns and set the values, then execute the command, then change the values and execute again
''--> set the command up once and repeatedly execute it
xlRow = 1 'only one row being used *as of now*, and that is the top row in the excel sheet
xlCol = 119 'First column of misc. data
While Cells(xlRow, xlCol) <> ""
For a = 1 To Fields.Count - 1
InsertQuery.Parameters("#p" & a).Value = Cells(xlRow, xlCol + a)
Next a
InsertQuery.Execute , , adExecuteNoRecords ''dont return a resultset
Wend
DBconnection.Close
Set DBconnection = Nothing
ErrorHandler:
If Err.Number <> 0 Then
Msg = "Error # " & Str(Err.Number) & " was generated by " _
& Err.Source & Chr(13) & "Error Line: " & Erl & Chr(13) & Err.Description
MsgBox Msg, , "Error", Err.HelpFile, Err.HelpContext
End If
End Sub
You have 100 columns and 1000 rows to insert from an excel sheet:
You set up the command, INSERT INTO ... VALUES (... 100 #parameter placeholders...)
You clear the parameters collection (in case VBA has decided to 'help' by creating them) and add a load of parameters that represent your strings in your sheet
You then iterate the sheet, row by row, setting each column value on the row, into the relevant parameter and after you set all the columns (100 times), you execute the command then move to the next row, set the values again and execute again (1000 times)
I've got absolutely no way of testing this, sorry - it's my best guess but I fully expect it still has some error because I don't really know where you got Fields from. The answer with 8 votes from here was helpful: VBA, ADO.Connection and query parameters - I distinctly recall from when I was using VB6 about 20 years ago, that ADODB would try and prepopulate the parameters collection in certain circumstances, with its guesses at the parameter types; we routinely cleared it and added our own, but you might have some success proceeding with the default parameters it makes
The names of the parameters are not relevant; only the position. There's no requirement that #p1 from the query string matches the #p1 name given for the parameter - if the first parameter in the string were called #bob and you then cleared and added a parameter named #alice, whatever #alice's value was would be assigned to #bob because #bob is first in the query and #alice is first in the parameters collection. I used #pXXX as a parameter name for ease of reference in both cases
Here is my basic ADODB Execute template. This isn't meant to be an answer but more a helpful post. It should assist in showing you what you're doing incorrectly, which appears to be simple syntax issues as well as being really new to this (formatting and other pieces of code suggest that maybe you've gotten yourself "googled into a corner.").
Private Sub ADODBExample()
Dim vbSql As String, cnnstr as string
Dim cnn As ADODB.Connection
vbSql = "sql statement ;"
Set cnn = New Connection
cnnstr = "Provider=SQLOLEDB;Data Source=SERVERNAME;Initial Catalog=DBNAME;User ID=USERID;Password=PASSWORD; Trusted_Connection=No"
cnn.Open cnnstr
cnn.Execute vbSql
cnn.Close
Set cnn = Nothing
End Sub
More helpful tips -
Stop looping through cells, ranges and other worksheet/book objects. Learn to use arrays - itll make processing way better.
Simplicity is best. You appear to doing what I consider alot of unnecessary things, but then again I dont know all the requirements.
So I amended the code to the following:
Sub Connection()
Dim i As Integer, sqlIns As String, sqlVals As String
Dim InsertQuery As New ADODB.Command
Dim firstRow As Long, firstCol As Integer, lastCol As Integer, currRow As Integer
Dim DBconnection As New ADODB.Connection
Dim ConnString As String
Dim Tbl As String
ConnString = "Provider=sqloledb;Server=SERVER;Inital Catalog=DB;Integrated Security=SSPI;User ID=ID;Password=PW;"
DBconnection.Open ConnString
InsertQuery.ActiveConnection = DBconnection
InsertQuery.CommandType = adCmdText
If LH = True Then
Tbl = "Info.CaseBLH"
sqlIns = "INSERT INTO Info.CaseBLH("
ElseIf RH = True Then
Tbl = "Info.CaseBRH"
sqlIns = "INSERT INTO Info.CaseBRH("
Else
MsgBox ("No available sheets")
'Application.Quit
End If
''build the command text side by side, named columns and values with param placeholders
sqlVals = " VALUES("
''i could work these out by scanning the sheet i suppose. hardcoded for now
firstRow = 1
firstCol = 119
lastCol = 231
''generate the SQL - its this that lets the column names come in any order in the sheet
For i = firstCol To lastCol
sqlIns = sqlIns & Cells(firstRow, i) & ","
sqlVals = sqlVals & "?,"
InsertQuery.Parameters.Append InsertQuery.CreateParameter("p" & i - firstCol, adVarChar, adParamInput, 255)
Next i
''chop off the extra trailing commas and form a syntax correct command
InsertQuery.CommandText = Left$(sqlIns, Len(sqlIns) - 1) & ")" & Left$(sqlVals, Len(sqlVals) - 1) & ")"
''iterate the data part of the sheet and execute the query repeatedlty
currRow = firstRow ' - not needed as the data is automatically replaced with the code above
While Cells(currRow, firstCol) <> ""
For i = firstCol To lastCol - 1
InsertQuery.Parameters("p" & i - firstCol).Value = Cells(currRow, i)
Next i
InsertQuery.Execute , , adExecuteNoRecords ''dont return a resultset
Wend
DBconnection.Close
Set DBconnection = Nothing
ErrorHandler:
If Err.Number <> 0 Then
MsgBox Err.Description
End If
End Sub
Right at the
InsertQuery.Execute , , adExecuteNoRecords
Line I'm getting a error telling me there is a syntax error around the ':' which doesn't make any sense to me. If I append my code to send the error to the error handler, every single row it cycles through throws me an error saying there is a syntax error around '-' or '/'. I think it has something to do with the parameter.value line.

Reference controls on a form from a function/sub

I have a strong suspicion that what I'm wanting to do is not possible, but I could use the confirmation.
I have a Class Library that I have created to handle some common Subs & Functions that I use a lot (in some form or fashion).
Some of those Subs & Functions can take a go long while (typically in cases where records are being iterated through & processed in some fashion). In those instances I usually have had the function update a progress window (text and stepping a progress bar).
Now that I'm genericizing to put them in the class library I obviously can't anticipate a specific form or control being there. But I'd like to retain the functionality. So what I'm hoping to do is create optional input variables for the RichTextBox & ProgressBar that would be getting updated, so that I can then pass in references to the controls that should be used for statusing.
Is this possible? If so, how? When I try to define any inputs as Windows.Forms.<anything> Intellisense drops out (strongly hinting that I can't type them as controls).
Thanks!
UPDATE: The function I'm trying to add the references to follows, errors are indicated for either System.Windows.Forms.<X>
Public Function ResOut(ByVal D As DataTable, ByVal epath As String, ByVal SAName As String, ByVal Parent As String, ByRef PBar As System.Windows.Forms.ProgressBar, _
ByRef RTB As System.Windows.Forms.RichTextBox) As String
'
Dim res As String = ""
Dim E As New Microsoft.Office.Interop.Excel.Application
Dim wb As Microsoft.Office.Interop.Excel.Workbook = Nothing
Dim ws As Microsoft.Office.Interop.Excel.Worksheet = Nothing
Dim x As Long = 0
Dim f As Long = 1
Dim s As New JMLib.Status
Dim Rng As Microsoft.Office.Interop.Excel.Range
'Define the range
Rng = ws.Range("A1:" & ColNumToStr(D.Columns.Count, epath) & D.Rows.Count)
'Create the array
Dim OArr(D.Rows.Count, x) As Object
'Create a workbook for the data and capture the workbook and sheet for ease of reference
Try
wb = E.Workbooks.Add
ws = wb.Worksheets(1)
Catch ex As Exception
res = "Encountered an error while creating the new workbook to export the results to. No data can be returned."
EL.AddErr(res & " ResOut was called by " & Parent & ". Error Details: " & ex.Message, epath)
End Try
'Fill in headers
If res = "" Then
Try
For Each c As DataColumn In D.Columns
ws.Range("A1").Offset(0, x).Value = c.ColumnName
x = x + 1
Next
Catch ex As Exception
res = "Encountered an error while filling in the column headers. This will prevent any data from being returned."
EL.AddErr(res & " ResOut was called by " & Parent & ". Error Details: " & ex.Message, epath)
End Try
End If
'Setup the step & frequency for the Step Progress bar
'Dim t() As Long = s.StatSetup(QR.Rows.Count, 58, "Query Runner\ResOut\" & QName, Replace(My.Settings.EPath, "<user>", Environment.UserName) & DStamp() & " Query Scheduler Log.txt")
'f = t(0)
'SProg.Step = t(1)
'Convert the datatable to a 2D array
If res = "" Then
Try
'Fill it
x = 0
For r As Long = 0 To D.Rows.Count - 1 Step 1
Dim dr As DataRow = D.Rows(r)
For c As Integer = 0 To D.Columns.Count - 1 Step 1
OArr(r, c) = dr.Item(c)
Next
x = x + 1
Next
Catch ex As Exception
res = "Encountered an error while outputing the " & x + 1 & "-th record of " & D.Rows.Count & ". No data will be output."
EL.AddErr(res & " ResOut was called by " & Parent & ". Error Details: " & ex.Message, epath)
End Try
End If
'output the array to the target range
If res = "" Then
Try
Rng.Value = OArr
'Save the workbook
wb.SaveAs(SAName)
wb.Close(SaveChanges:=False)
Catch ex As Exception
res = "Encountered an error during the export of the results. Some data may have been exported. Review the contents of the Excel workbook that will be visible following this message for more" _
& " details."
E.Visible = True
wb.Activate()
EL.AddErr(res & " ResOut was called by " & Parent & ". Error Details: " & ex.Message, epath)
End Try
Else
'Close the workbook without saving
wb.Close(SaveChanges:=False)
End If
'Cleanup the application
Try
E.Quit()
System.Runtime.InteropServices.Marshal.ReleaseComObject(E)
E = Nothing
wb = Nothing
ws = Nothing
s = Nothing
Rng = Nothing
OArr = Nothing
f = Nothing
x = Nothing
Catch ex As Exception
EL.AddErr("Encountered an error while cleaning up the resources used in JMLib\ResOut. ResOut was called by " & Parent & ". Error Details: " & ex.Message, epath)
End Try
Return res
End Function
I did this by adding references to the progress bar/text box to the Class.
When the class invoked you can set the references or not.
In the class if the reference ISNOT NOTHING I update the progress bar/textbox.
Nothing specific about being in a user written class - here is an example from some library code:
Sub DisplayParcels(aAPNs() As String,...., Optional ProgBar As ProgressBar = Nothing)
...
If ProgBar IsNot Nothing Then
ProgBar.Maximum = aAPNs.Length
ProgBar.Value = 0
ProgBar.Visible = True
End If
...
For Each sAPN In aAPNs
...
If ProgBar IsNot Nothing Then ProgBar.Value = iCnt
Next
...
If ProgBar IsNot Nothing Then ProgBar.Visible = False
End Sub

Read a text file which contains SQL code to create tables in a database

I need code to read a .txt file which is in my project bin\debug directory that contains SQL code to create tables in a large number it size of 936kb
This following code only I'm using...
By using this it gives result like table created but it is not reading the file... there is nothing in the database
Public Function readTextFile(ByVal fileName As String) As String
Dim strContent As String()
Dim x As String = ""
Try
'fileName = "CSYSS802.txt"
If Not System.IO.File.Exists(fileName) Then
'o Until EOF()
strContent = System.IO.File.ReadAllLines(fileName)
For Each Str As String In strContent
x = x + Str
Next
readTextFile = x
End If
Catch ex As Exception
MessageBox.Show(ex.ToString)
End Try
readTextFile = x
End Function
Public Sub createTable(ByVal vdbs As String, ByVal file As String)
username = frmlogin.txtusername.Text
password = frmlogin.txtusername.Text
vsvr = vServer
vdb = Trim$(vdbs)
strCon1 = "Server=" & vsvr & ";Database=" & vdb & ";uid=" & username & ";pwd=" & password & ";"
sqlCon1 = New SqlClient.SqlConnection(strCon1)
sqlCon1.Open()
Dim arr() As String
arr = Split(readTextFile(file), "GO")
Dim i As String
For Each i In arr
If i <> "" Then
Dim cmd2 As New SqlClient.SqlCommand("" & i & "")
cmd2.CommandType = CommandType.Text
cmd2.ExecuteNonQuery()
End If
Next
End Sub
In the readTextFile function, it will only attempt to read the text from the text file if the file DOESN'T exist. If the text file exists then the function returns an empty string and if the text file doesn't exist, the function will throw a file not found exception.
Replace:
If Not System.IO.File.Exists(fileName) Then
with:
If System.IO.File.Exists(fileName) = True Then
You might also want to include an Else clause in case the file doesn't exist as it won't throw an error since you have handled it correctly.
If System.IO.File.Exists(fileName) = True Then
strContent = System.IO.File.ReadAllLines(fileName)
For Each Str As String In strContent
x &= Str
Next
Return x
Else
MessageBox.Show("The file '" & fileName & "' does not exist.")
Return ""
End If
My Self I had Found The solution..I attache the Following Code...It now Creating All tables Properly..
Make sure that each Sql Commands in your Text File ends with go.. because i used "GO" Keyword to split the text...
Public Sub createTable(ByVal vdbs As String, ByVal file As String)
username = frmlogin.txtusername.Text
password = frmlogin.txtusername.Text
vsvr = vServer
vdb = Trim$(vdbs)
strCon1 = "Server=" & vsvr & ";Database=" & vdb & ";uid=" & username & ";pwd=" & password & ";"
sqlCon1 = New SqlClient.SqlConnection(strCon1)
sqlCon1.Open()
Dim arr() As String
arr = Split(readTextFile(file), " GO ")
Dim i As String
For Each i In arr
If i <> "" Then
Dim cmd2 As New SqlClient.SqlCommand("" & i & "", sqlCon1)
cmd2.CommandType = CommandType.Text
cmd2.ExecuteNonQuery()
End If
Next
End Sub
Public Function readTextFile(ByVal file As String) As String
Dim fso As New System.Object
Dim ts As Scripting.TextStream
Dim sLine As String
fso = CreateObject("Scripting.FileSystemObject")
ts = fso.openTextFile(file)
Do Until ts.AtEndOfStream
sLine = sLine & " " & ts.ReadLine
Loop
ts.Close()
fso = Nothing
readTextFile = sLine
End Function

Visio 2010 VBA autoconnect

First time user of VBA coding with Visio here!
I am using Visio 2010 Pro
I am trying to automate a the drawing of a system architecture diagram using VBA. The data source is an Excel sheet. Hopefully this is the result...
I have written VBA to read the Excel sheet, and can create the shapes on the page with a bit of help from the internet (thanks everyone!)
The path I was looking to take was:
Drop Objects for each of the systems first
Using autoconnect, loop through the recordset and draw the links (showing the integration) between systems
From the Excel data, the links know the name of the shapes they are connecting (and I assign the shape.name when I drop the shapes on the page).
I do not know how to use the shape name to identify a unique shape object (which could be used as parameters for the autoconnect method)
Is there a better or easier way to do this?
I have seen the Autoconnect example (http://msdn.microsoft.com/en-us/library/office/ms427221%28v=office.12%29.aspx); which works fine if I have a handle on the objects created at run time (I.e. a variable for each object created. In my case, I am not storing that anywhere. I gave thought to storing this info in an array and then looping through same to find the object.
I’d like some thoughts as to the best way to do this. Given I am a Visio newbie, some sample (working?) code would be very well received.
The code I am particularly interested in sorting out is commented with "connect the shapes..."
One other little issue I have; is that a new stencil is created every time that I run the VBA. How can I still choose a master without doing this?
Many thanks!
I wasn’t sure how much info people would need to get an idea as to what I am trying to achieve and so have attached the code I’ve written/hacked/plagiarised to date
Public Sub DrawSystem()
Dim strConnection As String
Dim strCommand As String
Dim vsoDataRecordset As Visio.DataRecordset
strConnection = "Provider=Microsoft.ACE.OLEDB.12.0;" _
& "User ID=Admin;" _
& "Data Source=" + "b:\visio\Objects2;" _
& "Mode=Read;" _
& "Extended Properties=""HDR=YES;IMEX=1;MaxScanRows=0;Excel 12.0;"";" _
& "Jet OLEDB:Engine Type=34;"
strCommand = "SELECT * FROM [Sheet1$]"
' load the data ...
Set vsoDataRecordset = ActiveDocument.DataRecordsets.Add(strConnection, strCommand, 0, "Objects")
'Stencil document that contains master
Dim stnObj As Visio.Document
'Master to drop
Dim mastObj As Visio.Master
'Pages collection of document
Dim pagsObj As Visio.Pages
'Page to work in
Dim pagObj, activePageObj As Visio.Page
'Instance of master on page
Dim shpObj As Visio.Shape
Dim shpFrom As Variant
Dim shpTo As Variant
Set stnObj = Documents.Add("Basic Shapes.vss")
' create a new page in the document
Set pagObj = ThisDocument.Pages.Add
pagObj.Name = "Page-" & Pages.Count
' -------------------------------------------------------
' LOOP THROUGH THE RECORDSET
' -------------------------------------------------------
Dim lngRowIDs() As Long
Dim lngRow As Long
Dim lngColumn As Long
Dim varRowData As Variant
' process the ENTITY records
Debug.Print "PROCESSING ENTITY RECORDS"
lngRowIDs = vsoDataRecordset.GetDataRowIDs("")
' draw rectangles for systems
Set mastObj = stnObj.Masters("Rectangle")
'Iterate through all the records in the recordset.
For lngRow = LBound(lngRowIDs) To UBound(lngRowIDs)
varRowData = vsoDataRecordset.GetRowData(lngRow)
If varRowData(2) = "ENTITY" Then
' draw a new object on the created page with the correct details
' TODO - work out how to programmatically draw them in an appropriate location
Set shpObj = pagObj.Drop(mastObj, lngRow / 2, lngRow / 2)
' set the appropriate attributes on the new object from the dataset
shpObj.Name = varRowData(3)
shpObj.Text = varRowData(7)
shpObj.data1 = varRowData(3)
shpObj.data2 = varRowData(7)
shpObj.Data3 = varRowData(8)
shpObj.Cells("Width") = 0.75
shpObj.Cells("Height") = 0.5
Debug.Print ("Created Object: " & varRowData(3) & " : ID = " & shpObj.ID)
Else
Debug.Print ("SKIPPED:" & varRowData(2) & " : " & varRowData(0))
End If
Next lngRow
' process the LINK records
Debug.Print "PROCESSING LINK RECORDS"
lngRowIDs = vsoDataRecordset.GetDataRowIDs("")
Set mastObj = stnObj.Masters("Dynamic Connector")
'Iterate through all the records in the recordset.
For lngRow = LBound(lngRowIDs) To UBound(lngRowIDs)
' only process LINK records
If varRowData(2) = "LINK" Then
Debug.Print ("Joining! " & varRowData(4) & " - " & varRowData(5) & " with " & varRowData(6))
Set shpObj = pagObj.Drop(mastObj, 2 + lngRow * 3, 0 + lngRow * 3)
varRowData = vsoDataRecordset.GetRowData(lngRow)
shpObj.Name = varRowData(6)
shpObj.Text = varRowData(7)
' connect the shapes ...
shpFrom = activePageObj.Shapes(varRowData(4))
shpTo = activePageObj.Shapes(varRowData(5))
shpFrom.AutoConnect shpTo, visAutoConnectDirNone
Else
Debug.Print ("LINK SKIPPED:" & varRowData(2) & " : " & varRowData(0))
End If
Next lngRow
End Sub
Here is the data file that I have been using to test ... (copy and paste into Excel)
1,,ENTITY,A,,,1,1: A,ONE
2,,ENTITY,B,,,2,2: B,TWO
3,,ENTITY,C,,,3,3: C,THREE
13,1,LINK,LINK1,A,B,13.1,13.1: LINK1,LINK1
13,2,LINK,LINK2,A,C,13.2,13.2: LINK2,LINK2
13,2,LINK,LINK2,C,B,13.2,13.2: LINK2,LINK2
This code should work for you:
Public Sub DrawSystem()
Dim strConnection As String
Dim strCommand As String
Dim vsoDataRecordset As Visio.DataRecordset
strConnection = "Provider=Microsoft.ACE.OLEDB.12.0;" _
& "User ID=Admin;" _
& "Data Source=" + "d:\Book1.xlsx;" _
& "Mode=Read;" _
& "Extended Properties=""HDR=YES;IMEX=1;MaxScanRows=0;Excel 12.0;"";" _
& "Jet OLEDB:Engine Type=34;"
strCommand = "SELECT * FROM [Sheet1$]"
Set vsoDataRecordset = ActiveDocument.DataRecordsets.Add(strConnection, strCommand, 0, "Objects")
Dim stnObj As Visio.Document
Dim mastObj As Visio.Master
Dim pagsObj As Visio.Pages
Dim pagObj, activePageObj As Visio.Page
Dim shpObj As Visio.Shape
Dim shpFrom As Visio.Shape
Dim shpTo As Visio.Shape
Set stnObj = Documents.OpenEx("Basic Shapes.vss", visOpenDocked)
Set pagObj = ThisDocument.Pages.Add()
Dim lngRowIDs() As Long
Dim lngRow As Long
Dim lngColumn As Long
Dim varRowData As Variant
Debug.Print "PROCESSING ENTITY RECORDS"
lngRowIDs = vsoDataRecordset.GetDataRowIDs("")
Set mastObj = stnObj.Masters("Rectangle")
For lngRow = LBound(lngRowIDs) To UBound(lngRowIDs)
varRowData = vsoDataRecordset.GetRowData(lngRow)
If varRowData(2) = "ENTITY" Then
Set shpObj = pagObj.Drop(mastObj, lngRow / 2, lngRow / 2)
shpObj.Name = varRowData(3)
shpObj.Text = varRowData(7)
shpObj.Data1 = varRowData(3)
shpObj.Data2 = varRowData(7)
shpObj.Data3 = varRowData(8)
shpObj.Cells("Width") = 0.75
shpObj.Cells("Height") = 0.5
End If
Next lngRow
lngRowIDs = vsoDataRecordset.GetDataRowIDs("")
Set mastObj = stnObj.Masters("Dynamic Connector")
For lngRow = LBound(lngRowIDs) To UBound(lngRowIDs)
varRowData = vsoDataRecordset.GetRowData(lngRow)
Debug.Print ("!ddd!!" & varRowData(2))
If varRowData(2) = "LINK" Then
Dim fromName As String
fromName = varRowData(4)
Dim toName As String
toName = varRowData(5)
Dim conName As String
conName = varRowData(6)
Set shpCon = pagObj.Drop(mastObj, 2 + lngRow * 3, 0 + lngRow * 3)
varRowData = vsoDataRecordset.GetRowData(lngRow)
shpCon.Name = conName
shpCon.Text = varRowData(7)
Set shpFrom = ActivePage.Shapes(fromName)
Set shpTo = ActivePage.Shapes(toName)
shpFrom.AutoConnect shpTo, visAutoConnectDirNone, shpCon
End If
Next lngRow
End Sub