"No data exists for the row/column." executing OLEDB Oracle OleDbDataReader - vb.net

I know this is very basic, and I've done this hundreds of times. But, for some strange reason I am executing this command on a database, and it fails when it tries to read a column from the result. If I execute this statement in SQL Plus logged in as the same credentials, the row (table has 1 row) is selected just fine. Any ideas what I am doing wrong? I tried accessing the columns by name, by index, and indeed any column - all of them give no data. I tried without the .NextResult() (just in case), same exception.
'...
' Determine if this database is Multisite enabled
Dim multisiteCmd As OleDbCommand = DbConnection.CreateCommand
multisiteCmd.CommandText = "SELECT * FROM TDM_DB_VERSION;"
Dim dbVersionReader As OleDbDataReader = multisiteCmd.ExecuteReader()
If dbVersionReader.HasRows Then
dbVersionReader.NextResult()
'If a ReplicaID was generated for the Database ID, then this is part of a
'multisite implementation
'Dim dbRepID As String = dbVersionReader("DB_REPLICID")
Dim dbRepID As String = dbVersionReader(9)
PluginSettings.UseMultisite = False
If Not dbRepID Is Nothing Then
If dbRepID.Length > 0 Then
PluginSettings.UseMultisite = True
PluginSettings.MultisiteReplicaId = dbRepID
End If
End If
End If
dbVersionReader.Close()
As you can see from these Immediate commands, the connection is open:
? DbConnection.Provider
"OraOLEDB.Oracle"
? DbConnection.State
Open {1}

NextResult() is for statements that have more than one result set. For example, if you had sent a command like this:
"SELECT * FROM TDM_DB_VERSION;SELECT * FROM dual;"
Note there are two queries in there. You can handle them both with a single call to the database and a single OleDbDataReader, and NextResult() is part of how you do that.
What you want instead is this:
Dim multisiteCmd As OleDbCommand = DbConnection.CreateCommand
multisiteCmd.CommandText = "SELECT * FROM TDM_DB_VERSION;"
Dim dbVersionReader As OleDbDataReader = multisiteCmd.ExecuteReader()
If dbVersionReader.Read() Then
'If a ReplicaID was generated for the Database ID, then this is part of a
'multisite implementation
'Dim dbRepID As String = dbVersionReader("DB_REPLICID")
Dim dbRepID As String = dbVersionReader(9)
PluginSettings.UseMultisite = False
If Not dbRepID Is Nothing Then ' Do you mean check for DbNull here? "Nothing" is not the same thing
If dbRepID.Length > 0 Then
PluginSettings.UseMultisite = True
PluginSettings.MultisiteReplicaId = dbRepID
End If
End If
End If
dbVersionReader.Close()

Related

How to remove column headings on returned data when making T-SQL calls from within VBA?

I am using VBA to output information into an Excel worksheet that has been gathered from a SQL Server database called "PHB". I can connect to the database and pull information by calling a view.
When I dump the data into my Excel worksheet the column headings of the database data are included and I don't want that. I have to use an offset to get the data to look right. I can manipulate the results worksheet and remove the columns with VBA. If there is some switch I can use on either (VBA or T-SQL) end it seems like it would be a much cleaner and simpler approach.
Here are the relevant parts of my logic:
Public Sub Show_ProductCode()
Dim PHB_cnn As New ADODB.Connection
Dim ProductCode_qry As String
Dim ProductCode_rst As New ADODB.Recordset
Dim ProductCode_qtbl As QueryTable
Dim ProductCode As String
Dim OffsetAmt As String
Dim OffsetAmt_int As Integer
PHB_cnn.Provider = "sqloledb"
PHB_cnn.CursorLocation = adUseClient
PHB_cnn.Open p_PHB_Connect_s 'In Module
.
.
.
For Each c In DataRange_rng
ProductCode = c.Value
ProductCode_qry = "SELECT * FROM vw_ShowPurchaseHistory WHERE ProductCode = '" & ProductCode & "'"
ProductCode_rst.Open ProductCode_qry, PHB_cnn, adOpenStatic, adLockOptimistic
With ProductCode_rst
OffsetAmt = .RecordCount
If ProductCode_rst.EOF Then
Debug.Print "No Records"
Else
OffsetAmt_int = OffsetAmt_int + (CInt(OffsetAmt) * 2)
With Worksheets("Results")
Set ProductCodes_qtbl = .QueryTables.Add(ProductCode_rst, .Range("A" & OffsetAmt_int))
ProductCodes_qtbl.Refresh
End With
End If
End With
If ProductCode_rst.State = adStateOpen Then ProductCode_rst.Close
Set ProductCode_rst = Nothing
Set ProductCode_qtbl = Nothing
Next c
exit_Show_ProductCode:
If ProductCode_rst.State = adStateOpen Then ProductCode_rst.Close
Set ProductCode_rst = Nothing
Set ProductCode_qtbl = Nothing
Exit Sub
err_Show_ProductCode:
MsgBox Err.Description, vbOKOnly
Resume exit_Show_ProductCode
End Sub
My input data:
My output:
your code is going to be very inefficient as it is executing a SQL statement for each ProductCode. It would be better to loop through these values and build up a SQL IN statement and then, after the loop, execute it once e.g.
...
ProductCode_in = "('productcode1', 'productcode2','productcode3',...., 'productcode_n')"
ProductCode_qry = "SELECT * FROM vw_ShowPurchaseHistory WHERE ProductCode IN '" & ProductCode_in
...
You'll then end up with all your data in Excel with a single header row - which is simple to delete with a VBA statement (sheet.row(1).delete).

VB.NET - Unable to retrieve the first ID on a table using SELECT query

Good day everyone! After 3 years without using VB.NET I decided to use again for my project that not require web development.
this is my code (Reference: link)
cmdOLEDB.CommandText = "SELECT Price FROM tblPrice"
cmdOLEDB.Connection = cnnOLEDB
Dim rdrOLEDB As OleDbDataReader = cmdOLEDB.ExecuteReader
Dim priceList(18) As String
Dim i As Integer = 0
If rdrOLEDB.Read = True Then
While rdrOLEDB.Read()
priceList(i) = rdrOLEDB.GetValue(0)
i += 1
End While
txtPrice1.Text = priceList(0).ToString
cnnOLEDB.Close()
Else
MsgBox("Record not found.")
cnnOLEDB.Close()
End If
when I put this code in a MsgBox
MsgBox(rdrOLEDB.GetValue(0))
the result is "2" but I have 1 more data before that. It means the query retrieve the ID # 2 not the ID # 1. Here's the screenshot on my Access database
and when I use this code:
txtPrice1.Text = priceList(17).ToString
the result is 35.
You are skipping the first record because you call two times the Read method.
The first call reads the first record and returns true, then you enter the while loop extracting the info, but at this point you are on the second record.
If you want to check if there are rows then call HasRows
If rdrOLEDB.HasRows Then
While rdrOLEDB.Read()
priceList(i) = rdrOLEDB.GetValue(0)
i += 1
End While
txtPrice1.Text = priceList(0).ToString
cnnOLEDB.Close()
Else
MsgBox("Record not found.")
cnnOLEDB.Close()
End If
Please check with If condition , replace rdrOLEDB.Read with rdrOLEDB.hasrows
If rdrOLEDB.Hasrows= True Then
and check it again.

Referencing an entire data set instead of a single Cell

I am creating a login form for users to log into using a database. I was wondering if there is a way in which i could get the program to search the entire table instead of a certain item. Here is my code so far.
Dim UserInputtedUsername As String
Dim UserInputtedPassword As String
UserInputtedUsername = txtAdminUsername.Text
UserInputtedPassword = txtAdminPassword.Text
sqlrunnerQuery = "SELECT * FROM tblLogin"
daRunners = New OleDb.OleDbDataAdapter(sqlrunnerQuery, RunnerConnection)
daRunners.Fill(dsRunner, "Login")
If UserInputtedUsername = dsadminlogin.Tables("Login").Rows(0).Item(2) And UserInputtedPassword = dsadminlogin.Tables("Login").Rows(0).Item(3) Then
Form1.Show()
ElseIf MsgBox("You have entered incorrect details") Then
End If
End Sub
Instead if searching the (in-memory) DataSet for your user serach the database in the first place. Therefore you have to use a WHERE in the sql query(with guessed column names):
sqlrunnerQuery = "SELECT * FROM tblLogin WHERE UserName=#UserName AND PassWord=#PassWord"
Note that i've used sql-parameters to prevent sql-injection. You add them in this way:
daRunners = New OleDb.OleDbDataAdapter(sqlrunnerQuery, RunnerConnection)
daRunners.SelectCommand.Parameters.AddWithValue("#UserName", txtAdminUsername.Text)
daRunners.SelectCommand.Parameters.AddWithValue("#PassWord", txtAdminPassword.Text)
Now the table is empty if there is no such user.
If dsadminlogin.Tables("Login").Rows.Count = 0 Then
MsgBox("You have entered incorrect details")
End If
For the sake of completeteness, you can search a complete DataTable with DataTable.Select. But i prefer LINQ-To-DataSet. Here's a simple example:
Dim grishamBooks = From bookRow in tblBooks
Where bookRow.Field(Of String)("Author") = "John Grisham"
Dim weHaveGrisham = grishamBooks.Any()

Fromat datagridview cells based on SQL logic

I have the following SQL statement that checks for duplicate date ranges within my data set.
SELECT *
FROM [DaisyServices].[dbo].[DaisyServicesIndigo] i
JOIN [DaisyServices].[dbo].[DaisyServicesIndigo] i2
on i.cli = i2.cli
and i.quantity = i2.quantity
and i.unitcost = i2.unitcost
and i.totalcost = i2.totalcost
and i.[description] = i2.[description]
and ((i.FromDate <= i2.ToDate) and (i.ToDate >= i2.FromDate))
WHERE i.id<>i2.id
AND (i2.[bill]=0 AND i2.[Billed Month] is Null AND i2.currentbill = 0)
I would like to use this SQL logic, to check each line of my data grid view and then format each line accordingly if the condition is met. i.e. the data ranges overlap.
Something like this, I am just not sure how to incorporate the SQL element?
For Each row As DataGridViewRow In DaisyServicesForm.DataGridView2.Rows
If "SQL condition for the specific line is true" Then
row.DefaultCellStyle.ForeColor = Color.Blue
End If
Next
Any help greatly appreciated.
Try this piece of code
For intcount=0 to DaisyServicesForm.DataGridView2.Rows.count-1
If checkdata(intcount)=true Then
datagridview2.rows(intcount).DefaultCellStyle.ForeColor = Color.Blue
End If
Next
and add a checking function in your code like:
Private Function checkdata(ByVal row As Integer) As Boolean
Dim da As SqlDataReader
Dim command As SqlCommand
command.CommandType = CommandType.Text
command.Connection = connection
command.CommandText = "Your query for checking if the item in specified row is in database"
da = command.ExecuteReader
If da.HasRows Then
Return True
Else
Return False
End If
End Function
specify the checking in the command text in fucntion.Just Provided this as an example,you may modify it upon our own needs.

SQL QUERY Return 0 Records but 2 records exist

This code returns zero rows count but there are 2 rows in appointment table.
The msgbox I commented was to check if the date is correct and format is correct and shows date as 2014/08/09. The appointment date in database is 2014/08/09 for 2 records (the only 2 records). Record count variable shows 0.
Table name (copied directly cut and paste) is Appointments and column is AppointmentDate.
The connectDatabase sub routine connects to the database successfully as I use it whenever I connect to database so it's correct as I connect to other tables correctly before I run this code using same sub routine.
Command.text contains
SELECT * FROM Appointments WHERE AppointmentDate = 2014/08/09
Don't know what other details to specify.
Private Sub frmAppointments_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'load appointments
LoadAppointments(dtpAppointmentDate.Value.Date)
End Sub
Public Sub LoadAppointments(whichdate As Date)
Dim sqlcmd As New OleDb.OleDbCommand
'set connection
ConnectDatabase()
With frmAppointments
'MsgBox(whichdate)
M_connDB.Open()
'fetch records from database
sqlcmd.Connection = M_connDB
sqlcmd.CommandText = "SELECT * FROM Appointments WHERE AppointmentDate = " & whichdate
.dataAdapterAppointments = New OleDb.OleDbDataAdapter(sqlcmd.CommandText, M_connDB)
'first clear data table to prevent duplicates
.dataTableAppointments.Rows.Clear()
.dataAdapterAppointments.Fill(.dataTableAppointments)
M_connDB.Close()
Dim rowindex As String
Dim iy As Long
Dim recordcount As Long
'check if any records exist
recordcount = .dataTableAppointments.Rows.Count
If Not recordcount = 0 Then
For iy = 0 To .dataTableAppointments.Rows.Count
For Each row As DataGridViewRow In .dtgrdAppointments.Rows
If row.Cells(0).Value = .dataTableAppointments.Rows(iy).Item(6) Then
rowindex = row.Index.ToString()
MsgBox(.dtgrdAppointments.Rows(rowindex).Cells(0).Value, vbInformation + vbOKOnly, "MSG")
Exit For
End If
Next
Next iy
Else
MsgBox("No Appointments for selected date.", vbInformation + vbOKOnly, "No Appoinments")
End If
End With
Use sql-parameters instead of string-concatenation. This should work in MS Access:
sqlcmd.CommandText = "SELECT * FROM Appointments WHERE AppointmentDate = ?"
sqlcmd.Parameters.AddWithValue("AppointmentDate", whichdate)
This prevents you from conversion or localization issues and -even more important- sql-injection.
2014/08/09 doesn't have quotes around it, making it a math expression (2014 divided by 8 divided by 9). Of course, your table has no rows with a date matching the result of that expression. But don't put in the quotes. Instead, add a parameter.
I don't VB, so I write it in C#. My point of view is it's better to specify the datatype too:
sqlcmd.CommandText = "SELECT * FROM Appointments WHERE AppointmentDate=#whichdate";
sqlcmd.Parameters.Add("#whichdate", SqlDbType.Date).Value = whichdate;