Function to check if Input is in column in SQL Server database - sql

Okay, I'm creating an application which requires the user to input there Gamer-Tag during the registration process. Only 'Members' of a group may have access to the program and so I need the application to refuse anyone who's Gamer-Tag is not in the SQL Server database.
I adapted a function I created to log-in with your details and changed it to check if the Gamer-Tag is on the database, but I'm receiving a strange error:
The column 'Gamer_Tag' is not a Member of 'Members_Details' Table.
Please note, that's an error and not what I am wanting it to do. It's saying the column 'Gamer_Tag' isn't in the table, but I know it is! I've checked spelling and spacing and everything's as it should be, so I'm assuming it's something wrong with the code.
The (relevant) register button code:
(Note, RegUserName.Text is the textbox that contains the users Gamer-Tag.
Dim dbManager As New DatabaseManager()
If dbManager.CheckGamerTagisMember(RegUserName.Text) Then
MsgBox("Gamer-Tag Not A Member.")
My.Settings.RegisterCount = 1
My.Settings.Save()
RegisterBTN.Enabled = True
MsgBox("Registration Failed.")
GoTo Ender
Else
MsgBox("Gamer-Tag is A Member.")
GoTo Ender
End If
And the function this is calling:
Public Function CheckGamerTagisMember(ByVal gamertag As String) As Boolean
Connection = New SqlConnection("Data Source =" & My.Settings.ServerIP & ";Initial Catalog=Members_Details;Integrated Security=False;User=" & My.Settings.UserName & ";Password=********;")
Connection.Open()
Dim gamertagDatSet As New DataSet()
usersDataAdapter.FillSchema(gamertagDatSet, SchemaType.Source, "Members_Details")
usersDataAdapter.Fill(gamertagDatSet, "Members_Details")
Dim table As DataTable = gamertagDatSet.Tables("Members_Details")
For i As Integer = 0 To table.Rows.Count - 1
Dim currentUsergt As String = table.Rows(i)("Gamer_Tag").ToString.Trim()
If (currentUsergt = gamertag) Then
gamertagDatSet.Dispose()
Connection.Close()
Return True
End If
Next
gamertagDatSet.Dispose()
Connection.Close()
Return False
End Function
And to prove the column is in the table, here's the table's columns;
I'm new to structuring my code into classes, so I could be 'passing' things around wrongly.
Any help would be much appreciated!

I haven't a clue what the issue is with your code (there's probably something else going on which is not evident in your question).
But I will say that the approach you are taking will not scale well. With each registration, you are downloading the entire table to the program and then scanning through the rows, one by one, looking for an ID.
It would be much easier to write a simple stored procedure to do this for you. You would also get the advantage of using indexes. For example:
CREATE PROCEDURE spGetMembers_ID
#Gamer_Tag varchar(255)
AS
SET NOCOUNT ON;
SELECT Members_ID
FROM Members_Details
WHERE Gamer_Tag = #Gamer_Tag
GO
You can call this stored proc this way:
Dim getMembers_IDCommand As New SqlCommand("TestProcedure", Connection)
getMembers_IDCommand.CommandType = CommandType.StoredProcedure
Dim gamerTagParam As SqlParameter = getMembers_IDCommand.Parameters.Add("#Gamer_Tag", SqlDbType.VarChar, 255)
gamerTagParam.Value = gamertag
SqlDataReader result = getMembers_IDCommand.ExecuteReader();
Dim exists As Boolean = myReader.Read()
result.Close()
Return exists
There, of course, many ways to execute a stored procedure and get the results. Several are documented here. But the point is that a stored procedure is faster, more scalable and generally faster.

Related

Problems updating a database using vb.net, oledbdataadapter

After going over multiple questions/answers on Stackoverflow and other boards I'm still lost on why I can't update an Access database from a datatable. I'm trying to take data from a datatable and insert that data into an Access table if it is blank, and replace the table if it already has data. I can successfully replace the table, but the data from the datatable does not get added.
However, the method which I'm using does not appear to work. My datatable comes from a bound datagridsource and the Access layer is called like this:
ConnectedDB.UpdateTable(DBTable, bsDataSource.DataSource)
Where ConnectedDB is the Access Layer class, DBTable is the string containing the Access table name, and bsDataSource is the bound data. As you can see, I passed the .Datasource to turn it into a datatable.
Here is the original (pre-Jan 29th) section of my work to add the datatable back into the Access table:
Public Function UpdateTable(strTable As String, dgDataTable As DataTable) As Boolean
Dim DS As New DataSet
dgDataTable.TableName = strTable
DS.Tables.Add(dgDataTable)
Using OpenCon = New OleDb.OleDbConnection(strConnectionString)
Using DataAdapter As New OleDbDataAdapter("SELECT * FROM " & strTable, OpenCon)
Dim DBcmd As OleDbCommandBuilder = New OleDbCommandBuilder(DataAdapter)
DBcmd.QuotePrefix = "["
DBcmd.QuoteSuffix = "]"
DataAdapter.UpdateCommand = DBcmd.GetUpdateCommand()
Try
OpenCon.Open()
DataAdapter.Fill(DS.Tables(strTable))
If DataAdapter.Update(DS.Tables(strTable)) > 0 Then
Return True
Else Return False
End If
Catch exo As Exception
MessageBox.Show(exo.Message)
Return False
End Try
End Using
End Using
End Function
My function tries to update an existing Access table with the name represented as strTable with the information in the datatable, dgDataTable from a datagridview. Each run hits the update check > 0 and returns a false which means syntax wise it should be working (i.e. no error messages). I have traced the table and it has all the data it should have (so the information is getting passed correctly from the grid through the update commands). I was playing with applying it in a dataset but I'm not sure I really need that.
I was tracing the variables through the update method and I think I found out why it won't update but I'm not sure what to do about it. The query it comes up with is like this:
UPDATE [RtoC] SET [R] = ?, [C] = ?, [Type] = ?, [Unknown] = ? WHERE (([R] = ?) AND ([C] = ?) AND ([Type] = ?) AND ((? = 1 AND [Unknown] IS NULL) OR ([Unknown] = ?)))
The Access table name is RtoC with fields R, C, Type, and unknown.
I'm thinking the "?" are not getting filled in causing the query to just not apply data back to Access. I'm not sure though how to set those items.
EDIT 1/29/20: I used the code changes I and jmcihinney document below and it does insert the lines into the Access table. This edit alters the question to be more specific about what I'm am trying to do, and how the datatable is created. Hopefully this clears up some wording on my part and provides some basis for the alteration of the row state.
The issue is that the Fill method of that data adapter calls AcceptChanges on the DataTable after populating it, thus there are no changes to save when you call Update.
That call to Fill shouldn't be there anyway though, because you don't want to retrieve any data, just save changes. You've got a whole lot of pointless code there. It should look more like this:
Public Function UpdateTable(strTable As String, dgDataTable As DataTable) As Boolean
Using DataAdapter As New OleDbDataAdapter("SELECT * FROM " & strTable, strConnectionString)
Dim DBcmd As OleDbCommandBuilder = New OleDbCommandBuilder(DataAdapter)
DBcmd.QuotePrefix = "["
DBcmd.QuoteSuffix = "]"
Try
Return DataAdapter.Update(dgDataTable) > 0
Catch exo As Exception
MessageBox.Show(exo.Message)
Return False
End Try
End Using
End Function
I took another tack at manipulating the database and in looking that up, I found the answer provided by jmcilhinney back in 2014! [Bulk Insert From DataTable to Access Database
In a for each loop across the rows of my datatable I set this:
row.SetAdded()
If I was filling I would have done something like:
DataAdapter.AcceptChangesDuringFill = True
Before the Fill command.
Unless this method has changed or there is a better way, I'll mark the link as the answer.
Thanks jmcilhinney....twice!

vb.net sql Check if value exists in table and translate result into a variable

I'm creating a simple relational database system, to ensure no repeated data is saved in. I have created a chunk of code to check if the value of Album is already present in the selected table:
If Album IsNot Nothing Then 'checks if ALBUM exists
ALBUM_CHECK = New SqlCommand("SELECT ALBUM_ID FROM ALBUM_DB WHERE NAME=(#NAME)", SQLcon)
ALBUM_CHECK.Parameters.AddWithValue("#NAME", Album)
If ALBUM_CHECK.ExecuteScalar IsNot Nothing Then
album_Exist = True
Else
album_Exist = False
End If
End If
However this returns the error:
System.Data.SqlClient.SqlException: 'The data types text and nvarchar are incompatible in the equal to operator.'
Any ideas on how to get round this?
I believe it's not allowing me to read if the value returned is null. All help appreciated!
From the documentation of SQL Server 2017 :
IMPORTANT! ntext, text, and image data types will be removed in a future version of SQL Server. Avoid using these data types in new development work, and plan to modify applications that currently use them. Use nvarchar(max), varchar(max), and varbinary(max) instead.
I would suggest to change the datatype of this column to varchar(max), which has the same capacity in terms of storage and is properly supported by this RDBMS.
Text is a very poor choice for an Album. Comments and explanations in line.
Private Sub OPCode()
Dim Album As String = ""
Dim albumExist As Boolean
If String.IsNullOrEmpty(Album) Then
Return
End If
'Using blocks ensure that your database objects are not only closed but disposed.
'The disposal is important because they can contain unmanaged objects.
Using SQLcon As New SqlConnection("Your connection string")
'If Exists is a good method for large tables because it stops as soon
'as it finds a match.
Using ALBUM_CHECK As New SqlCommand("If Exists (SELECT 1 FROM ALBUM_DB WHERE NAME = #NAME) Select 1 Else Select 0;", SQLcon)
'Using .Add instead of .AddWithValue tells the database what kind of
'data we are sending. Without this we have no control over what ADO
'decides to sent to the database.
ALBUM_CHECK.Parameters.Add("#NAME", SqlDbType.NVarChar).Value = Album
'The query will only return a 0 or a 1.
SQLcon.Open
albumExist = CBool(ALBUM_CHECK.ExecuteScalar())
End Using
End Using
End Sub

DataReader running very slow

I'm currently working with a database of over 50 million records, where I read a file which a person wants to search the database for etc. I have noticed my data reader part is running particularly slow, where as the query seems almost instant (database is indexed). I was just wondering does anyone know as to why it seems to be running slow?
con.Open()
Using sw As New StreamWriter("G:\USER\SEARCH-RESULTS.txt")
Try
For Each word As String In result
Using com As New SqlCommand("select t.SmeNbr, t.FilPth, r.MaxDate, t.DteAdd, t.LnePos from (Select SmeNbr, MAX(FilDte) as MaxDate from Test_Table where SmeNbr = #word group by SmeNbr)r inner join Test_Table t on t.SmeNbr = r.SmeNbr and t.FilDte = R.MaxDate", con)
com.Parameters.AddWithValue("#word", word)
Using RDR = com.ExecuteReader
If RDR.HasRows Then
Do While RDR.Read
MyFilePath = RDR.Item("FilPth").ToString()
linePos = RDR.Item("LnePos").ToString()
Using sr As New StreamReader(MyFilePath)
sr.BaseStream.Seek(4096 * (linePos - 1), SeekOrigin.Begin)
FoundWords.Add(sr.ReadLine)
For Each item As String In FoundWords
sw.WriteLine(item)
Next
FoundWords.Clear()
End Using
Loop
Else
Continue For
End If
End Using
End Using
Next
Catch ex As Exception
MessageBox.Show("Couldn't process search")
Finally
con.Close()
End Try
End Using
MsgBox("Complete!")
So it works perfect, as in it gets the records and bits of info I want very quickly through the query and all and even the writing reults to a new file is near instant, I used breakpoints and like I said it seems to take ages between the "Using RDR = com.ExecuteReader" and "If RDR.HasRows Then"
Any help or ideas would be greatly appreciated.
com.Parameters.AddWithValue("#word", word)
AddWithValue infers the parameter data type from the provided .NET object value. Since .NET strings are Unicode, this code is will add an nvarchar(n) parameter with the length of the actual value. I see from your comments that the actual column data type is char(13) so it would be best to explicitly specify that as the parameter data type:
com.Parameters.Add("#word", SqlDbType.Char, 13).Value = word
The implications with AddWithValue are that indexes might not be used due to the mismatched data type and there may be many variations of the same query in the SQL Server procedure cache that differ only by length. For these reasons, I suggest one avoid AddWithValue.

CommandText Property Has Not Been Initialized, retrieving data

Private Sub ButtonSubmitID_Click(sender As Object, e As EventArgs) Handles ButtonSubmitID.Click
Dim comm As New SqlCommand
Dim conn As New SqlConnection
conn.ConnectionString = "Data Source = localhost\SQLEXPRESS; Initial Catalog = test2Forms; Integrated Security = SSPI;"
comm.Connection = conn
Dim ID = TextBoxID.Text
comm.Parameters.AddWithValue("#ID", ID)
Dim adapter As SqlDataAdapter = New SqlDataAdapter(comm.CommandText, comm.Connection)
comm.CommandText = "SELECT * FROM withActivityLog3 WHERE ID = #ID"
Dim records As DataSet = New DataSet
adapter.Fill(records)
DataGridView2.DataSource = records
End Sub
CommandText property has not been initialized is the error I am receiving. I am able to pull all the data from the database into the GridView on the Form Load but when I try to narrow it down to one ID using a WHERE clause on the button trigger, it comes up with the above error. I've used the debugger to trace through one step at a time and the command and connection strings look correct. I've also successfully duplicated the query on my database using the SQL Server command line. I'm searching on a primary key (ID) so the expected results would be one uniquely identified row from the database.
As for the problem you know you have:
' initialize DataAdapter with (EMPTY) commandtext
Dim adapter As SqlDataAdapter = New SqlDataAdapter(comm.CommandText, comm.Connection)
' initialize Command Text
comm.CommandText = "SELECT * FROM withActivityLog3 WHERE ID = #ID"
When you pass the CommandText to the DataAdapter, it is empty because you havent set it yet which results in the error.
There is a fair amount of inefficiency in your code though. Rewritten:
' form level conn string
Private TheConnString As String = "Data Source = localhost\..."
Private Sub ButtonSubmitID_Click(sender ...
Dim dt As New DataTable
Using dbcon As New MySqlConnection(TheConnString)
Using cmd As New MySqlCommand("select * from Sample where Id = #id", dbcon)
cmd.Parameters.Add("#id", MySqlDbType.Int32).Value = Convert.ToInt32(TextBox2.Text)
dbcon.Open()
dt.Load(cmd.ExecuteReader)
dgvA.DataSource = dt
End Using
End Using
End Sub
Note: this uses MySQL but the concepts are the same for Sqlite, Access, SQL Server etc
There is no need to type or paste the connection string and over everywhere it is used. One form level variable will allow DRY (Dont Repeat Yourself) code.
Anything which implements the Dispose() method should be disposed of. That includes nearly all the DB Provider objects. The Using statement allows you to declare and initialize an object and at the End Using it is disposed of. Failing to Dispose of things can cause leaks and even run out of connections or resources to create things like DB Command objects.
There is no need to create a local DbDataAdapter. These are very powerful and useful critters meant to do much more than fill a DataTable. If that is all you are doing, you can use ExecuteReader method on the DbCommand object.
Nor do you need a local DataSet. Contrary to the name, these do not hold data, but DataTables. Since there is only one and it is local (goes out of scope when the method ends), you dont need a DataSet to store it.
The Add method should be used rather than AddWithValue. The code above specifies the datatype for the parameter so there is no guesswork required of the compiler. Of course with that comes the need to convert the text to a number...
...Since this is user input, you should not trust the user, so Integer.Tryparse would be more appropriate: I like pie will not convert to an integer. Data Validation is something you should do before you commence the DB ops.
Dim ID = TextBoxID.Text as used is pointless code. You do not need to move the textbox text into a new variable in order to use it. However, ID might be used to store the integer value

Fill Datatable using SQL 'select' WITHIN A TRANSACTION

I would like to fill a datatable with results from a SQL select statment but using a transaction. The reason that I am using a transaction is because I have a list of names (as a datatable), and I want to iterate through the list of names and select the database rows where the name = the name on the list. There are 500,000 names in the database and I only want to retreive the relevant rows. I have the code for the procedure as I think it should look like (untested) BUT I dont know HOW to place the data into a datatable .... so Im missing something where I declare the datatable and the 'fill' of that table , could someone help with this ? Or suggest how else I can get the information out of the batabase without looking up each name individually.
Using connection As New SQLite.SQLiteConnection(R2WconectionString)
connection.Open()
Dim sqliteTran As SQLite.SQLiteTransaction = connection.BeginTransaction()
Try
oMainQueryR = "SELECT NameID, Address, Ocupation FROM Employees Where Name= :Name"
Dim cmdSQLite As SQLite.SQLiteCommand = connection.CreateCommand()
With cmdSQLite
.CommandType = CommandType.Text
.CommandText = oMainQueryR
.Parameters.Add(":Name", SqlDbType.VarChar)
End With
'Prevent duplicate selects by using a dictionary
Dim NameInalready As New Dictionary(Of String, String)
For Each row As DataRow In TheLIST.Rows
If NameInalready.ContainsKey(row.Item("Name")) Then
Else
NameInalready.Add(row.Item("Name"), "")
cmdSQLite.Parameters(":Name").Value = row.Item("Name")
cmdSQLite.ExecuteNonQuery()
End If
Next
sqliteTran.Commit()
Catch ex As Exception
End Try
End Using
First, you don't need a transaction because you aren't updating the database.
Second, depending on the possible number of Names in TheLIST, it might be worthwhile for you to change the name selector to IN (i.e. SELECT * FROM Employees WHERE Name IN ('name1', 'name2'). However, if you expect more than about 10, this is probably not worth trouble.
Finally, you need to create a new DataTable to hold the results. Then you need to create a DataAdapter passing cmdSqlLite as the constructor parameter. And finally, replace your ExecuteNonQuery with DataAdapter.Fill(DataTable).
For example (after Dim cmdSQLite):
Dim oDataTable As New DataTable("Employees")
Dim oAdapter As New SqliteDataAdapter(cmdSQLite)
and replacing the ExecuteNonQuery line with:
oAdapter.Fill(oDataTable)
I will qualify this code by saying it may need some tweaks. I only work with class objects and collections, so my preference would have actually been to load a collection of Employee class instances.
I would have done that by replacing ExecuteNonQuery with ExecuteReader and then the loading the read data into a new class instance. This type of approach resolves various issues with serializing the data across service boundaries (i.e. Xml for web services) and also lets you embed business logic, if needed, into the classes.