Returning multiple values from a stored procedure - sql

I am using SQL Server. I am calling a stored procedure from another stored procedure.
I want to return multiple values from the first stored procedure.
Ex: I am calling Sub_SP from Master_SP. Sub_SP will return multiple values to Master_SP.
Can anyone give an example with OUTPUT parameters?
Thank you.

ALTER procedure ashwin #empid int,#empname varchar(20) output,#age int output
as
select #empname=ename,#age=age
from emp where empid=#empid;
declare #ename varchar(20),#age int
execute ashwin 101,#ename out,#age out
select #ename,#age;
//------------
namespace sqlserver
{
class Program
{
static void Main(string[] args)
{
createconnection();
}
public static void createconnection()
{
SqlConnection con=new SqlConnection("Data Source=ASHWIN\\SQLEXPRESS;Initial Catalog=employee;Integrated Security=True;Pooling=False");
con.Open();
SqlCommand cmd=new SqlCommand("ashwin",con);
cmd.CommandType=CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#empid",SqlDbType.Int,10,"empid"));
cmd.Parameters.Add(new SqlParameter("#empname", SqlDbType.VarChar, 20,ParameterDirection.Output,false,0,20,"ename",DataRowVersion.Default,null));
cmd.Parameters.Add(new SqlParameter("#age", SqlDbType.Int, 20, ParameterDirection.Output, false, 0, 10, "age", DataRowVersion.Default, null));
cmd.Parameters[0].Value = 101;
cmd.UpdatedRowSource = UpdateRowSource.OutputParameters;
cmd.ExecuteNonQuery();
string name = (string)cmd.Parameters["#empname"].Value;
int age = Convert.ToInt32(cmd.Parameters["#age"].Value);
Console.WriteLine("the name is {0}--and age is {1}", name,age);
Console.ReadLine();
}
}

How about this:
CREATE PROCEDURE dbo.SubSP
#Value1 INT OUTPUT, #Value2 INT OUTPUT
AS
-- just return two values into the OUTPUT parameters somehow....
SELECT #Value1 = 42, #Value2 = 4711
Test the dbo.SubSP:
DECLARE #Out1 INT, #Out2 INT
EXEC dbo.SubSP #Out1 OUTPUT, #Out2 OUTPUT -- int
SELECT #Out1, #Out2
Gives output:
#Out1 #Out2
42 4711
Then create "master" stored procedure:
CREATE PROCEDURE dbo.MasterSP
#SomeValue1 INT OUTPUT, #SomeValue2 INT OUTPUT, #SomeValue3 INT OUTPUT
AS BEGIN
DECLARE #Out1 INT, #Out2 INT
-- call the "sub" stored procedure and capture OUTPUT values
EXEC dbo.SubSP #Out1 OUTPUT, #Out2 OUTPUT
-- return those values - plus some others - from master stored procedure
SELECT #SomeVAlue1 = #Out1, #SomeVAlue2 = #Out2, #SomeValue3 = 9901
END
Test master stored proc:
DECLARE #Some1 INT, #Some2 INT, #Some3 INT
EXECUTE dbo.MasterSP #Some1 OUTPUT, #Some2 OUTPUT, #Some3 OUTPUT
SELECT #Some1, #Some2, #Some3
Gives output:
(No column name) (No column name) (No column name)
42 4711 9901
Does this work? Does this solve your problem? If not: where are you stuck, what exactly is the problem?

I had the same problem and I am working in VB. I have tried the #Scorpian275 's answer. This is the same solution as #Scorpian275 provided but it is for the VB. The sql part is the same.
Public Shared Sub createConnection()
Dim con As SqlConnection = New SqlConnection("Data Source=ASHWIN\\SQLEXPRESS;Initial Catalog=employee;Integrated Security=True;Pooling=False")
con.Open
Dim cmd As SqlCommand = New SqlCommand("ashwin", con)
cmd.CommandType = CommandType.StoredProcedure
cmd.Parameters.Add(New SqlParameter("#empid", SqlDbType.Int, 10, "empid"))
cmd.Parameters.Add(New SqlParameter("#empname", SqlDbType.VarChar, 20, ParameterDirection.Output, false, 0, 20, "ename", DataRowVersion.Default, Nothing))
cmd.Parameters.Add(New SqlParameter("#age", SqlDbType.Int, 20, ParameterDirection.Output, false, 0, 10, "age", DataRowVersion.Default, Nothing))
cmd.Parameters(0).Value = 101
cmd.UpdatedRowSource = UpdateRowSource.OutputParameters
cmd.ExecuteNonQuery
Dim name As String = CType(cmd.Parameters("#empname").Value,String)
Dim age As Integer = Convert.ToInt32(cmd.Parameters("#age").Value)
Console.WriteLine("the name is {0}--and age is {1}", name, age)
Console.ReadLine
End Sub
The last parameter in
cmd.Parameters.Add(New SqlParameter("#age", SqlDbType.Int, 20, ParameterDirection.Output, false, 0, 10, "age", DataRowVersion.Default, Nothing))
is the initial value that we provide for those variables (Here Nothing).

Related

Insert data into 2 related tables at once?

I am trying to insert into two tables at once using the primary key from the first table to insert into the second but I am not sure how to go about it.
My table structure is as follows:
Person
PersonId
FirstName
Surname
Employee
EmployeeId
HoursWorked
PersonId in the Person table is an auto incremented column. EmployeeId in the second table is a primary and foreign key that should be the same as PersonId.
I been trying with this query string which I found on Google but with not much luck:
string queryString = "BEGIN TRANSACTION DECLARE #DataID int; "
+"INSERT INTO Person(FirstName, Surname) VALUES(#firstName, #surname);"
+ "SELECT #DataID = scope_identity();"
+ "INSERT INTO Employee VALUES(#DataId, #hoursWorked);"
+ "COMMIT";
From C#, you can try something like this:
// define the INSERT query - insert a firstname,surname into "Person",
// and insert a row into the Emplyoee table with the new ID
// created by the first insert
string insertStmt = #"BEGIN TRANSACTION
INSERT INTO dbo.Person(FirstName, Surname) VALUES(#FirstName, #Surname);
DECLARE #NewPersonId INT = SCOPE_IDENTITY();
INSERT INTO dbo.Employee(EmployeeId, HoursWorked) VALUES(#NewPersonId, #HoursWorked);
COMMIT TRANSACTION;"
// define connection and command for inserting data
using (SqlConnection conn = new SqlConnection(-your-connection-string-here-))
using (SqlCommand cmdInsert = new SqlCommand(insertStmt, conn))
{
// Define parameters - adapt datatype and max length as required
cmdInsert.Parameters.Add("#FirstName", SqlDbType.VarChar, 100);
cmdInsert.Parameters.Add("#Surname", SqlDbType.VarChar, 100);
cmdInsert.Parameters.Add("#HoursWorked", SqlDbType.Int);
// set parameter values
cmdInsert.Parameters["#FirstName"].Value = "John";
cmdInsert.Parameters["#Surname"].Value = "Doe";
cmdInsert.Parameters["#HoursWorked"].Value = 35;
// Open connection, execute query, close connection
conn.Open();
int rowsInserted = cmdInsert.ExecuteNonQuery();
conn.Close();
}
Update: as mentioned by #charlieface, you can write this code more concisely IF you're only ever inserting a single row - like this:
// define connection and command for inserting data
using (SqlConnection conn = new SqlConnection(-your-connection-string-here-))
using (SqlCommand cmdInsert = new SqlCommand(insertStmt, conn))
{
// Define and set parameters
cmdInsert.Parameters.Add("#FirstName", SqlDbType.VarChar, 100).Value = "John";
cmdInsert.Parameters.Add("#Surname", SqlDbType.VarChar, 100).Value = "Doe";
cmdInsert.Parameters.Add("#HoursWorked", SqlDbType.Int).Value = 35;
// Open connection, execute query, close connection
conn.Open();
int rowsInserted = cmdInsert.ExecuteNonQuery();
conn.Close();
}
You need to specify the columns you are inserting into.
You should also use SET XACT_ABORT ON if you have an explicit transaction.
Note also the use of a multi-line string.
string queryString = #"
SET XACT_ABORT ON;
BEGIN TRANSACTION;
INSERT INTO Person (FirstName, Surname)
VALUES(#firstName, #surname);
DECLARE #DataID int = scope_identity();
INSERT INTO Employee (EmployeeId, HoursWorked)
VALUES(#DataId, #hoursWorked);
COMMIT;
";

Returning Unique IDs from Oracle Update statement in VB.NET

I am trying to update a selection of rows in an Oracle table we are using to handle messages. Because this table is busy, it would be best if the update could return the UniqueIDs of the rows it updated in an atomic transaction. I modified a code sample I found on StackOverflow to look like the following, but when I examine the parameter "p", I don't find any information coming back from the update statement, as I expected.
Any suggestions for modifying either the .NET code that is setting up the Oracle call, or modifying the Oracle SQL statement itself?
Dim connectString As String = data source=ORA1;user id=MESSAGEBOX;password=MESSAGEBOX
Dim conn As New OracleConnection(connectString)
If conn.State <> ConnectionState.Open Then
conn.Open()
End If
Dim transaction As OracleTransaction = conn.BeginTransaction()
Dim cmd As New OracleCommand()
cmd.Connection = conn
cmd.CommandText = "BEGIN UPDATE MESSAGE_TABLE SET C_WAS_PROCESSED = 2 WHERE C_ID IN (SELECT * FROM(SELECT C_ID FROM MESSAGE_TABLE WHERE C_WAS_PROCESSED = 0 AND C_CREATED_DATE_TIME < CAST(SYSTIMESTAMP AT TIME ZONE 'UTC' AS DATE) ORDER BY C_MESSAGE_PRIORITY, C_ID) WHERE ROWNUM < 16) RETURNING C_ID BULK COLLECT INTO :C_ID; END;"
cmd.CommandType = CommandType.Text
cmd.BindByName = True
cmd.ArrayBindCount = 15
Dim p As New OracleParameter()
p.ParameterName = "C_ID"
p.Direction = ParameterDirection.Output
p.OracleDbType = OracleDbType.Int64
p.Size = 15
p.ArrayBindSize = New Integer() {10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}
p.CollectionType = OracleCollectionType.PLSQLAssociativeArray
cmd.Parameters.Add(p)
Dim nRowsAffected As Integer = cmd.ExecuteNonQuery()
transaction.Commit()
conn.Close()
conn.Dispose()
I believe that problem here is that SQL variable being RETURNED BULK COLLECT INTO needs to be of a TABLE type, not a simple number type.
Alter your SQL to include a declaration block like follows:
cmd.CommandText = "DECLARE TYPE IDS IS TABLE OF MESSAGE_TABLE.C_ID%TYPE; C_ID IDS;
BEGIN UPDATE MESSAGE_TABLE SET C_WAS_PROCESSED = 2 WHERE C_ID IN (SELECT * FROM(SELECT C_ID FROM MESSAGE_TABLE WHERE C_WAS_PROCESSED = 0 AND C_CREATED_DATE_TIME < CAST(SYSTIMESTAMP AT TIME ZONE 'UTC' AS DATE) ORDER BY C_MESSAGE_PRIORITY, C_ID) WHERE ROWNUM < 16) RETURNING C_ID BULK COLLECT INTO :C_ID; END;"
I got the table declaration syntax from this oracle-base.com link.

How to get Scalar-value-function result VB.net form through stored procedure

VB.Net Code
' Getting Records Before Transfer to GL
Call OpenAccConnection(lblUserName.Text, lblPassword.Text)
Dim odcTotalsForTransferGL As OleDbCommand = New OleDbCommand("spPet_TotalsForTransferGL", conAccounts)
odcTotalsForTransferGL.CommandType = CommandType.StoredProcedure
' Parameter Assigning
Dim strCompanyCode As OleDbParameter = odcTotalsForTransferGL.Parameters.Add("#ComCod", OleDbType.VarChar, 2)
strCompanyCode.Direction = ParameterDirection.Input
Dim strLocationCode As OleDbParameter = odcTotalsForTransferGL.Parameters.Add("#LocCod", OleDbType.VarChar, 2)
strLocationCode.Direction = ParameterDirection.Input
Dim strPettyCashDate As OleDbParameter = odcTotalsForTransferGL.Parameters.Add("#PetDat", OleDbType.VarChar, 8)
strPettyCashDate.Direction = ParameterDirection.Input
Dim strBegVNo As OleDbParameter = odcTotalsForTransferGL.Parameters.Add("#BegVNo", OleDbType.Integer)
strBegVNo.Direction = ParameterDirection.Output
Dim strEndVNo As OleDbParameter = odcTotalsForTransferGL.Parameters.Add("#EndVNo", OleDbType.Integer)
strEndVNo.Direction = ParameterDirection.Output
Dim strVouTotal As OleDbParameter = odcTotalsForTransferGL.Parameters.Add("#VouTotal", OleDbType.Integer)
strVouTotal.Direction = ParameterDirection.Output
Dim decPetTotal As OleDbParameter = odcTotalsForTransferGL.Parameters.Add("#PetTotal", OleDbType.Decimal)
decPetTotal.Direction = ParameterDirection.Output
Dim intFinancialDates As OleDbParameter = odcTotalsForTransferGL.Parameters.Add("#FinancialDates", OleDbType.Integer)
intFinancialDates.Direction = ParameterDirection.Output
' Passing Parameters
' Company Code
strCompanyCode.Value = cboCompanyCode.SelectedItem.ToString.Substring(0, 2)
' Location Code
strLocationCode.Value = cboLocationCode.SelectedItem.ToString.Substring(0, 2)
' Petty Cash Date(Year & Month)
strPettyCashDate.Value = dtPettyCashDate.Value.Year.ToString + dtPettyCashDate.Value.Month.ToString("D2") + "01"
' Accounts Database Open
conAccounts.Open()
' Stored Procedure Process
Dim odrTotalsForTransferGL As OleDbDataReader = odcTotalsForTransferGL.ExecuteReader()
If odrTotalsForTransferGL.HasRows Then
Do While odrTotalsForTransferGL.Read
lblAccPeriod.Text = odrTotalsForTransferGL.GetValue(4).ToString.Substring(0, 4) + "/" + odrTotalsForTransferGL.GetValue(4).ToString.Substring(5, 4)
lblFiscalMonth.Text = odrTotalsForTransferGL.GetValue(4).ToString.Substring(9, 2)
lblBegVNo.Text = odrTotalsForTransferGL.GetValue(0).ToString
lblEndVNo.Text = odrTotalsForTransferGL.GetValue(1).ToString
lblPettyTotal.Text = odrTotalsForTransferGL.GetValue(3).ToString
Loop
End If
Stored Procedure
ALTER PROCEDURE [dbo].[spPet_TotalsForTransferGL]
-- Add the parameters for the stored procedure here
#ComCod as varchar(2),
#LocCod as varchar(2),
#PetDat as varchar(8),
#BegVNo as int OUT,
#EndVNo as int OUT,
#VouTotal as int OUT,
#PetTotal as decimal(12,2) OUT,
#FinancialDates as varchar(10) OUT
AS
BEGIN
SELECT MIN(PettyDetail.DPetVouNo),
MAX(PettyDetail.DPetVouNo),
MAX(PettyDetail.DPetVouNo) - MIN(PettyDetail.DPetVouNo),
ISNULL(SUM(PettyDetail.DPetAmount), 0)
FROM PettyDetail
WHERE (PettyDetail.DPetComCode = #ComCod) AND
(PettyDetail.DPetLocCode = #LocCod) AND
(YEAR(PettyDetail.DPetDate) = YEAR(CONVERT(Date,#PetDat,111))) AND
(MONTH(PettyDetail.DPetDate) = MONTH(CONVERT(Date,#PetDat,111)))
/* Getting Financial Dates */
EXECUTE #FinancialDates = dbo.fnApp_GetFinancialDates #PetDat
END
Scalar Function
ALTER FUNCTION [dbo].[fnApp_GetFinancialDates]
(
-- Add the parameters for the function here
#PetDat as varchar(8)
)
--RETURNS int(10)
RETURNS varchar(10)
AS
BEGIN
-- Declare the return variable here
--DECLARE #FinancialDates int(10)
DECLARE #FinancialDates varchar(10)
-- Add the T-SQL statements to compute the return value here
IF MONTH(CONVERT(date,#PetDat,111)) BETWEEN 4 AND 12
BEGIN
SELECT #FinancialDates = (SELECT
CAST((YEAR(CONVERT(date,#PetDat,111))) as varchar) +
CAST((YEAR(CONVERT(date,#PetDat,111)) + 1) as varchar) +
REPLICATE('0',(2-(LEN(CAST((MONTH(CONVERT(date,#PetDat,111)) - 3) as varchar))))) + (CAST((MONTH(CONVERT(date,#PetDat,111)) - 3) as varchar)))
END
ELSE
BEGIN
SELECT #FinancialDates = (SELECT
CAST((YEAR(CONVERT(date,#PetDat,111)) - 1)as varchar) +
CAST((YEAR(CONVERT(date,#PetDat,111))) as varchar) +
CAST((MONTH(CONVERT(date,#PetDat,111)) + 9) as varchar))
END
-- Return the result of the function
RETURN #FinancialDates
END
Above function #FinancialDates value didn't return to the .Net form. But other results are return to form.
Can any one please help me to solve this problem. Procedure & Function run correctly in Query Manager.
Two options :
Option 1 : change EXECUTE #FinancialDates = dbo.fnApp_GetFinancialDates #PetDat,
to :
SET #FinancialDates = dbo.fnApp_GetFinancialDates(#PetDat)
Option 2 : Include your fnApp_GetFinancialDates function in your SELECT statement (and you could just remove the #FinancialDates as varchar(10) OUT parameter statement.
ALTER PROCEDURE [dbo].[spPet_TotalsForTransferGL]
-- Add the parameters for the stored procedure here
#ComCod as varchar(2),
#LocCod as varchar(2),
#PetDat as varchar(8)
AS
BEGIN
SELECT MIN(PettyDetail.DPetVouNo) AS 'BegVNo',
MAX(PettyDetail.DPetVouNo) AS 'EndVNo',
MAX(PettyDetail.DPetVouNo) - MIN(PettyDetail.DPetVouNo) AS 'VouTotal',
ISNULL(SUM(PettyDetail.DPetAmount), 0) AS 'PetTotal'
dbo.fnApp_GetFinancialDates (#PetDat) AS 'FinancialDates'
FROM PettyDetail
WHERE (PettyDetail.DPetComCode = #ComCod) AND
(PettyDetail.DPetLocCode = #LocCod) AND
(YEAR(PettyDetail.DPetDate) = YEAR(CONVERT(Date,#PetDat,111))) AND
(MONTH(PettyDetail.DPetDate) = MONTH(CONVERT(Date,#PetDat,111)))
END
and for the option 2 VB code :
If odrTotalsForTransferGL.HasRows Then
Do While odrTotalsForTransferGL.Read
lblAccPeriod.Text = odrTotalsForTransferGL("FinancialDates").ToString.Substring(0, 4) + "/" + odrTotalsForTransferGL("FinancialDates").ToString.Substring(5, 4)
lblFiscalMonth.Text = odrTotalsForTransferGL("FinancialDates").ToString.Substring(9, 2)
lblBegVNo.Text = odrTotalsForTransferGL("BegVNo").ToString
lblEndVNo.Text = odrTotalsForTransferGL("EndVNo").ToString
lblPettyTotal.Text = odrTotalsForTransferGL("PetTotal").ToString
Loop
End If
edited : and don't forget to remove the following code because they are not necessary and will produce errors since the stored proc has no output parameters anymore:
Dim strBegVNo As OleDbParameter =
odcTotalsForTransferGL.Parameters.Add("#BegVNo", OleDbType.Integer)
strBegVNo.Direction = ParameterDirection.Output
Dim strEndVNo As OleDbParameter =
odcTotalsForTransferGL.Parameters.Add("#EndVNo", OleDbType.Integer)
strEndVNo.Direction = ParameterDirection.Output
Dim strVouTotal As OleDbParameter =
odcTotalsForTransferGL.Parameters.Add("#VouTotal", OleDbType.Integer)
strVouTotal.Direction = ParameterDirection.Output
Dim decPetTotal As OleDbParameter =
odcTotalsForTransferGL.Parameters.Add("#PetTotal", OleDbType.Decimal)
decPetTotal.Direction = ParameterDirection.Output
Dim intFinancialDates As OleDbParameter =
odcTotalsForTransferGL.Parameters.Add("#FinancialDates", OleDbType.Integer)
intFinancialDates.Direction = ParameterDirection.Output

"Procedure or function 'UPDATE' expects parameter '#Id', which was not supplied" in Windows Form

We created a Windows Form to update a table in SQL Server.
First I click Enter ID to retrieve details from database, then after changing some data, when I click on Update button, I get an error:
Procedure or function 'UPDATE' expects parameter '#Id', which was not supplied.
Windows Form Design :
Click here
Error :
Click here
Code for Windows Form:
public partial class Update : Form
{
string connectionString = #"Data Source=AMAR;Initial Catalog=Hotel;Integrated Security=True";
public Update()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
{
TestObject t = null;
string spName = "Get";
//string queryText = "Select * from TestTable where Id = " +txtId.Text;
SqlConnection conn = new SqlConnection(connectionString);
//SqlCommand com = new SqlCommand(spName, conn);
SqlCommand com = new SqlCommand(spName, conn);
com.Parameters.AddWithValue("#Id", ID.Text);
com.CommandType = CommandType.StoredProcedure;
conn.Open();
using (SqlDataReader reader = com.ExecuteReader())
{
t = new TestObject();
while (reader.Read())
{
t.Id = reader["ID"].ToString();
t.Status = reader["Status"].ToString();
t.FName = reader["FirstName"].ToString();
t.LName = reader["LastName"].ToString();
t.Addr = reader["Address"].ToString();
t.City = reader["City"].ToString();
t.State = reader["State"].ToString();
t.Country = reader["Country"].ToString();
t.PhoneNo = reader["PhoneNo"].ToString();
t.Email = reader["EmailId"].ToString();
t.Pin = reader["Pincode"].ToString();
t.CheckIn = reader["CheckIn"].ToString();
t.CheckOut = reader["CheckOut"].ToString();
t.AdultNo = reader["AdultNo"].ToString();
t.ChildNo = reader["InfantNo"].ToString();
t.InfantNo = reader["InfantNo"].ToString();
t.RoomNo = reader["RoomNo"].ToString();
};
}
Statustxt.Text = t.Status;
txtfName.Text = t.FName;
txtlName.Text = t.LName;
txtAddr.Text = t.Addr;
City.Text = t.City;
State.Text = t.State;
Country.Text = t.Country;
PhoneNo.Text = t.PhoneNo;
EmailID.Text = t.Email;
Pincode.Text = t.Pin;
CheckIN.Text = t.CheckIn;
CheckOut.Text = t.CheckOut;
Adult.Text = t.AdultNo;
Child.Text = t.ChildNo;
Infant.Text = t.InfantNo;
RoomNo.Text = t.RoomNo;
}
}
private void btnUpdate_Click(object sender, EventArgs e)
{
string Stat = Statustxt.Text;
string FirstName = txtfName.Text;
string LastName = txtlName.Text;
string Address=txtAddr.Text;
string Cities=City.Text;
string States= State.Text;
string Countries =Country.Text;
string PhoneNos= PhoneNo.Text;;
string EmailId= EmailID.Text;
string PinCode=Pincode.Text;
string CIn=CheckIN.Text;
string COut=CheckOut.Text;
string AdultNo=Adult.Text;
string ChildNo=Child.Text;
string InfantNo=Infant.Text;
string RoomNos=RoomNo.Text;
TestObject obj = new TestObject();
obj.Stat=Statustxt.Text;
obj.FirstName = txtfName.Text;
obj.LastName = txtlName.Text;
obj.Address=txtAddr.Text;
obj.Cities=City.Text;
obj.States= State.Text;
obj.Countries =Country.Text;
obj.PhoneNos= PhoneNo.Text;;
obj.EmailId= EmailID.Text;
obj.PinCode=Pincode.Text;
obj.CIn=CheckIN.Text;
obj.COut=CheckOut.Text;
obj.AdultNo=Adult.Text;
obj.ChildNo=Child.Text;
obj.InfantNo=Infant.Text;
obj.RoomNos=RoomNo.Text;
string spName = "UPDATE";
SqlConnection conn = new SqlConnection(connectionString);
SqlCommand com = new SqlCommand(spName, conn);
conn.Open();
com.Parameters.AddWithValue("#Stat", obj.Stat);
com.Parameters.AddWithValue("#FirstName", obj.FirstName);
com.Parameters.AddWithValue("#LastName", obj.LastName);
com.Parameters.AddWithValue("#Address", obj.Address);
com.Parameters.AddWithValue("#Cities", obj.Cities);
com.Parameters.AddWithValue("#States", obj.States);
com.Parameters.AddWithValue("#Countries", obj.Countries);
com.Parameters.AddWithValue("#PhoneNos", obj.PhoneNos);
com.Parameters.AddWithValue("#EmailId", obj.EmailId);
com.Parameters.AddWithValue("#PinCode", obj.PinCode);
com.Parameters.AddWithValue("#CIn", obj.CIn);
com.Parameters.AddWithValue("#COut", obj.COut);
com.Parameters.AddWithValue("#AdultNo", obj.AdultNo);
com.Parameters.AddWithValue("#ChildNo", obj.ChildNo);
com.Parameters.AddWithValue("#InfantNo", obj.InfantNo);
com.Parameters.AddWithValue("#RoomNos", obj.RoomNos);
com.CommandType = CommandType.StoredProcedure;
com.ExecuteNonQuery();
conn.Close();
MessageBox.Show("Customer Details updated in system");
}
}
SQL Server stored procedure:
ALTER PROCEDURE [dbo].[UPDATE]
#Id int,
#Stat nvarchar(100),
#FirstName nvarchar(100),
#LastName nvarchar(100),
#Address nvarchar(100),
#Cities nvarchar(100),
#States nvarchar(100),
#Countries nvarchar(100),
#PhoneNos int,
#EmailId nvarchar(100),
#PinCode int,
#CIn nvarchar(100),
#COut nvarchar(100),
#AdultNo int,
#ChildNo int,
#InfantNo int,
#RoomNos int
AS
BEGIN
SET NOCOUNT ON;
-- Insert statements for procedure here
UPDATE [Hotel].[dbo].[Details] SET
[Status] = #Stat,
[FirstName] = #FirstName,
[LastName] = #LastName,
[Address] = #Address,
[City] = #Cities,
[State] =#States ,
[Country] = #Countries,
[PhoneNo] = #PhoneNos,
[EmailId] = #EmailId,
[Pincode] = #PinCode,
[CheckIn] = #CIn,
[CheckOut] = #COut,
[AdultNo] = #AdultNo,
[ChildNo] = #ChildNo,
[InfantNo] = #InfantNo,
[RoomNo] = #RoomNos
WHERE ID = #Id
END
a. as Mitch Wheat wrote in the comments, NEVER use keywords as procedures names.
b. as marc_s wrote in his comment - stop using .AddWithValue(). read the article he links to.
c. you never provide the #id parameter to the command , this is why you get the error.
d. this has nothing to do with winforms.
e. in the future, Please provide only the relevant code. if the problem is in the update button click, we don't need to see the entire form class, only the button click event handler.

Exception from Stored Procedure not caught in .NET using SqlDataAdapter.Fill(DataTable)

I have a Stored Procedure that I am executing from VB.NET. The SP should insert records into a table and return a set to the calling app. The set returned are the records that were inserted.
If the INSERT fails, the exception is caught and re-thrown in the SP, but I never see the exception in my application. The severity level is 14, so I should see it.
Here is the stored procedure:
BEGIN TRY
BEGIN TRANSACTION
-- Declare local variables
DECLARE #DefaultCategoryID AS BIGINT = 1 -- 1 = 'Default Category' (which means no category)
DECLARE #DefaultWeight AS DECIMAL(18,6) = 0
DECLARE #InsertionDate AS DATETIME2(7) = GETDATE()
DECLARE #SendToWebsite AS BIT = 0 -- 0 = 'NO'
DECLARE #MagentoPartTypeID AS BIGINT = 1 -- For now, this is the only part type we are importing from COPICS ('simple' part type)
DECLARE #NotUploaded_PartStatusID AS TINYINT = 0 -- 0 = 'Not Uploaded'
DECLARE #Enabled_PartStatusID AS TINYINT = 1 -- 1 = 'Enabled'
DECLARE #Disabled_PartStatusID AS TINYINT = 2 -- 2 = 'Disabled'
-- Get the part numbers that will be inserted (this set will be returned to calling procedure).
SELECT c.PartNumber
FROM
COPICSPartFile c
LEFT JOIN Part p on c.PartNumber = p.PartNumber
WHERE
p.PartNumber IS NULL
-- Insert new records from COPICSPartFile (records that don't exist - by PartNumber - in Part table)
INSERT INTO Part
([PartNumber]
,[ReplacementPartNumber]
,[ShortDescription]
,[ListPrice]
,[PartStatusTypeID]
,[Weight]
,[CategoryID]
,[DateInserted]
,[SendToWebsite]
,[FileName]
,[MagentoPartTypeID]
,[PrintNumber])
SELECT
c.PartNumber
,c.ReplacementPartNumber
,c.ShortDescription
,c.ListPrice
,CASE WHEN c.PartStatusTypeID = #Enabled_PartStatusID THEN #NotUploaded_PartStatusID ELSE #Disabled_PartStatusID END
,#DefaultWeight
,#DefaultCategoryID
,#InsertionDate
,#SendToWebsite
,#FileName
,#MagentoPartTypeID
,c.PrintNumber
FROM
COPICSPartFile c
LEFT JOIN Part p on c.PartNumber = p.PartNumber
WHERE
p.PartNumber IS NULL
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION;
THROW;
END CATCH
And here is the .net code:
Try
'Create command
Dim command As New SqlCommand
command.CommandType = CommandType.StoredProcedure
conn = New SqlConnection(m_ConnectionString)
command.Connection = conn
command.CommandText = "trxInsertPartFromCOPICSPartFile"
With command.Parameters
.AddWithValue("#FileName", fileName)
End With
Dim da As New SqlDataAdapter(command)
Dim dt As New DataTable
da.Fill(dt)
If dt.Rows.Count > 0 Then
Return dt
Else
Return Nothing
End If
Catch ex As SqlException
Dim myMessage As String = ex.Message
Finally
If conn.State <> ConnectionState.Closed Then
conn.Close()
End If
End Try
As I was trying to figure out why the exception (duplicate key) wasn't being caught in my application, I tried commenting out the SELECT statement in the SP just before the INSERT and voila. The exception from the INSERT is caught in the application.
Can someone explain to me why the SELECT statement causes this? I know I can break out the SELECT into another SP, but I'd like to keep it all one atomic transaction if possible. Is this expected behavior? Is there a way around it?
Thanks.
The exception is being swallowed by the Fill method. Instead of using that method, create a SqlDataReader, do a command.ExecuteReader(), and then use the reader to populate the DataTable via Load(). This way the error should occur in the ExecuteReader() method and should be catchable. And then you shouldn't need the SqlDataAdapter.
Try
'Create command
Dim command As New SqlCommand
command.CommandType = CommandType.StoredProcedure
conn = New SqlConnection(m_ConnectionString)
command.Connection = conn
command.CommandText = "trxInsertPartFromCOPICSPartFile"
With command.Parameters
.AddWithValue("#FileName", fileName)
End With
Dim dt As New DataTable
conn.Open()
Dim reader As SqlDataReader = command.ExecuteReader()
dt.Load(reader)
If dt.Rows.Count > 0 Then
Return dt
Else
Return Nothing
End If
Catch ex As SqlException
Dim myMessage As String = ex.Message
Finally
If conn.State <> ConnectionState.Closed Then
conn.Close()
End If
End Try
Also, you might be better off on several levels if you combine the SELECT and the INSERT into a single statement. You can do this via the OUTPUT clause, as follows:
INSERT INTO Part
([PartNumber]
,[ReplacementPartNumber]
,[ShortDescription]
,[ListPrice]
,[PartStatusTypeID]
,[Weight]
,[CategoryID]
,[DateInserted]
,[SendToWebsite]
,[FileName]
,[MagentoPartTypeID]
,[PrintNumber])
OUTPUT INSERTED.[PartNumber] -- return the inserted values to the app code
SELECT
c.PartNumber
,c.ReplacementPartNumber
,c.ShortDescription
,c.ListPrice
,CASE WHEN c.PartStatusTypeID = #Enabled_PartStatusID
THEN #NotUploaded_PartStatusID
ELSE #Disabled_PartStatusID END
,#DefaultWeight
,#DefaultCategoryID
,#InsertionDate
,#SendToWebsite
,#FileName
,#MagentoPartTypeID
,c.PrintNumber
FROM
COPICSPartFile c
LEFT JOIN Part p on c.PartNumber = p.PartNumber
WHERE
p.PartNumber IS NULL