Simple way to refresh power pivot from VBA in Excel 2010? - vba

I want to perform the equivalent actions of:
Power Pivot > Tables > Update All
Pivot Table Tools > Data > Refresh All
using VBA. All the tables are Excel tables contained within the file.
Is there a simple way to do this in Excel 2010?

For Pivot Tables update, this code will work smoothly :
ThisWorkbook.RefreshAll
Or, if your Excel version is too old :
Dim Sheet as WorkSheet, _
Pivot as PivotTable
For Each Sheet in ThisWorkbook.WorkSheets
For Each Pivot in Sheet.PivotTables
Pivot.RefreshTable
Pivot.Update
Next Sheet
Next Pivot
In Excel 2013, to refresh PowerPivot, it is a simple line ActiveWorkbook.Model.Refresh.
In Excel 2010, ... It is FAR more complicated! Here is the general code made by Tom Gleeson :
' ==================================================
' Test PowerPivot Refresh
' Developed By: Tom http://www.tomgleeson.ie
' Based on ideas by Marco Rosso, Chris Webb and Mark Stacey
' Dedicated to Bob Phillips a most impatient man ...
' Sep 2011
'
' =======================================================
Option Explicit
#If Win64 Then
Public Declare PtrSafe Sub Sleep Lib "Kernel32" (ByVal dwMilliseconds As Long)
#Else
Public Declare Sub Sleep Lib "Kernel32" (ByVal dwMilliseconds As Long)
#End If
Sub Refresh()
Dim lDatabaseID As String
Dim lDimensionID As String
Dim lTable As String
Dim RS As Object 'ADODB.Recordset
Dim cnn As Object 'ADODB.Connection
Dim mdx As String
Dim xmla As String
Dim cnnName As String
Dim lSPID As String
Dim lArray
Dim i As Long
On Error Resume Next
' For Excel 2013+ use connection name e.g. "Text InvoiceHeaders"
' Fr Excel 2010 use table name e.g. "InvoiceHeaders"
lTable = [TableToRefresh]
On Error GoTo 0
' if Excel 2013 onwards: use Connections or Model refresh option via Object Model
If Application.Version() > 14 Then
' "wake up" model
ActiveWorkbook.Model.Initialize
If lTable <> "" Then
ActiveWorkbook.Connections(lTable).Refresh
Else
ActiveWorkbook.Model.Refresh
End If
' For Excel 2013 that's all folks.
Exit Sub
End If
cnnName = "PowerPivot Data"
'1st "wake up" default PowerPivot Connection
ActiveWorkbook.Connections(cnnName).Refresh
'2nd fetch that ADO connection
Set cnn = ActiveWorkbook.Connections(cnnName).OLEDBConnection.ADOConnection
Set RS = CreateObject("ADODB.Recordset")
' then fetch the dimension ID if a single table specified
' FIX: need to exclude all rows where 2nd char = "$"
mdx = "select table_id,rows_count from $System.discover_storage_tables where not mid(table_id,2,1) = '$' and not dimension_name = table_id and dimension_name='<<<<TABLE_ID>>>>'"
If lTable <> "" Then
mdx = Replace(mdx, "<<<<TABLE_ID>>>>", lTable)
RS.Open mdx, cnn
lDimensionID = fetchDIM(RS)
RS.Close
If lDimensionID = "" Then
lDimensionID = lTable
End If
End If
' then fetch a valid SPID for this workbook
mdx = "select session_spid from $system.discover_sessions"
RS.Open mdx, cnn
lSPID = fetchSPID(RS)
If lSPID = "" Then
MsgBox "Something wrong - cannot locate a SPID !"
Exit Sub
End If
RS.Close
'Next get the current DatabaseID - changes each time the workbook is loaded
mdx = "select distinct object_parent_path,object_id from $system.discover_object_activity"
RS.Open mdx, cnn
lArray = Split(lSPID, ",")
For i = 0 To UBound(lArray)
lDatabaseID = fetchDatabaseID(RS, CStr(lArray(i)))
If lDatabaseID <> "" Then
Exit For
End If
Next i
If lDatabaseID = "" Then
MsgBox "Something wrong - cannot locate DatabaseID - refesh PowerPivot connnection and try again !"
Exit Sub
End If
RS.Close
'msgbox lDatabaseID
If doXMLA(cnn, lDatabaseID, lDimensionID) = "OK" Then
Sleep 1000
' refresh connections and any related PTs ...
ActiveWorkbook.Connections(cnnName).Refresh
End If
End Sub
Private Function doXMLA(cnn, databaseID As String, Optional dimensionID As String = "", Optional timeout As Long = 30)
Dim xmla As String
Dim lRet
Dim comm As Object ' ADODB.Command
' The XMLA Batch request
If dimensionID = "" Then
xmla = "<Batch xmlns=""http://schemas.microsoft.com/analysisservices/2003/engine""><Parallel><Process xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:ddl2=""http://schemas.microsoft.com/analysisservices/2003/engine/2"" xmlns:ddl2_2=""http://schemas.microsoft.com/analysisservices/2003/engine/2/2"" xmlns:ddl100_100=""http://schemas.microsoft.com/analysisservices/2008/engine/100/100""><Object><DatabaseID><<<DatabaseID>>></DatabaseID></Object><Type>ProcessFull</Type><WriteBackTableCreation>UseExisting</WriteBackTableCreation></Process></Parallel></Batch>"
xmla = Replace(xmla, "<<<DatabaseID>>>", databaseID)
Else
xmla = "<Batch xmlns=""http://schemas.microsoft.com/analysisservices/2003/engine""><Parallel><Process xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:ddl2=""http://schemas.microsoft.com/analysisservices/2003/engine/2"" xmlns:ddl2_2=""http://schemas.microsoft.com/analysisservices/2003/engine/2/2"" xmlns:ddl100_100=""http://schemas.microsoft.com/analysisservices/2008/engine/100/100""><Object><DatabaseID><<<DatabaseID>>></DatabaseID><DimensionID><<<DimensionID>>></DimensionID></Object><Type>ProcessFull</Type><WriteBackTableCreation>UseExisting</WriteBackTableCreation></Process></Parallel></Batch>"
xmla = Replace(xmla, "<<<DatabaseID>>>", databaseID)
xmla = Replace(xmla, "<<<DimensionID>>>", dimensionID)
End If
Set comm = CreateObject("ADODB.command")
comm.CommandTimeout = timeout
comm.CommandText = xmla
Set comm.ActiveConnection = cnn
comm.Execute
' Make the request
'On Error Resume Next - comment out on error as most are not trappable within VBA !!!
'lRet = cnn.Execute(xmla)
'If Err Then Stop
doXMLA = "OK"
End Function
Private Function fetchDatabaseID(ByVal inRS As Object, SPID As String) As String
Dim i As Long
Dim useThis As Boolean
Dim lArray
Dim lSID As String
lSID = "Global.Sessions.SPID_" & SPID
Do While Not inRS.EOF
'Debug.Print inRS.Fields(0)
If CStr(inRS.Fields(0)) = lSID Then
lArray = Split(CStr(inRS.Fields(1)), ".")
On Error Resume Next
If UBound(lArray) > 2 Then
' find database permission activity for this SPID to fetch DatabaseID
If lArray(0) = "Permissions" And lArray(2) = "Databases" Then
fetchDatabaseID = CStr(lArray(3))
Exit Function
End If
End If
End If
On Error GoTo 0
inRS.MoveNext
Loop
inRS.MoveFirst
fetchDatabaseID = ""
End Function
Private Function fetchSPID(ByVal inRS As Object) As String
Dim lSPID As String
lSPID = ""
Do While Not inRS.EOF
If lSPID = "" Then
lSPID = CStr(inRS.Fields(0).Value)
Else
lSPID = lSPID & "," & CStr(inRS.Fields(0).Value)
End If
inRS.MoveNext
Loop
fetchSPID = lSPID
End Function
Private Function fetchDIM(ByVal inRS As Object) As String
Dim lArray
Dim lSID As String
If Not inRS.EOF Then
fetchDIM = inRS.Fields(0)
Else
fetchDIM = ""
End If
End Function

Related

Retrieving MS Access Database to vb6 and filter data using 2 DTpicker for the sum rate

Private Sub Command5_Click()
Dim li As ListItem
Dim lngRunningTotal As Long
For Each li In ListView1.ListItems
lngRunningTotal = lngRunningTotal + CLng(li.SubItems(6)) 'calculate all the in Total min column
Next
sumText.Text = CStr(lngRunningTotal) 'display total
End Sub
I want to filter data using 2 DTpicker for the sum rate.
Here's how you can do it with the code you posted:
Private Sub Command5_Click()
Dim li As ListItem
Dim lngRunningTotal As Long
Dim iLogDateIndex As Integer
Dim iMinutesIndex As Integer
iLogDateIndex = 5
iMinutesIndex = 6
For Each li In ListView1.ListItems
If CDate(li.SubItems(iLogDateIndex)) >= DTPickerStart And CDate(li.SubItems(iLogDateIndex)) <= DTPickerEnd Then
lngRunningTotal = lngRunningTotal + CInt(li.SubItems(iMinutesIndex)) ' calculate all the in Total min column
End If
Next
' Display total
sumText.Text = CStr(lngRunningTotal)
End Sub
The following demonstrates how to filter your data using an SQL Statement:
Private Sub cmdQuery_Click()
Dim objAdoConnection As New ADODB.Connection
Dim objRecordset As ADODB.Recordset
Dim sConnectionString As String
Dim sSQLStatement As String
Dim sDatabaseFile As String
Dim itm As ListItem
' Path to Access database
sDatabaseFile = "C:\Temp\Stack\ADO\Database.accdb"
' Connection string
sConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0; Data Source=" & sDatabaseFile
' Open Connection
objAdoConnection.Open sConnectionString
' Open recordset with SQL query
sSQLStatement = "SELECT * FROM TimeCards WHERE WorkDate BETWEEN #" & DTPickerStart & "# AND #" & DTPickerEnd & "#"
Set objRecordset = objAdoConnection.Execute(sSQLStatement)
' Clear ListView
ListView1.ListItems.Clear
' Move Recordset to first record
objRecordset.MoveFirst
' Display record data
Do While Not objRecordset.EOF
' Add record to ListView
Set itm = ListView1.ListItems.Add(, , objRecordset.Fields("ID"))
itm.SubItems(1) = objRecordset.Fields("EmployeeID")
itm.SubItems(2) = objRecordset.Fields("WorkDate")
itm.SubItems(3) = objRecordset.Fields("WorkHours")
' Move to next record
objRecordset.MoveNext
Loop
' Close connection and release objects
objAdoConnection.Close
Set objRecordset = Nothing
Set objAdoConnection = Nothing
End Sub
I made a quick UI with two DTPicker controls and a Query button. The results get shown into a ListView control:
I also created a simple TimeCards table with the following data for testing:
You can research SQL Statements on the internet and find many ways you can filter your data.

Vb.net crystal report not displaying the items

I am trying to display all of the purchases in an inventory system but it is not displaying in my crystal report PS: this my first time using Crystal Report
here's my code:
Dim show As String = String.Empty
show &= "select * from purchase_report "
show &= "where buildnumber=#build"
Using conn As New SqlConnection("Server=WIN10;database=purchase_stock;user=admin_report;password=54321")
Using cmd As New SqlCommand
With cmd
.Connection = conn
.CommandType = CommandType.Text
.CommandText = show
.Parameters.AddWithValue("#build", report.Text)
End With
Try
conn.Open()
Dim da As New SqlDataAdapter(cmd)
Dim ds As New DataSet
da.Fill(ds)
If ds.Tables.Count > 0 Then
CrystalReportViewer1.ReportSource = ds.Tables.Count
End If
conn.Close()
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Using
End Using
End Sub
End Class
Try this code :
Change it as per your needs
Friend Function ViewReport(ByVal sReportName As String, _
Optional ByVal sSelectionFormula As String = "", _
Optional ByVal param As String = "") As Boolean
'Declaring variables
Dim intCounter As Integer
Dim intCounter1 As Integer
'Crystal Report's report document object
Dim objReport As New _
CrystalDecisions.CrystalReports.Engine.ReportDocument
'object of table Log on info of Crystal report
Dim ConInfo As New CrystalDecisions.Shared.TableLogOnInfo
'Parameter value object of crystal report
' parameters used for adding the value to the parameter.
Dim paraValue As New CrystalDecisions.Shared.ParameterDiscreteValue
'Current parameter value object(collection) of crystal report parameters.
Dim currValue As CrystalDecisions.Shared.ParameterValues
'Subreport object of crystal report.
Dim mySubReportObject As _
CrystalDecisions.CrystalReports.Engine.SubreportObject
'Sub report document of crystal report.
Dim mySubRepDoc As New _
CrystalDecisions.CrystalReports.Engine.ReportDocument
Dim strParValPair() As String
Dim strVal() As String
Dim index As Integer
Try
'Load the report
objReport.Load(sReportName)
'Check if there are parameters or not in report.
intCounter = objReport.DataDefinition.ParameterFields.Count
'As parameter fields collection also picks the selection
' formula which is not the parameter
' so if total parameter count is 1 then we check whether
' its a parameter or selection formula.
If intCounter = 1 Then
If InStr(objReport.DataDefinition.ParameterFields(_
0).ParameterFieldName, ".", CompareMethod.Text) > 0 Then
intCounter = 0
End If
End If
'If there are parameters in report and
'user has passed them then split the
'parameter string and Apply the values
'to their concurrent parameters.
If intCounter > 0 And Trim(param) <> "" Then
strParValPair = param.Split("&")
For index = 0 To UBound(strParValPair)
If InStr(strParValPair(index), "=") > 0 Then
strVal = strParValPair(index).Split("=")
paraValue.Value = strVal(1)
currValue = _
objReport.DataDefinition.ParameterFields(_
strVal(0)).CurrentValues
currValue.Add(paraValue)
objReport.DataDefinition.ParameterFields(_
strVal(0)).ApplyCurrentValues(currValue)
End If
Next
End If
'Set the connection information to ConInfo
'object so that we can apply the
'connection information on each table in the report
ConInfo.ConnectionInfo.UserID = <User Name>
ConInfo.ConnectionInfo.Password = <Password>
ConInfo.ConnectionInfo.ServerName = <Server Name>
ConInfo.ConnectionInfo.DatabaseName = <Database Name>
For intCounter = 0 To objReport.Database.Tables.Count - 1
objReport.Database.Tables(intCounter).ApplyLogOnInfo(ConInfo)
Next
' Loop through each section of the report then look
' through each object in the section
' if the object is a subreport, then apply login info
' on each table of that subreport
For index = 0 To objReport.ReportDefinition.Sections.Count - 1
For intCounter = 0 To _
objReport.ReportDefinition.Sections(_
index).ReportObjects.Count - 1
With objReport.ReportDefinition.Sections(index)
If .ReportObjects(intCounter).Kind = _
CrystalDecisions.Shared.ReportObjectKind.SubreportObject Then
mySubReportObject = CType(.ReportObjects(intCounter), _
CrystalDecisions.CrystalReports.Engine.SubreportObject)
mySubRepDoc = _
mySubReportObject.OpenSubreport(mySubReportObject.SubreportName)
For intCounter1 = 0 To mySubRepDoc.Database.Tables.Count - 1
mySubRepDoc.Database.Tables(_
intCounter1).ApplyLogOnInfo(_
ConInfo)sp;
mySubRepDoc.Database.Tables(_
intCounter1).ApplyLogOnInfo(ConInfo)
Next
End If
End With
Next
Next
'If there is a selection formula passed to this function then use that
If sSelectionFormula.Length > 0 Then
objReport.RecordSelectionFormula = sSelectionFormula
End If
'Re setting control
rptViewer.ReportSource = Nothing
'Set the current report object to report.
rptViewer.ReportSource = objReport
'Show the report
rptViewer.Show()
Return True
Catch ex As System.Exception
MsgBox(ex.Message)
End Try
End Function

Load Image VB.NET from SQL Server 2005 type Image

I have problem with load image in VB.NET 2005 from SQL SERVER 2005 type image.
SQL table "pegawai"
code varchar(15)
foto image
VB Code:
Sub SendQueryParam(ByVal ssql As String, ByVal ParamArray args() As Object)
On Error GoTo errSendQuery
sqlnote = True
mCMD.CommandText = ssql
For i = LBound(args) To UBound(args) Step 2
mCMD.Parameters.Item((i - LBound(args)) \ 2).Type = args(i)
mCMD.Parameters.Item((i - LBound(args)) \ 2).Size = 500
mCMD.Parameters.Item((i - LBound(args)) \ 2).Value = args(i + 1)
Next
mCMD.Execute()
Exit Sub
errSendQuery:
MsgBox("Send Query:" & Err.Description, , "Send Query Param")
sqlnote = False
End Sub
Sub Get_Recordset(ByRef mREC As ADODB.Recordset)
On Error Resume Next
If mREC.State = 1 Then mREC.Close()
mREC.CursorType = ADODB.CursorTypeEnum.adOpenKeyset
mREC.LockType = ADODB.LockTypeEnum.adLockBatchOptimistic
mREC.Source = mCMD
mREC.Open()
End Sub
Dim mEdit As New ADODB.Recordset
SendQueryParam("select * from pegawai where code='KR-00001.04.16'")
Get_Recordset(mEdit)
If Not mEdit.EOF Then
Dim photo() As Byte = CType(mEdit("foto").Value, Byte())
Dim ms As New MemoryStream(photo)
myimage.Image = Image.FromStream(ms)
End If
myimage is pictureBox as component in VB.NET
I got error "Parameter is not valid." in line "myimage.Image = Image.FromStream(ms)"
Thanks for your concern and GBU

Fast Export of Large Datatable to Excel Spreadsheet in VB.Net

I have an interesting conundrum here, how do I quickly (under 1 minute) export a large datatable (filled from SQL, 35,000 rows) into an Excel spreadsheet for users. I have code in place that can handle the export, and while nothing is "wrong" with the code per se, it is infuriatingly slow taking 4 minutes to export the entire file (sometimes longer if a user has less RAM or is running more on their system). Sadly, this is an improvement over the 10+ minutes it used to take using our old method. Simply put, can this be made any faster, without using 3rd party components? If so, how? My code is as follows, the slow down occurs between messageboxes 6 and 7 where each row is written. Thank you all for taking the time to take a look at this:
Private Sub btnTest_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnJeffTest.Click
Test(MySPtoExport)
End Sub
Private Sub Test(ByVal SQL As String)
'Declare variables used to execute the VUE Export stored procedure
MsgBox("start stop watch")
Dim ConnectionString As New SqlConnection(CType(ConfigurationManager.AppSettings("ConnString"), String))
Dim cmdSP As New SqlClient.SqlCommand
Dim MyParam As New SqlClient.SqlParameter
Dim MyDataAdapter As New SqlClient.SqlDataAdapter
Dim ExportDataSet As New DataTable
Dim FilePath As String
MsgBox("stop 1 - end of declare")
Try
' open the connection
ConnectionString.Open()
' Use the connection for this sql command
cmdSP.Connection = ConnectionString
'set this command as a stored procedure command
cmdSP.CommandType = CommandType.StoredProcedure
'get the stored procedure name and plug it in
cmdSP.CommandText = SQL
'Add the Start Date parameter if required
Select Case StDt
Case Nothing
' there's no parameter to add
Case Is = 0
' there's no parameter to add
Case Else
'add the parameter name, it's direction and its value
MyParam = cmdSP.Parameters.Add("#StartDate", SqlDbType.VarChar)
MyParam.Direction = ParameterDirection.Input
MyParam.Value = Me.txtStartDate.Text
End Select
MsgBox("stop 2 - sql ready")
'Add the End Date parameter if required
Select Case EdDt
Case Nothing
' there's no parameter to add
Case Is = 0
' there's no parameter to add
Case Else
'add the parameter name, it's direction and its value
MyParam = cmdSP.Parameters.Add("#EndDate", SqlDbType.VarChar)
MyParam.Direction = ParameterDirection.Input
MyParam.Value = Me.txtEndDate.Text
End Select
'Add the single parameter 1 parameter if required
Select Case SPar1
Case Is = Nothing
' there's no parameter to add
Case Is = ""
' there's no parameter to add
Case Else
'add the parameter name, it's direction and its value
MyParam = cmdSP.Parameters.Add(SPar1, SqlDbType.VarChar)
MyParam.Direction = ParameterDirection.Input
MyParam.Value = Me.txtSingleReportCrt1.Text
End Select
'Add the single parameter 2 parameter if required
Select Case Spar2
Case Is = Nothing
' there's no parameter to add
Case Is = ""
' there's no parameter to add
Case Else
'add the parameter name, it's direction and its value
MyParam = cmdSP.Parameters.Add(Spar2, SqlDbType.VarChar)
MyParam.Direction = ParameterDirection.Input
MyParam.Value = Me.txtSingleReportCrt2.Text
End Select
MsgBox("stop 3 - params ready")
'Prepare the data adapter with the selected command
MyDataAdapter.SelectCommand = cmdSP
' Set the accept changes during fill to false for the NYPDA export
MyDataAdapter.AcceptChangesDuringFill = False
'Fill the Dataset tables (Table 0 = Exam Eligibilities, Table 1 = Candidates Demographics)
MyDataAdapter.Fill(ExportDataSet)
'Close the connection
ConnectionString.Close()
'refresh the destination path in case they changed it
SPDestination = txtPDFDestination.Text
MsgBox("stop 4 - procedure ran, datatable filled")
Select Case ExcelFile
Case True
FilePath = SPDestination & lblReportName.Text & ".xls"
Dim _excel As New Microsoft.Office.Interop.Excel.Application
Dim wBook As Microsoft.Office.Interop.Excel.Workbook
Dim wSheet As Microsoft.Office.Interop.Excel.Worksheet
wBook = _excel.Workbooks.Add()
wSheet = wBook.ActiveSheet()
Dim dt As System.Data.DataTable = ExportDataSet
Dim dc As System.Data.DataColumn
Dim dr As System.Data.DataRow
Dim colIndex As Integer = 0
Dim rowIndex As Integer = 0
MsgBox("stop 5 - excel stuff declared")
For Each dc In dt.Columns
colIndex = colIndex + 1
_excel.Cells(1, colIndex) = dc.ColumnName
Next
MsgBox("stop 6 - Header written")
For Each dr In dt.Rows
rowIndex = rowIndex + 1
colIndex = 0
For Each dc In dt.Columns
colIndex = colIndex + 1
_excel.Cells(rowIndex + 1, colIndex) = dr(dc.ColumnName)
Next
Next
MsgBox("stop 7 - rows written")
wSheet.Columns.AutoFit()
MsgBox("stop 8 - autofit complete")
Dim strFileName = SPDestination & lblReportName.Text & ".xls"
If System.IO.File.Exists(strFileName) Then
System.IO.File.Delete(strFileName)
End If
MsgBox("stop 9 - file checked")
wBook.SaveAs(strFileName)
wBook.Close()
_excel.Quit()
End Select
MsgBox("File " & lblReportName.Text & " Exported Successfully!")
'Dispose of unneeded objects
MyDataAdapter.Dispose()
ExportDataSet.Dispose()
StDt = Nothing
EdDt = Nothing
SPar1 = Nothing
Spar2 = Nothing
MyParam = Nothing
cmdSP.Dispose()
cmdSP = Nothing
MyDataAdapter = Nothing
ExportDataSet = Nothing
Catch ex As Exception
' Something went terribly wrong. Warn user.
MessageBox.Show("Error: " & ex.Message, "Stored Procedure Running Process ", _
MessageBoxButtons.OK, MessageBoxIcon.Error)
Finally
'close the connection in case is still open
If Not ConnectionString.State = ConnectionState.Closed Then
ConnectionString.Close()
ConnectionString = Nothing
End If
' reset the fields
ResetFields()
End Try
End Sub
Even though the question was asked several years ago, I thought I would add my solution since the question was posed in VB and the "best answer" is in C#. This solution writes 22,000+ rows (1.9MB) in 4 seconds on an i7 System w/ 16GB RAM.
Imports Excel = Microsoft.Office.Interop.Excel
Public Class Main
Private Sub btnExportToExcel(sender As Object, e As EventArgs) Handles btnExpToExcel.Click
'Needed for the Excel Workbook/WorkSheet(s)
Dim app As New Excel.Application
Dim wb As Excel.Workbook = app.Workbooks.Add()
Dim ws As Excel.Worksheet
Dim strFN as String = "MyFileName.xlsx" 'must have ".xlsx" extension
'Standard code for filling a DataTable from SQL Server
Dim strSQL As String = "My SQL Statement for the DataTable"
Dim conn As New SqlConnection With {.ConnectionString = "My Connection"}
Dim MyTable As New DataTable
Dim cmd As New SqlCommand(strSQL, conn)
Dim da As New SqlDataAdapter(cmd)
da.Fill(MyTable)
'Add a sheet to the workbook and fill it with data from MyTable
'You could create multiple tables and add additional sheets in a loop
ws = wb.Sheets.Add(After:=wb.Sheets(wb.Sheets.Count))
DataTableToExcel(MyTable, ws, strSym)
wb.SaveAs(strFN) 'save and close the WorkBook
wb.Close()
MsgBox("Export complete.")
End Sub
Private Sub DataTableToExcel(dt As DataTable, ws As Excel.Worksheet, TabName As String)
Dim arr(dt.Rows.Count, dt.Columns.Count) As Object
Dim r As Int32, c As Int32
'copy the datatable to an array
For r = 0 To dt.Rows.Count - 1
For c = 0 To dt.Columns.Count - 1
arr(r, c) = dt.Rows(r).Item(c)
Next
Next
ws.Name = TabName 'name the worksheet
'add the column headers starting in A1
c = 0
For Each column As DataColumn In dt.Columns
ws.Cells(1, c + 1) = column.ColumnName
c += 1
Next
'add the data starting in cell A2
ws.Range(ws.Cells(2, 1), ws.Cells(dt.Rows.Count, dt.Columns.Count)).Value = arr
End Sub
End Class
Hope it helps.
As when using VBA to automate Excel, you can assign an array directly to the value of a Range object: this is done as a single operation, so you remove the overhead associated with making multiple calls across the process boundaries between your .Net code and the Excel instance.
Eg, see the accepted answer here: Write Array to Excel Range
The answer from CPRouse worked for me except that it left off the last row of data. In the Private Sub DataTableToExcel function, I added 1 to the rows.count on this line and it wrote all the records. ws.Range(ws.Cells(2, 1), ws.Cells(dt.Rows.Count + 1, dt.Columns.Count)).Value = arr
Here is a piece of my own code that performs a very fast export of data from a DataTable to an Excel sheet (use the "Stopwatch" object to compare the speed and let me a comment):
Dim _excel As New Excel.Application
Dim wBook As Excel.Workbook
Dim wSheet As Excel.Worksheet
wBook = _excel.Workbooks.Add()
wSheet = wBook.ActiveSheet()
Dim dc As System.Data.DataColumn
Dim colIndex As Integer = 0
Dim rowIndex As Integer = 0
'Nombre de mesures
Dim Nbligne As Integer = DtMesures.Rows.Count
'Ecriture des entêtes de colonne et des mesures
'(Write column headers and data)
For Each dc In DtMesures.Columns
colIndex = colIndex + 1
'Entête de colonnes (column headers)
wSheet.Cells(1, colIndex) = dc.ColumnName
'Données(data)
'You can use CDbl instead of Cobj If your data is of type Double
wSheet.Cells(2, colIndex).Resize(Nbligne, ).Value = _excel.Application.transpose(DtMesures.Rows.OfType(Of DataRow)().[Select](Function(k) CObj(k(dc.ColumnName))).ToArray())
Next
We had a VB.NET app that did exactly this, and took even longer for our users who were on slow PC's... sometimes 15 minutes.
The app is now an ASP/VB.NET app which simply builds an HTML table and outputs the result as an .xls extension... excel is able to read the HTML table and parse it into a grid format. You can still pass in XML for formatting and options, horizontal pane locking, etc.
If you don't have the option of using ASP.NET... try looking into a way to build an HTML table string and have excel parse & populate for you... much faster! I'm sure excel can parse other types as well.... XML, Arrays, HTML, etc... all would be quicker than manually building each row through VB.NET objects.

Why is this causing a type mismatch?

I have a VB6 application that calls a Crystal Report XI Report. However when I try to change the connection info I get a type mismatch. Any help would be appreciated.
Dim Report As craxddrt.Report ' This is how Report is defined
ChangeReportTblLocation Report ' This is the function where the mismatch occurs
This is the definition of ChangeReportTblLocation:
Private Function ChangeReportTblLocation(ByRef pReport As craxddrt.Report) As Boolean
Dim ConnectionInfo As craxddrt.ConnectionProperties
Dim crxTables As craxddrt.DatabaseTables
Dim crxTable As craxddrt.DatabaseTable
Dim crxSections As craxddrt.Sections
Dim crxSection As craxddrt.section
Dim crxSubreportObj As craxddrt.SubreportObject
Dim crxReportObjects As craxddrt.ReportObjects
Dim crxSubreport As craxddrt.Report
Dim ReportObject As Object
Dim Y As Integer
Dim lsDatabase As String
On Error GoTo errHandle_CRTL
lsDatabase = GetCurrentUserRoot("SOFTWARE\COTTSYSTEMS\APP", "Database")
If lsDatabase = "" Then
lsDatabase = gConn.DefaultDatabase
End If
If lsDatabase = "" Then
lsDatabase = "frasys"
End If
With pReport
For Y = 1 To .Database.Tables.Count
Set ConnectionInfo = .Database.Tables(Y).ConnectionProperties
ConnectionInfo.DeleteAll
ConnectionInfo.Add "DSN", frasysdsn
ConnectionInfo.Add "Database", lsDatabase
'This is the Line that causes the type mismatch
.Database.Tables(Y).Location = lsDatabase & ".dbo." & Database.Tables(Y).Location
Next Y
Set crxSections = .Sections
For Each crxSection In crxSections
Set crxReportObjects = crxSection.ReportObjects
For Each ReportObject In crxReportObjects
If ReportObject.Kind = crSubreportObject Then
Set crxSubreportObj = ReportObject
Set crxSubreport = crxSubreportObj.OpenSubreport
Set crxTables = crxSubreport.Database.Tables
For Y = 1 To crxTables.Count
Set crxTable = crxTables.Item(Y)
crxTable.Location = lsDatabase & ".dbo." & crxTable.Location
Next Y
End If
Next ReportObject
Next crxSection
End With
Set ConnectionInfo = Nothing
Set crxTables = Nothing
Set crxTable = Nothing
Set crxSections = Nothing
Set crxSection = Nothing
Set crxSubreportObj = Nothing
Set crxReportObjects = Nothing
Set crxSubreport = Nothing
Set ReportObject = Nothing
ChangeReportTblLocation = True
Exit Function
errHandle_CRTL:
Screen.MousePointer = vbDefault
MsgBox err.Number, err.Description, "ChangeReportTblLocation", err.Source
End Function
I think its just a typo:
.Database.Tables(Y).Location = lsDatabase & ".dbo." & .Database.Tables(Y).Location
I've added a . before the second Database.Tables(Y).Location in this line.
This does suggest though that you aren't using Option Explicit in your code. I can't stress strongly enough how important it is to use this. It will save you lots of time looking for odd typos (like this) and save your code from doing all sorts of weird things.
try using
call ChangeReportTblLocation(Report)