SqlDataReader.GetValue(0) cannot get anything - vb.net

I am writing some script to read the sql query result at Intouch environment, it's not exactly C# language but similar. I just want to get the "1" stored in my "SQLTest" variable (Define as a string data type).
Here's the result of my sql query
And here is my code:
Dim objDB As System.Data.SqlClient.SqlConnection;
Dim objCmd As System.Data.SqlClient.SqlCommand;
Dim objDR As System.Data.SqlClient.SqlDataReader;
Dim objTbl As System.Data.DataTable;
Dim sDBConnStr As String;
Dim sSQL As String;
Dim bOk As Boolean;
sDBConnStr = "Server=Desktop-3J641FK;Database=Runtime;Integrated Security=True;";
'' Connect and open the database
objDB = New System.Data.SqlClient.SqlConnection(sDBConnStr);
objDB.Open();
sSQL = "SELECT sum (case when EventLogKey = '5' and DetectDateTime between '2022-07-21 11:00:20' and '2022-07-25 11:00:20' then 1 else 0 end) FROM [Runtime].[dbo].[EventHistory]";
'' Invoke the SQL command
objCmd = New System.Data.SqlClient.SqlCommand(sSQL, objDB);
'' Retrieve the queried fields from the query into the reader
objDR = objCmd.ExecuteReader();
InTouch:SQLTesting = objDR.Read();
while objDR.Read() == true
InTouch:SQLTest = objDR.GetValue(0).ToString;
endwhile;
objDR.Close();
objDB.Dispose();
objCmd.Dispose();

InTouch:SQLTesting = objDR.Read();
while objDR.Read() == true
You are calling Read twice, so what do you expect to happen?
If there will be exactly one row, just call Read.
If there will be zero or one row, call Read with an if statement.
If there may be more than one row, call Read with a while loop.
Do one and only one of the above. If there might be more than one row and you want to do something different if there are no rows, use the HasRows property first, then use the while loop.
Having said all that, if there will only be one value in the result set then you should be calling ExecuteScalar, so the data reader is irrelevant:
InTouch:SQLTest = objCmd.ExecuteScalar().ToString();

I think your While loop is unnecessary here as you Read the sqldata reader already before While and you are using Sum in your sql which will return one value always. Try this :
InTouch:SQLTesting = objDR.Read();
InTouch:SQLTest = objDR.GetValue(0).ToString;

Related

How to get the value of a column using a dataview in vb.net

I have a list of records and for Employee R1005, I need to check if that Employee has been Enabled for login alert (i.e EnableLoginAlert = Yes), then a button will be displayed.
CompanyID EmployeeNo EnableLoginAlert
10046 R1005 Yes
20041 Ajax12 No
47021 Drek Yes
I have tried the below codes:
If dCompanyDetails.Tables(0).Rows.Count > 0 Then
Dim dataView As DataView = dCompanyDetails.Tables(0).DefaultView
dataView.RowFilter = "EmployeeNo = '" & strEmployeeNumber & "'"
Dim svalue As String = dataView.Table.Rows(0).ItemArray(0).ToString()
If svalue = "No" Then
AlertButton.Visible = False
ElseIf svalue = "Yes" Then
{
//Do something else
}
End If
End If
If you are going to use a DataView then use it. This:
Dim svalue As String = dataView.Table.Rows(0).ItemArray(0).ToString()
is simply going back to the DataTable and using it, ignoring the DataView. The DataView contains DataRowView objects so get the one you need and use it. It is similar to a DataRow and you can use it the same way in this case:
Dim enableLoginAlert = CStr(dataView(0)("EnableLoginAlert")) = "Yes"
Now you have an actual Boolean that represents the state you want.
That's not how you should do it though. Generally speaking, you would use a DataView when you want to bind data. In fact, if you bind a DataTable then the data you see in the UI actually comes from the DefaultView. That's why you can filter and sort it. In this case, there are better options.
If you want to find a row by its primary key then the Rows collection of a DataTable has a Find method, e.g.
Dim row = dCompanyDetails.Tables(0).Rows.Find(strEmployeeNumber)
Dim enableLoginAlert = CStr(row("EnableLoginAlert")) = "Yes"
If you're searching by other than the primary key, the DataTable itself has a Select method. Because multiple rows may match, it returns an array, so you need to get the row out of that, e.g.
Dim row = dCompanyDetails.Tables(0).Select($"EmployeeNo = '{strEmployeeNumber}'").First()
Dim enableLoginAlert = CStr(row("EnableLoginAlert")) = "Yes"
If you want to look up a single row it's perhaps easiest to use LINQ:
Dim row = dCompanyDetails.Tables(0).Rows.Cast(Of DataRow).AsQueryable().FirstOrDefault(Function(r) r("EmployeeNo").ToString() = strEmployeeNumber)
If row IsNot Nothing AndAlso row("EnableLoginAlert").ToString() = "Yes" Then
...
..though I'd be the first to claim that using LINQ on base DataTables is very verbose, because of the Cast/AsQueryable. I'd use strongly typed DataTables (in a dataset); if you were to convert your code to using strongly typed tables it would look like:
Dim r = someDataSet.AProperTableName.FirstOrDefault(Function(r) r.EmployeeNo = strEmployeeNumber)
If r?.EnableLoginALert = "Yes" Then
...
...using strongly typed datatables is much less messy..
nb: You need to Imports System.Linq for these to work
That LINQ is the same thing as:
For Each r as DataRow in dCompanyDetails.Tables(0)
If r("EmployeeNo").ToString() = "R1005" AndAlso r("EnableLoginAlert").ToString() = "Yes" Then
...
You also have the option of using DataTable.Select (not a LINQ thing, though LINQ has a Select too)
Dim matchingRows = dCompanyDetails.Tables(0).Select($"[EmployeeNo] = '{strEmployeeNumber}'")
If matchingrows.Count > 0 AndAlso matchingRows(0)("EnableLoginAlert").ToString() = "Yes"

vba function which returns more than one value and so can be called in sql

I'm new to VBA and i need help.
I want to create vba function which takes table name as input, and distinct specific field from that table. I created function, and it works when i run it in vba immediate window (when i use debug.print command to display results). But when i call this function in sql, instead whole field values, it returns just last one. I'm not good at vba syntax so i need help to understand. Does function can return more than one value? If can, how, and if not what else to use? Here's my code:
Public Function TableInfo(tabela As String)
Dim db As Database
Dim rec As Recordset
Dim polje1 As Field, polje2 As Field
Dim sifMat As Field, pogon As Field, tipVred As Field
Set db = CurrentDb()
Set rec = db.OpenRecordset(tabela)
Set sifMat = rec.Fields("Field1")
Set pogon = rec.Fields("Field2")
Set tipVred = rec.Fields("Field3")
For Each polje1 In rec.Fields
For Each polje2 In rec.Fields
TableInfo = pogon.Value
rec.MoveNext
Next
Next
End Function
Any help is appreciated.
The problem is with this line probably:
TableInfo = pogon.Value
It runs inside the loop and returns the last value of the loop.
Instead of returning one value TableInfo, you may try to return something similar to a Collection or an Array.
Inside the loop, append values in the Collection and after the loop, return the Collection back from the function.
Edit:
I have re-written the code shared by you:
Public Function TableInfo(tabela As String) as String()
Dim db As Database
Dim rec As Recordset
Dim polje1 As Field, polje2 As Field
Dim sifMat As Field, pogon As Field, tipVred As Field
Dim returnValue() As String
Dim i as Integer
Set db = CurrentDb()
Set rec = db.OpenRecordset(tabela)
Set sifMat = rec.Fields("Field1")
Set pogon = rec.Fields("Field2")
Set tipVred = rec.Fields("Field3")
' I am not going to modify this but I think we can do away with two For Each loops.
' Just iterate over rec like
' For Each r In rec -> please use proper naming conventions and best practices
' and access each field as r("Field1") and r("Field2")
For Each polje1 In rec.Fields
For Each polje2 In rec.Fields
returnValue(i) = pogon.Value
i = i + 1
rec.MoveNext
Next
Next
TableInfo = returnValue
End Function
Please note: I have not tested this code but I assume this should work for you. Also, I have assumed that you want to return String() array. Please change the data type if you want to return some other type.
When you call the array (as posted in theghostofc's answer), you will need to do something like this:
Dim TableInfo() As String
For i = LBound(TableInfo) To UBound(TableInfo)
YourValue = TableInfo(i)
... Process some code that uses YourValue
Next i
If you're not looping through your array, you're not going to get each individual value out of it.

how to set result of aggregate sql query as lable caption

I am using this code to retrieve no of machines (count)...
Dim strCntSr As String = "SELECT count(sr_no) FROM Vendor_Machine WHERE chaln_no='" & cmbChal_no.Text & "'"
comm_getCnt = New OleDb.OleDbCommand(strCntSr, cnnOLEDB)
comm_getCnt.ExecuteNonQuery()
***Here I want to set result of the above query [count(sr_no)] as text of lblMachine***
lblMachine.Text =
Please suggest me the code.. Thank you..
ExecuteNonQuery return only the number of the rows affected not the value/s returned by the query.
In your case the correct method to use is ExecuteScalar that returns the first column of the first row obtained by the query.
Notice also that is a very bad practice to build query text using string concatenation.
The problems are Sql Injection and correct parsing of text provided by you.
Dim strCntSr As String = "SELECT count(sr_no) FROM Vendor_Machine WHERE chaln_no=?"
comm_getCnt = New OleDb.OleDbCommand(strCntSr, cnnOLEDB)
comm_getCnt.Parameters.AddWithValue("#p1", cmbChal_no.Text)
Dim result = comm_getCnt.ExecuteScalar()
lblMachine.Text = Convert.ToInt32(result);

SQL.REQUEST Excel function Alternative? Create one with VBA?

I am looking to execute SQL SELECT statement inside a single cell in Excel, using other cells as inputs to the SELECT statement. After some searching, I found that the sql.request function would have done exactly what I'm looking for. However, that function was deprecated in after 2002, and I'm running Excel 2007 and 2010 here at work. Citation
I have tried to create a Macro / VBA script that does the same thing, but haven't been able to get very far with it. I do all my programming in LabVIEW, Mathematica, and SQL; I have no idea what's going on in VBA. This is what I've managed to come up with:
Sub Test2()
' Declare the QueryTable object. I'm not actually sure why this line is here...
Dim qt As QueryTable
' Set up the SQL Statement
sqlstring = "SELECT `Substrate ID` FROM temp_table WHERE `id`=" & Range("A1").Value
' Set up the connection string, reference an ODBC connection
connstring = "ODBC;DSN=OWT_x64;"
' Now implement the connection, run the query, and add
' the results to the spreadsheet
With ActiveSheet.QueryTables.Add(Connection:=connstring, Destination:=Range("A22"), Sql:=sqlstring)
.Refresh BackgroundQuery:=False
End With
End Sub
There are three primary issues with the above code:
This code returns the column ID ("Substrate ID") in cell A22, and the result of the SQL query in cell A23. I only want the result, and I only want it in cell A22. All queries are forced to return only 1 row and 1 column.
I don't know how to make it so that the output cell, A22, is whatever cell is active when the script is run. Also, the input cell, A1, should be the cell directly to the left (column-1) of the active cell.
I don't know how to turn this into an Excel function
=sql.request(connection_string,[output_ref],[driver_prompt],[query_text],[col_names_logical])
which is my final goal. This way, I can give this code to others at my company and they can easily use it.
The connection is a ODBC connection to a MySQL 5.6 database. The query is pretty simple, and along the lines of:
SELECT column FROM table WHERE id=excel_cell_value
as you can see from the VBA code that I have.
Currently, I run a query in a different Excel worksheet that returns all rows of the "id" and "Substrate ID" columns and then run VLOOKUP to find the item of interest. This is starting to become an issue, as our database size is growing quite fast.
So, I ask:
How can I get rid of the column ID in the result?
How can I turn this into a custom excel function? I've looked at Office.com and it doesn't seem too difficult, but I need a working script first.
-OR- Has anyone already made a custom function that they're willing to share?
Thanks!
EDIT: Managed to get something working thanks to Tim's link.
Function SQLQuery(sqlString As String, connString As String, Optional TimeOut As Integer) As String
SQLQuery = Error 'Assume an error happened
Dim conn As ADODB.Connection
Dim record As ADODB.Recordset
Set conn = New ADODB.Connection
conn.ConnectionString = connString
conn.Open
Set record = New ADODB.Recordset
If TimeOut > 0 Then
conn.CommandTimeout = TimeOut
End If
record.Open sqlString, conn
Dim cols As Long
Dim i As Long
cols = record.Fields.Count 'Count how many columns were returned
If Not record.EOF Then 'Put results into comma-delimited string
record.MoveFirst
s = ""
If Not record.EOF Then
For i = 0 To cols - 1
s = s & IIf(i > 0, ",", "") & record(i)
Next i
End If
End If
SQLQuery = s
End Function
However, it's quite slow. Any ideas on how to speed it up?
Here's a quick test of caching the connection. On a test worksheet with 100 lookups it reduced calculation time from about 18 sec to about 0.5 sec
Remember though it will keep a connection open until you close Excel (or the VB environment gets reset).
If you want to test the difference in your environment, you can comment out the marked line (don't forget to also press the "stop" button in the VBE to clear the static variables).
Function SQLQuery(sqlString As String, connString As String, _
Optional TimeOut As Integer) As String
Static cs As String
Static conn As ADODB.Connection
SQLQuery = Error 'Assume an error happened
Dim s
If conn Is Nothing Or connString <> cs Then
Set conn = New ADODB.Connection
conn.ConnectionString = connString
conn.Open
If TimeOut > 0 Then conn.CommandTimeout = TimeOut
cs = connString '###comment this out to disable caching effect
End If
Dim record As New ADODB.Recordset
record.Open sqlString, conn
Dim cols As Long
Dim i As Long
cols = record.Fields.Count 'Count how many columns were returned
If Not record.EOF Then 'Put results into comma-delimited string
record.MoveFirst
s = ""
If Not record.EOF Then
For i = 0 To cols - 1
s = s & IIf(i > 0, ",", "") & record(i)
Next i
End If
End If
SQLQuery = s
End Function

How do you use ExecuteScalar to return a single value from an Oracle Database?

Been using the code below to return a single record from the database. I have read that ExecuteScalar is the right way to return a single record. I have never been able to get ExecuteScalar to work though. How would I change this to return a single value in VB.Net using ExecuteScalar?
Dim oracleConnection As New OracleConnection
oracleConnection.ConnectionString = LocalConnectionString()
Dim cmd As New OracleCommand()
Dim o racleDataAdapter As New OracleClient.OracleDataAdapter
cmd.Connection = oracleConnection
cmd.CommandText = "FALCON.CMS_DATA.GET_MAX_CMS_TH"
cmd.CommandType = CommandType.StoredProcedure
cmd.Parameters.Add(New OracleParameter("i_FACID_C", OracleType.Char)).Value = facilityShortName
cmd.Parameters.Add(New OracleParameter("RS_MAX", OracleType.Cursor)).Direction = ParameterDirection.Output
Try
Using oracleConnection
oracleConnection.Open()
Using oracleDataAdapter
oracleDataAdapter = New OracleClient.OracleDataAdapter(cmd)
Dim workingDataSet As DataSet
oracleDataAdapter.TableMappings.Add("OutputSH", "RS_MAX")
workingDataSet = New DataSet
oracleDataAdapter.Fill(workingDataSet)
For Each row As DataRow In workingDataSet.Tables(0).Rows
Return CDate(row("MAXDATE"))
Next
End Using
End Using
From Microsoft
"The ExecuteOracleScalar() method of the OracleCommand class is used to execute a SQL statement or stored procedure that returns a single value as an OracleType data type. If the command returns a result set, the method returns the value of the first column of the first row. The method returns a null reference if a REF CURSOR is returned rather than the value of the first column of the first row to which the REF CURSOR points. The ExecuteScalar() method of the OracleCommand class is similar to the ExecuteOracleScalar() method, except it returns a value as a .NET Framework data type.
Having said that, neither of these methods is useful when working with Oracle stored procedures. Oracle stored procedures cannot return a value as part of the RETURN statement, only as OUT parameters—see the Stored Procedures That Do Not Return Data section. Also, you cannot return a result set except through a REF CURSOR output parameter—this is discussed in the next section.
You can retrieve the return value for an Oracle function only by using a RETURN parameter (shown in the previous section) and not by using the one of the ExecuteScalar methods."
http://msdn.microsoft.com/en-us/library/ms971506.aspx
ExecuteScalar returns a single value (scalar) not a record.
not sure why the other answer is marked as accepted, as it doesn't appear to answer your question
How would I change this to return a single value in VB.Net using ExecuteScaler
ExecuteScalar will only return a Single Value - so keep that in mind when writing the query portion of your command.
The code to accomplish this would be as follows:
oracleConnection.Open
Dim obj as object 'Object to hold our return value
obj = cmd.ExecuteScalar()
oracleConnection.Close
If obj IsNot Nothing then
Return CDate(obj)
end if