SQL - INSERT with Scope_Identity() - getting the record id - asp.net

I have an ASP.NET page written in VB.NET that gets the items into a GridView by using a SELECT statement with INNER JOIN and also allows you to add an item to the invoice.
INNER JOIN that gets data from items and project_items.
SELECT Items.item_id, Items.item_name, Items.item_cost, project_items.item_quantity
FROM Items
INNER JOIN project_items
ON items.item_id = project_items.item_id
WHERE project_items.project_id = #parameter
#parameter is Session("ProjectID")
(There is a foreign key project_items.item_id -> items.item_id.)
I have an trying to use an SQL statement in VB.NET to try and INSERT into two tables simultaneously. What I tried is I tried to get the item_id of the last record created and insert into another table (project_items) by using that data. However, data is only being entered into the first table.
Any idea what I can do?
This is the code:
Protected Sub btnAddItem_Click(sender As Object, e As EventArgs) Handles btnAddItem.Click
Dim conn As New SqlConnection("Data Source=BRIAN-PC\SQLEXPRESS;Initial Catalog=master_db;Integrated Security=True")
Dim addItemComm As String = "SELECT item_id FROM project_items WHERE project_id=#ProjectID"
Dim user_id_select As New Integer
Dim addItemSQL As New SqlCommand
conn.Open()
addItemSQL = New SqlCommand(addItemComm, conn)
addItemSQL.Parameters.AddWithValue("#ProjectID", Convert.ToInt32(Session("ProjectID")))
Dim datareader As SqlDataReader = addItemSQL.ExecuteReader()
datareader.Close()
conn.Close()
Dim AddNewItemComm As String = "INSERT INTO Items (item_name, item_cost, item_code) VALUES (#ItemName, #ItemCost, #ItemCode); SELECT SCOPE_IDENTITY()"
Dim AddNewItem2Comm As String = "INSERT INTO project_items (item_id, project_id, item_quantity) VALUES (#ItemID, #ProjectID, #ItemQuantity) "
Dim AddNewItemSQL As New SqlCommand
conn.Open()
AddNewItemSQL = New SqlCommand(AddNewItemComm, conn)
AddNewItemSQL.Parameters.AddWithValue("#ItemName", txtItemName.Text.Trim)
AddNewItemSQL.Parameters.AddWithValue("#ItemCost", Convert.ToInt32(txtItemCost.Text))
AddNewItemSQL.Parameters.AddWithValue("#ItemCode", txtItemCost.Text.ToString.ToUpper)
Dim ItemId As Integer
ItemId = AddNewItemSQL.ExecuteScalar()
AddNewItemSQL.ExecuteNonQuery()
conn.Close()
conn.Open()
AddNewItemSQL = New SqlCommand(AddNewItem2Comm, conn)
AddNewItemSQL.Parameters.AddWithValue("#ItemID", ItemId)
AddNewItemSQL.Parameters.AddWithValue("#ProjectID", Convert.ToInt32(Session("ProjectID")))
AddNewItemSQL.Parameters.AddWithValue("#ItemQuantity", Convert.ToInt32(txtItemQuantity.Text))
AddNewItemSQL.ExecuteNonQuery()
conn.Close()
End Sub

Why are you doing this in multiple statements in the first place? Why not:
INSERT dbo.Items (item_name, item_cost, item_code)
OUTPUT inserted.ItemID, #ProjectID, #ItemQuantity
INTO dbo.project_items(item_id, project_id, item_quantity)
VALUES (#ItemName, #ItemCost, #ItemCode);
Now you only have to call one ExecuteNonQuery() and your app doesn't have to care about the actually SCOPE_IDENTITY() value generated. (You can still retrieve SCOPE_IDENTITY() if you want, of course, using ExecuteScalar - but as Nenad rightly points out, pick one instead of calling both.)
Since we now know that there is an explicit foreign key here, we can still reduce your C# code to one call even if we can't use the OUTPUT clause.
DECLARE #i INT;
INSERT dbo.Items (item_name, item_cost, item_code)
SELECT #ItemName, #ItemCost, #ItemCode;
SELECT #i = SCOPE_IDENTITY();
INSERT dbo.project_items(item_id, project_id, item_quantity)
SELECT #i, #ProjectID, #ItemQuantity
SELECT #i; -- if necessary
Would be even cleaner to put this into a stored procedure.

ItemId = AddNewItemSQL.ExecuteScalar()
AddNewItemSQL.ExecuteNonQuery()
These two rows next to each other will execute the command twice. You should remove the second one - ExecuteNonQuery. This will have your data inserted twice in the Items - two same rows but with different IDs.
Since you only retrieve ItemID from the first row, that one should be inserted in project_items, but the other one that was last inserted in items will have no matching row.
Also - complete section from beginning of button click method up before Dim AddNewItemComm As String - where you open and close DataReader and do nothing with it seems completely unnecessary.

Related

Retrieve the Id of recently inserted record

I currently have this SQL insert code in code behind
Dim con As New SqlConnection
Dim conString As String
conString = ConfigurationManager.ConnectionStrings("MyConnection").ConnectionString
con = New SqlConnection(conString)
con.Open()
Dim cmd As New SqlCommand("INSERT INTO AdditionalDaysRequest(Status, AdditionalDays, Justification,RequestDaySubmitted) VALUES (#Status,#AdditionalDays,#Justification,#RequestDaySubmitted)", con)
cmd.Parameters.AddWithValue("#Status", "Pending Request")
cmd.Parameters.AddWithValue("#AdditionalDays", TB_Days.Text)
cmd.Parameters.AddWithValue("#Justification", TB_Justification.Text)
cmd.Parameters.AddWithValue("#RequestDaySubmitted", Date.Now)
cmd.ExecuteNonQuery()
con.Close()
The Id in this table is automatically generated and incremented
What I would like to have now is the Id of this record inserted to add it to another table
Change your query text to add a second statement:
...;SELECT SCOPE_IDENTITY();
The SELECT SCOPE_IDENTITY() statement Returns the last identity value inserted into an identity column in the same scope as from the MSDN article above.
In addition, you can use the ability of the Sql engine to understand and process two or more command statements passed as a single string if you separe the statements with a semicolon.
In this way you have the great benefit to execute a single trip to the database.
Dim cmd As New SqlCommand("INSERT INTO AdditionalDaysRequest(Status, " & _
"AdditionalDays, Justification,RequestDaySubmitted) VALUES " & _
"(#Status,#AdditionalDays,#Justification,#RequestDaySubmitted);" & _
"SELECT SCOPE_IDENTITY()", con)
cmd.Parameters.AddWithValue("#Status", "Pending Request")
cmd.Parameters.AddWithValue("#AdditionalDays", TB_Days.Text)
cmd.Parameters.AddWithValue("#Justification", TB_Justification.Text)
cmd.Parameters.AddWithValue("#RequestDaySubmitted", Date.Now)
Dim result = cmd.ExecuteScalar()
con.Close()
if result IsNot Nothing Then
Dim lastInsertId = Convert.ToInt32(result)
End If
Notice that the two statements are now executed using ExecuteScalar instead of ExecuteNonQuery because we want to catch the result of the last command.
You will want to run a new SqlCommand. Set the value of lastInsertId with this statement:
SELECT SCOPE_IDENTITY()
This would be an additional knowledge.
we have multiple options like:
##IDENTITY
SCOPE_IDENTITY
IDENT_CURRENT
All three functions return last-generated identity values. However
IDENT_CURRENT returns the last identity value generated for a specific table in any session and any scope.
##IDENTITY returns the last identity value generated for any table in the current session, across all scopes.
SCOPE_IDENTITY returns the last identity value generated for any table in the current session and the current scope.

SQL Nesting iteration to update one table from another ASP.NET VB

I'm looking for a suggestion for the best way to accomplish the following task. I need to run an SQL query that will pull up to 200 rows. With each row, I need to take the data from that row and insert it into another table. At first I thought I would nest database connections like the code example I've listed below. However I was wondering if this was the incorrect way of doing it and what would be a better way.
Any ideas?
Dim dbconn As New SqlConnection
Dim dbconn2 As New SqlConnection
dbconn = New SqlConnection(ConfigurationManager.ConnectionStrings("databaseName").ConnectionString
dbconn2 = New SqlConnection(ConfigurationManager.ConnectionStrings("databaseName").ConnectionString
dbconn.Open()
Dim cmd As New SQLCommand
Dim cmd2 As New SQLCommand
Dim UserReader As SqlDataReader
Dim cmdStringSource As String = "SELECT approx 25 fields WHERE fldRandomField = 26"
Dim cmdStringUpdate As String
cmd = New SQLCommnd(cmdStringSource, dbconn)
UserReader = cmdExecuteReader()
If UserReader.HasRows Then
While UserReader.Read()
cmdStringUpdate = "UPDATE tblUpdate SET a whole bunch of stuff from the cmdStringSource current record"
dbconn2.Open()
cmd2=New SQLCommand(cmdStringUpdate, dbconn2)
cmd2.ExecuteNonQuery()
dbconn2.close()
End While
End If
UserReader.Close()
dbconn.close()
Instead of doing this in the loop, with multiple connections and datareaders - do it entirely in the back end.
SQL Server supports Insert from select e.g.
INSERT INTO Table1 (field1, field2, field3 ...)
SELECT field1, field2, field3 FROM FROM Table2
WHERE <some condition>
Same goes for UPPDATE e.g.
UPDATE Table1
SET Table1.Field1 = Table2.Field1,
Table1.Field2 = Table2.Field2
Table1.Field3 = Table2.Field3
FROM Table1 INNER JOIN Table2
ON Table1.CommonField = Table2.CommonField
WHERE <some condition>
Just build this statement and call it once with a single connection and ExecuteNonQuery command.

If I select a row

I am going to do my best to explain this, though admittedly I have not attempted much in months so I'm not only rusty, I wasn't good to being with.
I am using visual web developers and asp, vb is the code behind with a sql db.
If I select a columns from a table, for example:
sqlCmd.CommandText = "SELECT Username, W1, W2, W3, W4 FROM tablename"
Say there are multiple rows in this table with data in these columns.
When I do a datareader, or how I have been shown, I declare dr like:
Dim dr As Data.SqlClient.SqlDataReader
I can work with the selected items such as:
dr.item(0)
dr.item(1)
etc.
But the only items I can work with are the first row of items selected. How do I select all of the rows in the table. Or how can I work with the data from multiple rows using dr.item or by somehow telling it to move onto the next row so that dr.item(0) becomes the username for the second row in the table.
I hope that made sense and I'm sorry if this is a stupid question. I appreciate the time and help in advance. Thanks guys.
SqlDataReader.Read advances the reader to the next record and returns true when there is at least one other row:
Using conn = New SqlClient.SqlConnection(connString)
Using cmd = New SqlClient.SqlCommand("SELECT Username, W1, W2, W3, W4 FROM tablename", conn)
conn.Open()
Using dr = cmd.ExecuteReader()
While dr.Read()
Dim UserName As String = dr.GetString(0)
' ... '
End While
End Using
End Using
End Using
Use Using to dispose anything that implements IDisposable as soon as possible. It will also close connections implicitely.
Edit: using a DataTable
How do I select all of the rows in the table
The DataReader approach above works well, but if you want to select all rows anyway and it's ok to load all into memory, you can use a DataTable instead. Then you could also access each row via indexer like an array or list:
Dim tblUsers = New DataTable()
Using conn = New SqlClient.SqlConnection(connString)
Using da = New SqlClient.SqlDataAdapter("SELECT Username, W1, W2, W3, W4 FROM tablename", conn)
da.Fill(tblUsers)
End Using
End Using
' access a row via index: '
Dim row10 As DataRow = tblUsers.Rows(9)
Dim user10 = row10.Field(Of String)("Username")
' of course you can also iterate all rows: '
For Each row As DataRow In tblUsers.Rows
Dim userName = row.Field(Of String)("Username")
Next
To iterate through the rows of Data Table you need to use the following method:
while dr.read()
Dim col1=dr.item(0)
Dim col2=dr.item(1)
End while
So that You can each Attribute of ALL Rows.

Send Email based on Dropdownlist.SelectedItem & Also Insert the Dropdownlist.SelectedValue into Database

I don't know how to go about this. I have a web form with dropdown list, named Recruiter. I have two SQL tables named Perm_Commision_Lookup & UserList.
One of the tables contain email and the other does not. The Perm_Commision_Lookup table does not contain recruiter's email, so I decided to Inner Join it with UserList table which contain recruiter emails. The LookupValue (Recruiter's display name) column from the Perm_Commision_Lookup table is what's displayed to the end user and the pk_LookupID column is the one that's inserted into the database.
What I want to achieve is this: When a user select let's say "John Doe" from the Recruiter dropdown list, I want to send out an email to John Doe alerting him that a form has been submitted and at the same time insert the selected value (pk_LookupID) into the database.
I see that the dropdown list has two field named: DataTextField="LookupValue" and DataValueField="pk_LookupID but how to get the User_Email is my major problem. Below is my SQL select...So far I can do a SQL INNER JOIN...which shown below and also I can display multiple columns into dropdown list...I'm also trying SQLDataReader but I'm just stop with how to get it done.
SELECT Perm_Commision_Lookup.pk_LookupID, Perm_Commision_Lookup.LookupValue, UserList.User_Email
FROM Perm_Commision_Lookup
INNER JOIN UserList ON Perm_Commision_Lookup.LookupUserName = UserList.GM_Username
Any help will be appreciated...
Thanks for your reply! Correct! It's linked via LookupUserName. That's what I have as for now...but still have the problem
Public Sub BindDropDownListData()
' connection string
Dim connectionString As String = ConfigurationManager.ConnectionStrings("ConnectionString").ConnectionString
Using mySqlConnection As New SqlConnection(connectionString)
Try
' open the Sql connection
mySqlConnection.Open()
' Sql Command query to retrieve pk_LookupID, LookupValue, GM_Username, User_Email
Dim mySqlCommand As New SqlCommand(" SELECT Perm_Commision_Lookup.pk_LookupID, Perm_Commision_Lookup.LookupValue, UserList.GM_Username, UserList.User_Email FROM Perm_Commision_Lookup INNER JOIN UserList ON Perm_Commision_Lookup.LookupUserName = UserList.GM_Username order by LookupValue asc", mySqlConnection)
Dim mySqlDataAdapter As New SqlDataAdapter()
mySqlDataAdapter.SelectCommand = mySqlCommand
Dim myDataSet As New DataSet
' mySqlConnection.Open()
' Sql Data Reader to fetch the records row by row in forward direction.
Dim myDataReader As SqlDataReader = mySqlCommand.ExecuteReader()
'Perm_Commision_Lookup
If myDataReader.HasRows Then
' read each row fetched using DataReader
While myDataReader.Read()
Dim li As New ListItem()
'email = myDataReader("User_Email")
li.Value = myDataReader("pk_LookupID")
li.Text = myDataReader("LookupValue")
DropDownList1.Items.Add(li)
End While
End If
myDataReader.Close()
Catch ex As Exception
Label1.Text = ex.Message
Finally
' close the Sql Connection
mySqlConnection.Close()
End Try
DropDownList1.Items.Insert(0, New ListItem("Please Select Recruiter", ""))
End Using
End Sub
1: Use dropdown selectedIndexChanged event.
2: Inside the handler query the database to get the Email and send email.
3: You can selected lookupname from dropdown in the SelectedIndexChanged handler like myddl.SelectedItem.Text or you can get lookupid like myddl.SelectedValue.
From your query it seems your tables are linked via LookupUserName correct or is it LookupId?
And this might help.

how to insert an integer value from a Grid to Sql table?

I have a AdvWebGrid where the 7th column is DynEdit where user will enter the value. Now I have to take the entered value and insert it into the SQL table.
For example I have 7 records in the grid, the user will enter some comments for the first three records and save. Now I want to insert/ update the first three comments in the table.
If you are able to get the info in the 7th column you could use a datable with the rows you need, the use a sqldataadapter to fill the info into the sql server, i feel this is the best way to do it.
Other way is create a stored procedure in your SQL Server then invoque it from your .net program using SqlCommand and SqlDataReader...
Here is an example:
Dim val as integer = value you want to insert
Dim comi As New SqlCommand
Dim dr As SqlDataReader
Dim _con as sqlconnection
_con.ConnectionString = _strcon ' connection string
comi.CommandType = CommandType.StoredProcedure
comi.CommandText = sp_name ' your stored procedure is sp_name this inserts a value into the table x
comi.Connection = _con
comi.Parameters.AddWithValue("val",val)
dr = comi.ExecuteReader
dr.Close()
This should do the trick ...
Greetings !

Resources