vb.net Loading Images from Access Database to DataTable? - sql

So I have a MS Access Database with 1 table (Records) and 2 fields in it ("RecordID" (Number), which is the primary key, and "LowRes" (OLE Object) which is a low Resolution image). There are about 100 records.
I/m trying to load the Access Table into a DataTable (ID_Table) in VB.net.
Code so far:
Dim cnString As String = "Provider=Microsoft.ACE.OLEDB.12.0; Data Source=SBS2257_ID.accdb;"
Dim theQuery As String = "SELECT [RecordID], [LowRes] FROM [Records];"
Using CN As New OleDbConnection(cnString)
Dim command As New OleDbCommand(theQuery, CN)
Using objDataAdapter = New OleDbDataAdapter(command)
Dim ID_Table As New DataTable
CN.Open()
Dim pictureData As Byte() = DirectCast(command.ExecuteScalar(), Byte())
Dim picture As Image = Nothing
Using stream As New IO.MemoryStream(pictureData)
picture = Image.FromStream(stream)
objDataAdapter.Fill(ID_Table)
End Using
End Using
End Using
However the "DirectCast" command fails when I tell it to look at more then 1 field in my SQL statement with a datatype mismatch (if I just do [LowRes] it does not throw a error). However, I get stuck again when trying to apply the result to the table via the objDataAdapter, it doesnt fill the table with anything? I also notice that "picture" only contains the first image in the database.
I could put this database query in a function using "WHERE RECORDID=..." and loop it manually building the table returning "picture" each time, but Id like to avoid running a function 100 times, esp one that access a database.
Is it possible to read the whole database that contains images and just load it directly into a Datatable in one big swoop?
EDIT: So I got this to work:
Dim strConnection As String = "Provider=Microsoft.ACE.OLEDB.12.0; Data Source=SBS2257_ID.accdb;"
Dim strSQL As String = "SELECT [RecordID], [LowRes] FROM [Records];"
Using objConnection = New OleDbConnection(strConnection)
Using objCommand = New OleDbCommand(strSQL, objConnection)
Using objDataAdapter = New OleDbDataAdapter(objCommand)
Dim objDataTable As New DataTable("IDs")
objDataAdapter.Fill(objDataTable)
Return objDataTable
End Using
End Using
End Using
how ever when I go to view row 0, col 1 which should be the first LowRes image via a .ToString Useing this code:
Private Sub PrintValues(ByVal table As DataTable)
For Each row As DataRow In table.Rows
For Each col As DataColumn In table.Columns
MsgBox(row(col).ToString())
Next col
Next row
End Sub
It just displays "System.Byte[]". It knows its a Byte datatype, but how do I display that in a picturebox?

The ExecuteScalar() executes the query, and returns the first column of the first row in the result set returned by the query.
as your query is
Dim theQuery As String = "SELECT [RecordID], [LowRes] FROM [Records];"
the first column is RecordID which is not a Byte().
you can change your query as following:
Dim theQuery As String = "SELECT [LowRes] FROM [Records];"
or you have to use other methods to get data from the database
Dim strSql As String = "SELECT [RecordID], [LowRes] FROM [Records]"
Dim dtb As New DataTable
Using cnn As New OleDbConnection(connectionString)
cnn.Open()
Using dad As New OleDbDataAdapter(strSql, cnn)
dad.Fill(dtb)
End Using
cnn.Close()
End Using

Related

Looping through tables and querying each one which has TRelationcode in it

I'm having trouble with code that loops through the tables that contain TRelationCode. When it finds one it has to get the RelationCode from it and then convert it into the new RelationCode and update it to the new one.
To create the new RelationCode I've made a function Called MakeRelationCode(OldRelation). I have this code to loop through the tables:
Dim query As String = "use fmsStage; SELECT * FROM INFORMATION_SCHEMA.columns WHERE COLUMN_NAME = 'TRelationcode'"
Dim myCmd As SqlDataAdapter = New SqlDataAdapter(query, con)
Dim myData As New DataSet()
myCmd.Fill(myData)
For Each table As DataTable In myData.Tables
For Each row As DataRow In table.Rows
For Each col As DataColumn In table.Columns
Next
Next
Next
But now I need to update the old codes to the new ones.
I prefer simple SQL commands and a little vb logic thus I skipped the SqlDataAdapter part. This will only cost performance and is only necessary if you display something in a grid and want two-way-binding.
The following code is untested and typed blind so please check for typos etc.
I put everything in one method.
Dim tableNames As New List(Of String)
'Key: Old code, Value: New code'
Dim trelationcodes As New Dictionary(Of String, String)
Using conn As New SqlClient.SqlConnection("YourConnectionString") 'Change connection string to your needs'
Dim qTableNames = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.columns WHERE COLUMN_NAME = 'TRelationcode'"
conn.Open()
'Get table names with TRelationcode column'
Using commTableNames As New SqlClient.SqlCommand(qTableNames, conn)
Dim dataReader = commTableNames.ExecuteReader()
While dataReader.Read()
tableNames.Add(dataReader.GetString(0))
End While
End Using
'Select all distinct old TRelationcode which will be updated'
Dim qTrelationcodesOld = "SELECT DISTINCT TRelationcode FROM {0}"
For Each tableName In tableNames
'Get all old TRelationcodes from table found previuosly'
Using commTrelationcodesOld As New SqlClient.SqlCommand()
commTrelationcodesOld.Connection = conn
commTrelationcodesOld.CommandText = String.Format(qTrelationcodesOld, tableName)
Dim dataReader = commTrelationcodesOld.ExecuteReader()
While dataReader.Read()
Dim code = dataReader.GetString(0)
If Not trelationcodes.ContainsKey(code) Then
trelationcodes.Add(code, "") 'Value will be set later'
End If
End While
End Using
'Get new TRelationcodes'
For Each tRelCodeOld In trelationcodes.Keys
trelationcodes(tRelCodeOld) = MakeRelationCode(tRelCodeOld)
Next
'Set new TRelationcodes'
Dim uTRelationcode = "UPDATE {0} SET TRelationcode = #newCode WHERE TRelationcode = #oldCode"
For Each tRelCodes In trelationcodes
Using commTrelationcodesNew As New SqlClient.SqlCommand()
commTrelationcodesNew.Connection = conn
commTrelationcodesNew.CommandText = String.Format(uTRelationcode, tableName)
commTrelationcodesNew.Parameters.Add("#oldCode", SqlDbType.VarChar).Value = tRelCodes.Key 'Varchar correct?'
commTrelationcodesNew.Parameters.Add("#newCode", SqlDbType.VarChar).Value = tRelCodes.Value 'Varchar correct?'
commTrelationcodesNew.ExecuteNonQuery()
End Using
Next
Next
End Using
The code is far away from being optimal, e.g. I skipped exception handling.
The most concerning part is your MakeRelationCode function. If the logic inside could be written in T-SQL in a Stored Procedure, the overall coding would also be simplified.

How to specify field data type when reading from a SQL database

I have a SQL table called "Customers". In it is a CustomerNumber field with values like "0001234567", i.e. they are made up of numbers only, but some include leading 0s. So, when I try to run something like
sqlFetch(channel=myconn, sqtable="Customers", stringsAsFactors=FALSE)
it returns my customers table where the CustomerNumber field is numeric (instead of character), and consequently I lose all those leading 0s.
Is there a way I can specify the fieldtype for a column or an alternative solution that won't truncate all my leading 0s?
You can control the types of columns more fully using the as.is argument, which is documented in the Details section of ?sqlFetch as well as at the linked documentation for ?sqlQuery and ?sqlGetResults.
Basically, it is either a vector of logicals or a vector of numeric or character indices specifying which columns to leave untouched. This vector will be recycled as necessary.
(Note that RODBC will clobber columns stored in the database with type.convert even if the C API correctly returns char or varchar as the data type of the column in the database. The maintainer has not responded to any of my 4-5 emails on this issue over the past year, and I have since simply used a forked version of RODBC with the needed one line modification ever since.)
You could try specifying a query string and you could even cast the field if you're still having problems.
Sub Main()
Dim myConn As String = System.Configuration.ConfigurationSettings.AppSettings.Get("ConnStr")
Dim sqlConnection As SqlConnection = New SqlConnection(myConn)
Dim strSQL As String = "select cast(CustomerNumber as varchar(30)) as CustomerNumber_varchar, * from Customers"
Dim myds As DataSet
sqlConnection.Open()
Dim cmd As SqlCommand = New SqlCommand(strSQL, sqlConnection)
cmd.CommandTimeout = 60
Dim myReader As SqlDataReader = cmd.ExecuteReader()
myds = ConvertDataReaderToDataSet(myReader)
myReader.Close()
End Sub
Public Function ConvertDataReaderToDataSet(ByVal reader As SqlDataReader) As DataSet
Dim dataSet As DataSet = New DataSet()
Dim schemaTable As DataTable = reader.GetSchemaTable()
Dim dataTable As DataTable = New DataTable()
Dim intCounter As Integer
For intCounter = 0 To schemaTable.Rows.Count - 1
Dim dataRow As DataRow = schemaTable.Rows(intCounter)
Dim columnName As String = CType(dataRow("ColumnName"), String)
Dim column As DataColumn = New DataColumn(columnName, _
CType(dataRow("DataType"), Type))
dataTable.Columns.Add(column)
Next
dataSet.Tables.Add(dataTable)
While reader.Read()
Dim dataRow As DataRow = dataTable.NewRow()
For intCounter = 0 To reader.FieldCount - 1
dataRow(intCounter) = reader.GetValue(intCounter)
Next
dataTable.Rows.Add(dataRow)
End While
Return dataSet
End Function

Joining many-to-many DataTables in VB.Net

I have 2 data sources from different source types, like:
Dim connSql As String = "Data Source=sql_src;Initial Catalog=my_db;Persist Security Info=True;User ID=usr;Password=pwd;"
Dim connOle As String = "Provider=IBMDA400;Data source=src;User Id=usr;Password=pwd"
dim qrySql as String = "Select uniqueID, name From tblSql"
dim qryOle as String = "Select name, ID From tblOle"
I place them into DataTables using functions like this (mostly for reference):
Public Shared Function sqlQryDT(qry As String, conn As String) As DataTable
Dim dt As New DataTable
Dim sqlconn As New SqlClient.SqlConnection(conn)
sqlconn.Open()
Dim adapter As New SqlClient.SqlDataAdapter(qry, sqlconn)
Try
adapter.Fill(dt)
Catch ex As Exception
Finally
sqlconn.Close()
End Try
Return dt
End Function
Public Shared Function oleQryDT(qry As String, conn As String) As DataTable
Dim dt As New DataTable
Dim cn As New OleDb.OleDbConnection(conn)
Dim cmd As New OleDb.OleDbCommand("", cn)
Dim da As New OleDb.OleDbDataAdapter(cmd)
cmd.CommandText = qry
Try
cn.Open()
da.Fill(dt)
Catch ex As Exception
Finally
cn.Close()
End Try
Return dt
End Function
Dim dtSql as DataTable = sqlQryDT(qrySql, connSQL)
Dim dtOle as DataTable = oleQryDT(qryOle , connOle)
My goal is to have a DataTable consisting of uniqueID,name,ID
(Edit) If I could do an SQL query it would look like SELECT * FROM dtSql JOIN dtOle ON dtSql.name = dtOle.name
I have tried using a DataRelation by placing the tables into a DataSet and adding a relationship on name, but since names may not be unique, this fails.
I have also tried using LINQ, but that always returns IEnumerable(System.Collections.Generic) and I can't find an easy way to place the contents into a DataTable, except by iterating through every item. And, the code is messy when there are a large number of columns.
I feel like there should be a way to do this without using a For Each loop
I looked at these question, but they didn't fully help me.
How to inner join two already filled DataTables in VB.NET
Merging 2 data tables in vb.net

VB Count query result in a textbox

I want to populate the result of an SQL count query on a Access database in a textbox showing how many results there are. For example I have a code number inputted into the database 200 time, i want the textbox to show 200.
Heres my code so far:
ID = DataGridView1.CurrentRow.Cells(0).Value
fn = DataGridView1.CurrentRow.Cells(1).Value
ln = DataGridView1.CurrentRow.Cells(2).Value
SKU = DataGridView1.CurrentRow.Cells(4).Value
FC = DataGridView1.CurrentRow.Cells(5).Value
Dim countNoTwo As String = "SELECT COUNT skuNo FROM table WHERE skuNo = " & SKU & ""
Dim connection As New OleDbConnection(duraGadgetDB)
Dim dataadapter As New OleDbDataAdapter(countNoTwo, connection)
Dim ds As New DataSet()
connection.Open()
dataadapter.Fill(ds, "dura")
connection.Close()
txtbox1.Text
How do i bind the result of the dataset to the txtbox1.Text?
First, do not use string concatenation to build sql commands
(reason=Sql Injection + Parsing problems)
Second, if your command returns only one single result you could use
ExecuteScalar
Third, use the Using statement to be sure that your connection and
commands are correctly disposed after use
Dim countNoTwo As String = "SELECT COUNT skuNo FROM table WHERE skuNo = ?"
Using connection As New OleDbConnection(duraGadgetDB)
Using command As New OleDbCommand(countNoTwo, connection)
command.Parameters.AddWithValue("#sku", SKU)
connection.Open()
Dim result = Convert.ToInt32(command.ExecuteScalar())
txtbox1.Text = result.ToString
End Using
End Using
Try this
Dim dt As DataTable = ds.Tables(0)
txtbox1.Text = dt.Rows(0).Item(0).ToString()

Saving record with dataset

I want to save a record in the database using a dataset, but my data is not committing into my database.
My code can be viewed below:
Dim mydataset1 As New MyDataSet
Dim row As DataRow = mydataset1.Tables("testtable").NewRow()
With row
.Item("name") = "Segun Omotayo"
.Item("address") = "Abuja"
End With
mydataset1.Tables("testtable").Rows.Add(row)
Any help will be appreciated
A DataSet/DataTable is a offline/in-memory representation of your database. If you want to update the database, you need to use a DataAdapter.
For example (assuming you're using MS-Sql-Server):
Public Function UpdateDataSet(dataSet As DataSet) As Int32
Using con = New SqlConnection(My.Settings.SqlConnection)
Dim sql = "INSERT INTO TUser(Name,Address)VALUES(#Name,#Address)"
Using cmd = New SqlCommand(sql, con)
cmd.Parameters.Add(New SqlParameter("#Name", SqlDbType.VarChar))
cmd.Parameters.Add(New SqlParameter("#Address", SqlDbType.VarChar))
Using da = New SqlDataAdapter()
da.InsertCommand = cmd
con.Open()
Dim rowCount = da.Update(dataSet)
Return rowCount
End Using
End Using
End Using
End Function
I could be rusty here since its a long time since I wrote any VB.NET or used data adapters/datasets/datatables but I think if you decide to take that route you would need code like this:
Dim connection As New SqlConnection("#####YourConnectionString#####")
connection.Open()
Dim adapter As New SqlDataAdapter("SELECT * FROM testtable", connection)
' For the line below to work, you must have a primary key field in "testtable"
Dim builder As New SqlCommandBuilder(adapter)
Dim testtable As New DataTable("testtable")
adapter.Fill(testtable)
Dim row As DataRow = testtable.NewRow()
With row
.Item("name") = "Segun Omotayo"
.Item("address") = "Abuja"
End With
testtable.Rows.Add(row)
adapter.Update(testtable)
connection.Close()