SQLDataAdapter filling datatable with primary key produces error and exits sub - vb.net

Ok so this is going to take some explaining.
The process I am trying to do is grab data from a table function in SQL and then fill a dataset with the returned values.
I then have to run this query twice more to query an alternative number table. Then add to the same table as the previous queries.
This needs to be as fast as possible, so I am currently using an adapter.fill to populate the datasets and then a dataset.merge to put them all into one table.
The problem is the query can return duplicates which waste time and space, because of this I made column 3(part_ID) the primary key to stop duplicates.
When this is run with the .merge it quits at the first instance of a duplication and doesn't continue with the population.
The code below is what I used to fix this, I was just wondering if there is a better more elegant solution.
com = New SqlCommand(sqlPN, myConnect)
adapter.SelectCommand = com
adapter.Fill(temp, "Table(0)")
Dim data As New DataSet
data = temp
temp.Tables(0).Columns(3).Unique = True
firstSet = temp.Tables(0).Rows.Count
temp.AcceptChanges()
If temp.Tables(0).Rows.Count < maxRecords Then
Dim sqlAlt As String = "select Top " & (maxRecords + 10 - temp.Tables(0).Rows.Count) & " * from getAltEnquiry('" & tbSearchFor.Text & "') ORDER BY spn_partnumber"
adapter.SelectCommand.CommandText = sqlAlt
adapter.FillLoadOption = LoadOption.OverwriteChanges
adapter.Fill(temp, "Table(1)")
For i = 0 To temp.Tables(1).Rows.Count - 1
Try
temp.Tables(0).ImportRow(temp.Tables(1).Rows(i))
Catch e As Exception
End Try
Next
End If
If temp.Tables(0).Rows.Count < maxRecords Then
Dim sqlSuPN As String = "select Top " & (maxRecords + 5 - temp.Tables(0).Rows.Count) & " * from getSuPNEnquiry('" & tbSearchFor.Text & "') ORDER BY spn_partnumber"
adapter.SelectCommand.CommandText = sqlSuPN
adapter.Fill(temp, "Table(2)")
For i = 0 To temp.Tables(2).Rows.Count - 1
Try
temp.Tables(0).ImportRow(temp.Tables(2).Rows(i))
Catch e As Exception
End Try
Next
End If</code>
Thanks for any help or advice ^__^

Since you are looping through the records from the additional queries and using the ImportRow, your code will throw an exception if more than one record with the same value in the primary key field is attempted to be inserted. That is the purpose of a primary key when using in this way. If you want to ensure that your table only has unique records, you will need to ensure that the records are distinct before inserting them by checking the new row's part_id value against those already in the table. However, your design isn't necessarily the ideal approach.
Since you mentioned that this needs to be fast, it will probably be best if you could write a stored procedure to return just the rows you need from all tables and do the Fill into the table once.
If that's not possible, you can call adapter.Fill on the same DataTable for each of your data sources. Use the Fill overload that takes just the DataTable to fill and as per the docs, it will merge the data together if more than one record with the same primary key exists. The way you have the Fill method called, it is creating a new DataTable with the name you provide for each time you call Fill. Instead, you want to Fill just one DataTable.
"You can use the Fill method multiple times on the same DataTable. If a primary key exists, incoming rows are merged with matching rows that already exist. If no primary key exists, incoming rows are appended to the DataTable."

Related

Clone a datarow into same table but primary key creatin error

I'd like to allow the user to clone a record. Data is from MS SQL db and is contained in a single table with a primary key (int32) autoincrementing titled "CriteriaID". I get a CriteraID can't be null error which I would expect since it's an ID column. I tried setting it to allowdbnull in on the dataset side but no luck. How do I set it to the value needed to eventually save it into DB? If I hit new record that column is -1,-2,-3, etc. but in this case I want to clone all columns EXCEPT for the CriteriaID. I also tried NOTHING, VBNULL, and a random integer not already in DB. WOuld it be easier to use a different method?
Try
If Not IsNothing(TCriteriaBindingSource.DataSource) Then
Dim sr As DataRow() = DsCriteria.Tables("tCriteria").Select("CriteriaID = " & DsCriteria.Tables("tCriteria").Rows(TCriteriaBindingNavigator.BindingSource.Position).Item("CriteriaID"))
sr(0).Item("CriteriaID") = DBNull.Value
DsCriteria.Tables("tCriteria").NewRow()
DsCriteria.Tables("tCriteria").Rows.Add(sr(0))
End If
Catch ex As Exception
MsgBox(ex.Message)
End Try

Adding +1 in a auto increment value in SQL using vb

I want to generate an id in my vb program it will get the latest auto incremented number and will add one but my code don't work am I missing some thing or am I doing it wrong? please help me! THANKS!
Here is the code:
OpenServer()
Dim num As Integer
Dim num1 = num + 1
Newdataset("SELECT MAX(IDnum) AS IDnum FROM addnewemployee WHERE IDnum = '" & num & "' ")
txtEmpNumber.Text = "" & num1 & "." & dtpdatehired.Value.ToString("yyyyMMdd") & ""
Your method has several flaws.
First, your SQL statement should be:
SELECT MAX(IDnum) AS IDnum FROM addnewemployee
This will get the highest IDnum from the addnewemployee table. The problem with this is that if a higher record has been created and deleted. Lets say that you get a return value of 55. It is possible a record with an id of 56 has been created and deleted.
The query you really want to use is
SHOW TABLE STATUS WHERE `Name` = 'addnewemployee'
Where will get a column named Auto_increment. Get that value from the dataset, then increment it.
But it is a bad idea to do this.
If you have multiple, dependant insert, you shouild either retrieve the id at the time of insert and store that in a variable. Or better still do everything in one statement or stored procedure.

Best way to extract rows from DataTable (based on date field) and copy to another DataTable

I have a DataTable containing about 30 rows and I need to extract all rows having a date field bigger than a date stored into a variable.
(This code will be executed a lot of times)
I found three ways to do this but I would like to know how to choose because I don't know the differences between various codes.
Here is what I was able to write (and my worries):
1st way (DataTable.Select)
Dim SelectedRows() As DataRow = DT_DBData.Select("In_Date=#" & _
LastDate.ToString("yyyy-MM-dd") & "#")
Using New_Dt As DataTable = SelectedRows.CopyToDataTable
'Some code
End Using
I'm worried about the string format: I'm afraid that some rows may be not extracted because of a date formatting error.
2nd way (query Linq)
Using New_Dt As DataTable = (From DBData In DT_DBData.AsEnumerable() _
Where DBData.Field(Of Date)("In_Date") >= LastDate).CopyToDataTable
'Some code
End Using
I never used Linq and so I don't know what kind of issues can it give me.
3rd way (For Each Loop + If Then)
Using New_Dt As DataTable = DT_DBData.Clone
For Each dr As DataRow In DT_DBData.Rows
If dr("In_Date") >= LastDate Then
New_Dt.Rows.Add(dr.ItemArray)
End If
Next
'Some code
End Using
I'm not really worried about this code. I only think that the others could be better or faster (but I can't answer to this)
Faster is kind of irrelevant when dealing with 30 rows.
The first one is kind of wasteful. You start with a DataTable, Select to get a subset, then convert the result into a new DataTable. Time to extract matching Rows: 8 ms.
You can work with the SelectedRows array without putting it into a new DataTable. If it goes back to the DB after "some code", I would not extract it from the DT.
By the way, there is no reason to worry about matching date formats as long as the DB column is a date type (and therefore, the DataTable column will be also). Dates do not have a format; formats are just how computers (and by extension, us) display them to users.
Dim drs = dt.Select(String.Format("StartDate > '{0}'", dtTgt.Date), "")
The date type I pass will compare/filter just fine with the DateTime data for that column. Formats only come into play when you convert them to string, which is mostly only needed for those pesky users.
One option you missed might be especially useful if this will be done over and over: A DataView:
dt.Load(cmd.ExecuteReader)
' create dataview
Dim dv As New DataView(dt)
dv.RowFilter = String.Format("StartDate > '{0}'", dtTgt.Date)
dv.Sort = "StartDate asc"
' show/iterate/whatever
dgv.DataSource = dv
If the data goes back to the DB, using this method, the rows will retain all the rowstate values.

Checking for a null (or no result) from dataset

I'm working with the following bit of code in VisualBasic.NET. It's essentially supposed to be pulling a row ID from the table with specific conditions in mind. However, I would like to set up a failsafe of getting around those conditions if need be.
I'm trying to write an If statement to compare the Item(0) against but this bit of code seems to trigger no matter what. How can I compare to see if the Query being written as QuestionConnectionQuery is actually returning rows?
For i As Integer = 1 To intNoOfQuestions
'TODO: If there are no valid questions, pull up any of them that meets the difficulty requirement....
' Go into the Database and retrieve a question that hasn't been selected in the last seven days.
Dim QuestionConnectionQuery As New OleDb.OleDbCommand("SELECT TOP 1 Questions.QuestionID, Questions.QuestionCategory & ' : ' & Questions.QuestionSubCategory AS Category FROM Questions WHERE (((Questions.QuestionDifficulty)=[?])) OR (((Questions.LastDateRevealed) Is Null)) OR ((Questions.LastDateRevealed)>=DateAdd('d',-7,Now())) ORDER BY Rnd(QuestionID);", QuestionConnection)
QuestionConnectionQuery.Parameters.AddWithValue("?", intQuestionDifficulty(i - 1).ToString)
Dim QuestionDataAdapter As New OleDb.OleDbDataAdapter(QuestionConnectionQuery)
Dim QuestionDataSet As New DataSet
QuestionDataAdapter.Fill(QuestionDataSet, "Questions")
' If the result is not null... add it to the array.
If IsNothing(QuestionDataSet.Tables("Questions").Rows(0).Item(0)) Then
' Add that to the intQuestArray() array
intQuestArray(i - 1) = QuestionDataSet.Tables("Questions").Rows(0).Item(0)
Else
MessageBox.Show("ERROR: Not enough questions.")
QuestionDataAdapter.Fill(QuestionDataSet, "Questions")
End If
' Update the database by adding today's date to the "LastDateRevealed" column of the table.
Dim QuestionUpdateQuery As New OleDb.OleDbCommand("UPDATE Questions SET LastDateRevealed=NOW() WHERE QuestionID = [?]", QuestionConnection)
QuestionUpdateQuery.Parameters.AddWithValue("?", QuestionDataSet.Tables("Questions").Rows(0).Item(0).ToString)
QuestionUpdateQuery.ExecuteNonQuery()
Next
If IsDBNull(QuestionDataSet.Tables("Questions").Rows(0).Item(0)) Then
https://msdn.microsoft.com/en-us/library/tckcces5(v=vs.90).aspx

How do I access multiple records from the same table using SQLDataAdapter?

This almost works. I get an error at the last line that looks like it's complaining about the C1 reference. Is there a simple way around this? There is nothing wrong with the query or connection.
Dim CmdString As String
Dim con As New SqlConnection
Try
con.ConnectionString = PubConn
CmdString = "select * from " & PubDB & ".dbo.Suppliers as S " & _
" join " & PubDB & ".dbo.Address as A" & _
" on S.Supplier_Address_Code = A.Address_IDX" & _
" join " & PubDB & ".dbo.Contacts as C1" & _
" on S.Supplier_Contact1 = C1.Contact_IDX" &
" join " & PubDB & ".dbo.Contacts as C2" & _
" on S.Supplier_Contact2 = C2.Contact_IDX" &
" WHERE S.Supplier_IDX = " & LookupIDX
Dim cmd As New SqlCommand(CmdString)
cmd.Connection = con
con.Open()
Dim DAdapt As New SqlClient.SqlDataAdapter(cmd)
Dim Dset As New DataSet
DAdapt.Fill(Dset)
con.Close()
With Dset.Tables(0).Rows(0)
txtAddress1.Text = .Item("Address1").ToString
txtAddress2.Text = .Item("Address2").ToString
txtSupplierName.Text = .Item("Address_Title").ToString
txtAttn.Text = .Item("Attn").ToString
txtBusinessPhone1.Text = .Item("C1.Contact_Business_Phone").ToString
You would not include the "C1" table alias as part of your column name. It will be returned from your query as Contact_Business_Phone.
For accessing multiple rows you could use the indexer as you are in the example above "Rows(0)" by placing your With block into a For loop and accessing the "Rows(i)" with your loop variable. However, this would not help much as your are assigning this to individual text boxes, so you'd only see the last value on your page/screen.
The alias C1 is used by SQL Server and is not persisted to the result set. Have you taken this query into SQL Management Studio to see the results?
Since you requested all columns (*) and joined to the Contacts table twice, you'll end up with duplicate column names in the result. For example, if the Contacts table has a LastName field, you'll end up with TWO LastName columns in your result.
I haven't tried to duplicate this in my local environment, but I can't imagine the data adapter is going to like having duplicate column names.
I recommend specifically including the columns you want to return instead of using the *. That's where you'll use the alias of C1, then you can rename the duplicate columns using the AS keyword:
SELECT C1.LastName AS [Supplier1_LastName],
C2.LastName AS [Supplier2_LastName],
...
This should solve your problem.
Good Luck!
You should only be pulling back the columns that you're in fact interested in, as opposed to *. It's sort of hard to tell exactly what data exists in which tables since you're pulling the full set, but at a quick guess, you'll want in your select statement to pull back A.Address1, A.Address2, A.AddressTitle, ?.Attn (not sure which table this actually derives from) and C1.Contact_Business_Phone. Unless you actually NEED the other fields, you're much better off specifying the individual fields in your query, besides having the possible duplicate field issue that you're running into here, it can also be a significant performance hit pulling everything in. After you clean up the query and only pull in the results you want, you can safely just reference them the way you are for the other fields, without needing a table alias (which as others have pointed out, isn't persisted to the result set anyways).