I am reading a table from an access database in vb.net. I would like to know how many records are from a certain year. If the year does not exist in the array I like to add it and set the count to 1. When the year already exists in the array I want to increase the count to 2.
For example DOCUMENT1.2019 creates the year 2019 in the array with count 1, then DOCUMENT2.2019 sets the count to 2 for 2019, then DOCUMENT1.2018 creates the year 2018 and sets the count to 1.
and so on.
So I dont know how large the array will be when I start.
Dim sSQL As String
Dim sGetString As String
Dim sPartString As String
sSQL = "SELECT [Document Name] FROM Archief ORDER BY Id DESC"
Dim cmd As New OleDb.OleDbCommand(sSQL, con)
Dim read As OleDb.OleDbDataReader = cmd.ExecuteReader()
If read.HasRows Then
While read.Read()
sGetString = read.Item("Document Name").ToString()
sPartString = Mid(sGetString, Len(sGetString) - 11, 4) 'retrieve the year like 2019
End While
End If
The endstate will be an overview of the years and the amount of documents from that year.
h
A connection needs to remain open while a reader is active. You don't want to do a lot of processing while the connection is open.
Commands and DataReaders need to be disposed. Using `Using...End Using blocks takes care of this even if there is an error. Declare and dispose connections in the method where they are used.
Let the database do the work. Access offers some string manipulation functions that you can use in queries. You can also use Count with Group By to get the results you desire.
Private Sub GetYearCountData()
Dim sSQL = "SELECT Right([Document Name],4) As [Year], Count([Year]) FROM Archief Group By Right([Document Name],4) Order By Right([Document Name],4) ;"
Dim dt As New DataTable
Using cmd As New OleDbCommand(sSQL, New OleDbConnection(ConStr))
cmd.Connection.Open()
Using reader = cmd.ExecuteReader
dt.Load(reader)
End Using
End Using
DataGridView1.DataSource = dt
End Sub
Related
Here's my .mdf database file that has 5 columns
I want to add each of those values from my Id column in a list
Private Sub Read_Click(sender As Object, e As EventArgs) Handles Read.Click
Try
If con.State = ConnectionState.Open Then
con.Close()
End If
con.Open()
cmd = con.CreateCommand()
cmd.CommandType = CommandType.Text
cmd.CommandText = "SELECT Id FROM tablekongbago"
cmd.ExecuteNonQuery()
Dim dr As SqlClient.SqlDataReader
dr = cmd.ExecuteReader(CommandBehavior.CloseConnection)
While dr.Read
element = dr.GetInt32(0).ToString()
End While
Catch ex As Exception
End Try
MessageBox.Show(element)
End Sub
The problem is that I can only retrieve the last row of my Id column and not all of the values from my Id column using
element = dr.GetInt32(0).ToString()
If I try to iterate and turn it into
dr.GetInt32(1).ToString()
it displays nothing.
I want to create a collection of Id's to a List(Of Integer) I know how to create a list and a for loop but I don't know how can I retrieve all of my Id's from my Id column, what kind of code should I use if "dr.GetInt32(0)" is only for the last row of the Id column?, is there a way I can loop starting from the very first top row up to the last row of my Id column? I want something like "list[0] - referring to the first row and list[2] - referring to the last row, so that I can add it my List(Of Integer).
I cringe whenever I see If con.State = ConnectionState.Open Then. Connections should be declared in the method where they are used. You should never have to question the ConnectionState.
You have executed your command twice. A Select in not a NonQuery. NoQuery is Insert, Update and Delete.
Your While loop keeps overwriting the element varaiable on each iteration so you only get the value in the last record.
Never write an empty Catch block. It will just swallow errors and you may get unexpected results with no clue why.
It is a good idea to separate you database code from you user interface code.
Create your connection and command with a Using...End Using block so you know they are properly disposed. Likewise with the reader. I like to do as little as possible with a reader because it requires and open connection and connections should be open for as short a time as possible.
Private ConStr As String = "Your connection string"
Private Sub Read_Click(sender As Object, e As EventArgs) Handles Read.Click
Dim dt As DataTable
Try
dt = GetIds()
Catch ex As Exception
MessageBox.Show(ex.Message)
Return
End Try
Dim ListOfIDs = (From row As DataRow In dt.AsEnumerable
Select CInt(row(0))).ToList
ListBox1.DataSource = ListOfIDs
End Sub
Private Function GetIds() As DataTable
Dim dt As New DataTable
Using con As New SqlConnection(ConStr),
cmd As New SqlCommand("SELECT Id FROM tablekongbago;", con)
con.Open()
Using reader = cmd.ExecuteReader
dt.Load(reader)
End Using
End Using
Return dt
End Function
You can simply create a List of Integer and add the ids to your collection during each call to dr.Read()
Dim ids = New List(Of Integer)()
While dr.Read()
ids.Add(dr.GetInt32(0))
End While
You code looks a bit messed up. This should work:
Note that a sql command object is VERY nice.
It has a reader built in - you don't need to define one
It has the command text - you don't need to define one
it has a connection object - again no need to create one (but you look to have one)
And using a dataTable is nice, since you can use for/each, or use the MyTable.Rows(row num) to get a row.
And a datatable is nice, since you don't need a loop to READ the data - use the built in datareader in sqlcommand object.
Using cmdSQL As New SqlCommand("Select Id FROM tblekingbago", con)
cmdSQL.Connection.Open()
Dim MyTable As New DataTable
MyTable.Load(cmdSQL.ExecuteReader)
' table is now loaded with all "ID"
' you can see/use/display/play/have fun with ID like this:
For Each OneRow As DataRow In MyTable.Rows
Debug.Print(OneRow("Id"))
Next
' display the 5th row (it is zero based)
Debug.Print(MyTable.Rows(4).Item("Id"))
End Using
Please I am typing from my phone as I am not with the laptop, don't decrease my reputation.
I have a column in my SQL server database named Total_Amount, I want to get the Sum(Total_Amount) for a specific day for a specific Cashier. I am able to get for specific cashier only but if I want to get using the WHERE clause for both cashier AND date, it returns nothing. The command works well in SQL server Management Studio but from the VB. nET, it does not.
The below is my code.
Dim conn As New SqlConnection("data.
source=PRECIOUSMUM\MSSQLSERVER_1; initial.
catalog=inventory; user id=mantics;
password=emudeji;")
Try
'Dim Total_Amountss As Double
conn.Open()
Dim cmd = New SqlCommand
With cmd
.Connection = conn
.CommandText = "SELECT SUM(Total_Amount)
AS Total_Amount FROM tblOrder WHERE.
(cashier=#cashier) AND (Order_date=#Order_date)"
.Parameters.AddWithValue("#cashier",
lbl_Cashier_Name.Text)
.Parameters.AddWithValue("#Order_date", Date.Today.ToString)
'.Parameters.AddWithValue("#enddate", dtpicker.Value.Date)
End With
Dim dr As SqlDataReader
dr = cmd.ExecuteReader
dr.Read()
If IsDBNull(dr("Total_Amount")) Then
lbl_cashier_Totalsales.Text = "N0.00"
Else
Dim str As Double
str = dr.Item("Total_Amount")
lbl_cashier_Totalsales.Text = FormatCurrency(dr.Item("Total_Amount"))
End If
The first thing that it is clearly wrong is the fact that you use a string to query a Date column. This is never correct because the string is something that you use to display the date to your end users. It is not how the database (or .NET) stores the date value. So, a Date column is not queried using a string but passing directly the C# DateTime value in the Add method and specifying the DataType of the parameter.
A second possible error is caused by the fact that if you have also stored the Time part then passing a Date like Today will never match any row but only the ones that have 00:00:00 as their time value.
You need to query for >= midnight of the starting date and < of the following day.
This considerations will give a query like this:
With cmd
.Connection = conn
.CommandText = "SELECT SUM(Total_Amount) AS Total_Amount
FROM tblOrder
WHERE (cashier=#cashier) AND
(Order_date >= #StartDate AND
Order_date < #EndDate)"
.Parameters.Add("#cashier", SqlDbType.NVarChar).Value = lbl_Cashier_Name.Text
.Parameters.Add("#StartDate", SqlDbType.DateTime).Value = Date.Today
.Parameters.Add("#EndDate", SqlDbType.DateTime).Value = Date.Today.AddDays(1)
End With
Hi I'm trying to display a selected product on a listbox similar in this video:
https://www.youtube.com/watch?v=QbbZzaMZGhY
In the video, when he click an item from the listbox, its values appear(price and name) on the textbox. I reviewed the source code but he was not using a database. In my case, I need to use an access database to list all of my product and their id and price. Here's what I got so far from asking here:
Private Sub listboxitems_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles listboxitems.SelectedIndexChanged
Using lbconn As New OleDb.OleDbConnection("PROVIDER=Microsoft.ACE.Oledb.12.0; Data Source = C:\Users\USER PC\Desktop\orderDB1.accdb")
Using lbcmd As New OleDb.OleDbCommand("SELECT productid, product, price FROM productlog WHERE productid = ? AND product = ? AND price = ?", lbconn)
'Set your values here. The parameters must be added in the same order that they
'appear in the sql SELECT command
Dim prodidparam As New OleDbParameter("#productid", Me.txtproductid.Text)
Dim prodparam As New OleDbParameter("#product", Me.txtproduct.Text)
Dim priceparam As New OleDbParameter("#price", Me.txtprice.Text)
lbcmd.Parameters.Add(prodidparam)
lbcmd.Parameters.Add(prodparam)
lbcmd.Parameters.Add(priceparam)
'Open the connection
lbconn.Open()
txtproduct.Text = listboxitems.SelectedItem
Using lbreader As OleDbDataReader = lbcmd.ExecuteReader()
While lbreader.Read
txtproductid.Text = lbreader.GetInt32("productid").ToString()
txtproduct.Text = lbreader.GetString("product")
txtprice.Text = lbreader.GetString("price").ToString()
End While
End Using
End Using
End Using
End Sub
In the line:
txtproduct.Text = listboxitems.SelectedItem
I managed to show its name in the textbox, but its not coming from my database. I can't just type their price and name in the project but I need my data source to come from the database. So far nothing is showing up in the app. What am i missing? Thanks.
EDIT: The form load code where the listbox is filled with the database.
Private Sub shop_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'Create a connection to the database
provider = "PROVIDER=Microsoft.ACE.Oledb.12.0; Data Source="
datafile = "C:\Users\USER PC\Desktop\orderDB1.accdb"
connString = provider & datafile
myConnection.ConnectionString = connString
'Open the connection with error handling
Try
If Not myConnection.State = ConnectionState.Open Then
End If
myConnection.Open()
Catch OleDbExceptionErr As OleDbException
MessageBox.Show(OleDbExceptionErr.Message)
Catch InvalidOperationErr As InvalidOperationException
MessageBox.Show(InvalidOperationErr.Message)
End Try
'Command Object. Select from productlog. 'productlog name of table'
Dim objcmd As New OleDbCommand("SELECT * FROM productlog", myConnection)
'data adapter and data table.
Dim da As New OleDbDataAdapter(objcmd)
Dim dt As New DataTable("productlog")
da.Fill(dt)
'Create connection and release resources
myConnection.Close()
myConnection.Dispose()
myConnection = Nothing
objcmd.Dispose()
objcmd = Nothing
da.Dispose()
da = Nothing
'fill from access to the listbox
For Each row As DataRow In dt.Rows
listboxitems.Items.Add(row.Item("product"))
Next
'Release resources
dt.Dispose()
dt = Nothing
End Sub
EDIT: CODE UPDATED
Private Sub listboxitems_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles listboxitems.SelectedIndexChanged
Using lbconn As New OleDb.OleDbConnection("PROVIDER=Microsoft.ACE.Oledb.12.0; Data Source = C:\Users\USER PC\Desktop\orderDB1.accdb")
Using lbcmd As New OleDb.OleDbCommand("SELECT productid, product, price FROM productlog WHERE product = ?", lbconn)
'Set your values here. The parameters must be added in the same order that they
'appear in the sql SELECT command
Dim prodparam As New OleDbParameter("#product", listboxitems.SelectedItem)
Dim prodidparam As New OleDbParameter("#productid", listboxitems.SelectedItem)
Dim prodpriceparam As New OleDbParameter("#price", listboxitems.SelectedItem)
lbcmd.Parameters.Add(prodparam)
lbcmd.Parameters.Add(prodidparam)
lbcmd.Parameters.Add(prodpriceparam)
'Open the connection
lbconn.Open()
txtproduct.Text = listboxitems.SelectedItem
Using lbreader As OleDbDataReader = lbcmd.ExecuteReader()
While lbreader.Read
txtproductid.Text = listboxitems.SelectedItem.ToString()' iknow im missing alot in this line of code i just dont know what that is'
txtproduct.Text = listboxitems.SelectedItem.ToString()
txtprice.Text = listboxitems.SelectedItem.ToString()
End While
End Using
End Using
End Using
End Sub
I changed the get statements cause im having an error that says cannot convert type integer to string. im sorry if im making this really hard stack overflow is like my first line of defense and my last resort at the same time.
Your query is wrong. You want to get back the productid, product and price from the productlog table WHERE the record searched is equal to the productid, product and price that you supply as parameters.
Did you see the problem?
If you already know these values why ask the database? I suppose that your task is to find the product and price given the product stored in the current list item. If so, there is no need to use the textboxes and your query should be
SELECT productid, product, price FROM productlog WHERE product = ?
And the parameter is the data extracted by the listbox item
Dim prodidparam As New OleDbParameter("#product", listboxitems.SelectedItem)
Now your code could reach the while loop and set the textboxes with the missing informations. Of course this works because you have distinct product names in your table (meaning, there are no two records with the same product name)
EDIT
Looking at your comments below it seems that you are really confused how to use the GetPos, GetString, GetInt32 and eventually GetDecimal.
Once you have called lbreader.Read() you have a record at your disposition to transfer into your textboxes. But there is little point to take in consideration. You should call the various GetXXXX appropriate for the datatype of the underlying column. This problem is often overlooked by VB.NET programmers used to the automatic type conversion applied by the VB.NET compiler. These conversions don't exist in the lower levels of NET and it is better to avoid these conversions at all to not fall in subtle problems.
However, to call a OleDbDatareader.GetXXXX you need the ordinal position of the field in the returned record. So you need to call first OleDbDataReader.GetPos and then use the value returned by GetPos to extract the info from the GetXXXXX call.
Using lbreader As OleDbDataReader = lbcmd.ExecuteReader()
While lbreader.Read
Dim pos = lbreader.GetPos("product")
txtProduct.Text = lbreader.GetString(pos)
pos = lbreader.GetPos("productid")
txtProductID.Text = lbreader.GetInt32(pos).ToString()
pos = lbreader.GetPos("Price")
txtPrice.Text = lbreader.GetDecimal(pos).ToString()
End While
End Using
The last line uses GetDecimal assuming the column Price to be a numeric decimal in your database (as it should being it a currency value), if not, then use the appropriate GetXXXXX. Note also that the two last GetXXXX returns an Int32 and a Decimal. To assign these values to a property of type string (like Text) you should use an explicit conversion to a string (ToString())
I checked whether a value exists
Dim connectionString = [connection string ]
Using exist As New SqlConnection(connectionString)
Dim cmd As SqlCommand = New SqlCommand("SELECT * FROM Employees WHERE WorkEmail = #WorkEmail", exist)
cmd.Parameters.AddWithValue("#WorkEmail", DataObjects.Contacts.ElectronicAddress.Email)
If cmd.ExecuteScalar > 0 Then
//return row so that I can grab values from it, given column names
How would I go about doing that commented section on the last line?
You should execute your command and then check if it returned any rows.
See Retrieving Data Using a Data Reader
An excerpt slightly tweaked for your use case:
Private Sub HasRows(ByVal connection As SqlConnection)
Using connection
Using cmd As SqlCommand = New SqlCommand("SELECT * FROM Employees WHERE WorkEmail = #WorkEmail", connection)
cmd.Parameters.AddWithValue("#WorkEmail", DataObjects.Contacts.ElectronicAddress.Email)
connection.Open()
Using reader As SqlDataReader = cmd.ExecuteReader()
While reader.Read()
REM Your code to pull the data you want from the returned data goes here
End While
End Using
End Using
End Using
End Sub
In the future try looking through the Microsoft Documents. You will usually find what you need there.
Every week, my analysts have a spreadsheet of invoices which they need to update with a check number and check date. The checks table exists in SQL server.
I've written them a macro that iterates through each row of the spreadsheet, opens an ADO recordset using a statement like this:
SELECT CheckNumber, CheckDate FROM CHECKS WHERE Invoice_Number = " & cells (i,2)
... and then uses the fields from the recordset to write the number and date to the first two columns of that row in the Excel spreadsheet.
The code performs acceptably for a few hundred rows, but is slow when there are thousands of rows.
Is there a faster way to update an Excel spreadsheet than with a row-by-row lookup using ADO? For example, is there a way to do a SQL join between the spreadsheet and the table in SQL Server?
Edit: In response to Jeeped's questions, here's a bit of clarification.
What I'm really trying to do is find a way to "batch" update an Excel spreadsheet with information from SQL server, instead executing SQL lookups and writing the results a row at a time. Is there a way to do the equivalent of a join and return the entire results set in a single recordset?
The Invoice example above really represents a class of problems that I encounter daily. The end users have a spreadsheet that contains their working data (e.g. invoices) and they want me to add information from a SQL server table to it. For example, "Using the invoice number in column C, add the check number for that invoice in column A, and the check date in column B". Another example might be "For each invoice in column b, add the purchase order number to column a."
The Excel source column would be either a number or text. The "match" column in the SQL table would be of a corresponding data type, either varchar or integer. The data is properly normalized, indexed, etc. The updates would normally affect a few hundred or thousand rows, although sometimes there will be as many as twenty to thirty thousand.
If I can find a way to batch rows, I'll probably turn this into an Excel add-in to simplify the process. For that reason, I'd like to stay in VBA because my power users can extend or modify it to meet their needs--I'd rather not write it in a .NET language because then we need to dedicate developer time to modifying and deploying it. The security of the Excel application is not a concern here because the users already have access to the data through ODBC linked tables in an MS Access database and we have taken appropriate security precautions on the SQL Server.
Moving the process to SSIS would require a repeatability that doesn't exist in the actual business process.
In the past I've had success with pulling in all of the data from SQL server into a client side disconnected ADO recordset. I then looped once through the entire recordset to create a VBA dictionary storing the ID Value (in this case the InvoiceNum) as key, and the recordset bookmark as the pair item. Then loop though each value checking the invoice number against the dictionary using the "Exists" function. If you find a match you can set your recordset to the bookmark and then update the values on the spreadsheet from the recordset. Assuming the Invoice table isn't a few million rows this method should prove speedy.
EDIT: Added batch processing to try to limit returned records from large datasets. (Untested Code Sample)
Public Sub UpdateInvoiceData(invoiceNumRng As Range)
'References: Microsoft ActiveX Data Objects x.x
'References: Microsoft Scripting Runtime
Dim cell As Range
Dim tempCells As Collection
Dim sqlRS As ADODB.Recordset
Dim dict As Scripting.Dictionary
Dim iCell As Range
Dim testInvoiceNum As String
Dim inClause As String
Dim i As Long
i = 1
For Each cell In invoiceNumRng
If i Mod 25 = 0 Or i = invoiceNumRng.cells.Count Then 'break up loop into batches of 25:: Modify batch size here, try to find an optimal size.
inClause = CreateInClause(tempCells) 'limit sql query with our test values
Set sqlRS = GetInvoiceRS(inClause) 'retrieve batch results
Set dict = CreateInvoiceDict(sqlRS) 'create our lookup dictionary
For Each iCell In tempCells
testInvoiceNum = iCell.Value 'get the invoice number to test
If dict.Exists(testInvoiceNum) Then 'test for match
sqlRS.Bookmark = dict.Item(testInvoiceNum) 'move our recordset pointer to the correct item
iCell.Offset(0, 1).Value = sqlRS.Fields("CheckNum").Value
iCell.Offset(0, 2).Value = sqlRS.Fields("CheckDate").Value
End If
Next iCell
'prepare for next batch of cells
Set tempCells = Nothing
Set tempCells = New Collection
Else
tempCells.Add cell
End If
i = i + 1 'our counter to determine batch size
Next cell
End Sub
Private Function CreateInClause(cells As Collection) As String
Dim retStr As String
Dim tempCell As Range
retStr = ""
For Each tempCell In cells
retStr = retStr & "'" & tempCell.Value & "'" & ", " 'assumes textual value, omit single quotes if numeric/int
Next tempCell
If Len(retStr) > 0 Then
CreateInClause = Left(retStr, Len(retStr) - 2) 'trim off last comma value
Else
CreateInClause = "" 'no items
End If
End Function
Private Function GetInvoiceRS(inClause As String) As ADODB.Recordset
'returns the listing of InvoiceData from SQL
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim sql As String
Set cn = New ADODB.Connection
cn.ConnectionString = "Your Connection String"
sql = "SELECT * FROM [Invoices] WHERE InvoiceID IN(" & incluase & ")"
cn.Open
rs.CursorLocation = adUseClient 'use clientside cursor since we will want to loop in memory
rs.CursorType = adOpenDynamic
rs.Open sql, cn
Set rs.ActiveConnection = Nothing 'disconnect from connection here
cn.Close
Set GetInvoiceRS = rs
End Function
Private Function CreateInvoiceDict(dataRS As ADODB.Recordset) As Dictionary
Dim dict As Scripting.Dictionary
Set dict = New Scripting.Dictionary
If dataRS.BOF And dataRS.EOF Then
'no data to process
Else
dataRS.MoveFirst 'make sure we are on first item in recordset
End If
Do While Not dataRS.EOF
dict.Add CStr(dataRS.Fields("InvoiceNum").Value), dataRS.Bookmark
dataRS.MoveNext
Loop
Set CreateInvoiceDict = dict
End Function
The best way to do this is to use SSIS and insert the information (through SSIS) into a range in the spreadsheet. Remember that SSIS expects the target range to be empty and one row above the target range should also be empty. If you do this you can schedule the SSIS job through the windows scheduler.