I have a C# console program
Selects 10000 records from Input table (Keyid varchar(4), Address varchar(100), Name varchar(100)).
for each record from Input table, it calls an API that returns data (if person works in that address, status is OK else NOT OK, also returns address type-place of interest or residential, commercial etc.) that needs to be saved in one main table and a detail table.
Main table:
1001|JOE STILTON| 2 MAIN ST, SALEM,PA| OK|4/15/2014
Detail table:
1001|PHARMACY
1001|COMMERCIAL
i.e Joe works in a pharmacy which is also a commercial bldg.
Right now, I call the API. then I call a method,
private static void insertTable(string keyid, DateTime updDate, string name, string address,string status)
{
Int32 rowsAffected = 0;
string connectionString = GetConnectionString();
SqlConnection connection = new SqlConnection(connectionString);
connection.Open();
SqlCommand cmd = new SqlCommand("google.usp_InsertCompanyAddrComponents", connection);
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandTimeout = 90;
cmd.Parameters.Add("#keyid", SqlDbType.VarChar);
cmd.Parameters["#keyid"].Value = keyid;
cmd.Parameters.Add( new SqlParameter("#dateverified", updDate));
cmd.Parameters.Add("#name", SqlDbType.VarChar);
cmd.Parameters["#name"].Value = name;
cmd.Parameters.Add("#address", SqlDbType.VarChar);
cmd.Parameters["#address"].Value = address;
cmd.Parameters.Add( new SqlParameter("#status", status));
try
{
rowsAffected = cmd.ExecuteNonQuery();
}
catch (Exception ep)
{
Console.WriteLine(ep.Message);
}
connection.Close();
}
Then, I call another similar method that inserts into detail table.
Since I have to do this for 10,000 records at a time, there is lot of I/O. how can I change to do batch insert? all 10000 insert at a time?
Thanks
R
You can also take a look into table types in SQL Server. You can pass two table types in stored procedure and do required operations directly over there.
Here is my sample stored proc
CREATE PROCEDURE [dbo].[usp_AssociateTags]
#Tags AS UDT_Tag READONLY
AS
SET XACT_ABORT ON
BEGIN TRAN
--Insert into Tag Master
INSERT INTO dbo.TagMaster
(
Name
,IsActive
)
VALUES ( '', -- Name - varchar(50)
1 -- IsActive - bit
)
DECLARE #TagId AS INT
SET #TagId=SCOPE_IDENTITY()
INSERT INTO dbo.TagCollection
( TagNumber, TagId )
SELECT TagNumber, #TagId FROM #Tags t
WHERE NOT EXISTS(SELECT * FROM dbo.TagCollection WHERE TagNumber = t.TagNumber)
COMMIT TRAN
SET XACT_ABORT OFF
Script to test this stored procedure
--DECLARE #hello as UDT_Tag
--INSERT INTO #hello VALUES('vaibhav')
--INSERT INTO #hello VALUES('Shantanu')
--INSERT INTO #hello VALUES('Sam')
--INSERT INTO #hello VALUES('Aakash')
--EXEC usp_AssociateTags #hello
--SELECT * FROM dbo.TagCollection
C# code to consume this procedure
SqlParameter Tags = new SqlParameter { ParameterName = "#Tags"
, Value = entity.Tags.ToDataTable()
, Direction = ParameterDirection.Input
, SqlDbType = SqlDbType.Structured, TypeName="UDT_Tag" };
SqlHelper.ExecuteNonQuery(tran, CommandType.StoredProcedure
, "usp_AssociateTags", Tags);
CodeProject
Related
I am using Npgsql 3.0.3 with Postgres 9.4. Here is my code in Postgres:
CREATE TABLE temp_test
(
id serial NOT NULL,
name text,
CONSTRAINT temp_test_pk PRIMARY KEY (id)
)
and the "Upsert" / merge function that returns the changed record as refcursor:
CREATE OR REPLACE FUNCTION test_save(
v_ref refcursor,
iv_id integer,
v_name character varying)
RETURNS refcursor AS
$BODY$
DECLARE
v_ref alias for $1;
v_id integer := iv_id;
BEGIN
UPDATE onepm.temp_test
SET name = v_name
WHERE id = v_id;
IF NOT FOUND THEN
INSERT INTO onepm.temp_test
(name)
VALUES
(v_name)
RETURNING id INTO v_id;
END IF;
OPEN v_ref FOR
SELECT id
, name
FROM onepm.temp_test
WHERE id = v_id;
RETURN v_ref;
END;
$BODY$
LANGUAGE plpgsql;
In my .net project I have the following function that returns a IDatareader:
public static IDataReader ExecuteReader()
{
NpgsqlConnection conn = new NpgsqlConnection(connectionString);
conn.Open();
NpgsqlTransaction _tran = conn.BeginTransaction();
NpgsqlCommand cmd = conn.CreateCommand();
cmd.CommandType = CommandType.Text;
cmd.CommandText = "SELECT onepm.test_save(#ref, #id, #name)";
NpgsqlParameter _p = new NpgsqlParameter();
_p.ParameterName = "#ref";
_p.NpgsqlDbType = NpgsqlDbType.Refcursor;
_p.NpgsqlValue = "ref";
_p.Direction = ParameterDirection.InputOutput;
cmd.Parameters.Add(_p);
cmd.Parameters.Add(new NpgsqlParameter("#id", 1));
cmd.Parameters.Add(new NpgsqlParameter("#name", "test"));
cmd.ExecuteNonQuery();
cmd.CommandText = "fetch all in \"ref\"";
cmd.CommandType = CommandType.Text;
return cmd.ExecuteReader();
}
This all works fine, I do receice the inserted or updated record in the reader, except that the data is never committed to the table - no data found in pgAdmin. If I call the same function in pgAdmin everything works fine - records are committed:
SELECT onepm.test_save('v_ref', 1, 'xxxxxx');
FETCH ALL IN "v_ref";
Thankful for any help!
Ummm, I think you need to commit the transaction you started...!
Regardless, you may also want to look at PostgreSQL 9.5's new built-in upsert functionality...
MySql Procedure Code:
CREATE DEFINER=`root`#`localhost` PROCEDURE `USP_CreateCliente`(IN nome_cliente VARCHAR(45))
BEGIN
Select 20;
INSERT INTO clienti ( nome_cliente )
VALUES ( nome_cliente );
Select id_cliente from clienti;
END
C# code in the controller page:
ClienteInfo CI = new ClienteInfo();
DboUser objdbo = new DboUser();
int id_cliente = 0;
CI.nome_cliente = txtNomeCliente.Text;
id_cliente = objdbo.CreateClienteInfo(CI);
DboUser class:
public int CreateClienteInfo(ClienteInfo CI)
{
int result;
MySqlConnection conn = new MySqlConnection();
DbConnection db = new DbConnection();
conn = db.ConnessioneDb();
MySqlCommand cmd = new MySqlCommand(Costanti.StoredProcedures.USP_CreateCliente, conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#nome_cliente", CI.nome_cliente);
result = cmd.ExecuteNonQuery();
conn.Close();
return result;
}
I want my C# code to retrieve the id of my customer after inserting it into the database, so I can save it to the session and retrieve it again later in a page that will print a document with the customer's informations.
My id_cliente returns 0, do you notice any syntax error?
Did I do something wrong?
I'm 90% sure it's a problem dealing with the stored precedure tough, cause the customer is inserted correctly
Change this line
result = cmd.ExecuteNonQuery();
to
result = Convert.ToInt32(cmd.ExecuteScalar());
but you should also change your stored procedure because it doesn't return the last id generated for you by the AUTO_INCREMENT column id_cliente
CREATE DEFINER=`root`#`localhost` PROCEDURE `USP_CreateCliente`(IN nome_cliente VARCHAR(45))
BEGIN
INSERT INTO clienti ( nome_cliente ) VALUES ( nome_cliente );
Select LAST_INSERT_ID();
END
In MySql, to get the generated auto_increment value, you could use LAST_INSERT_ID(), next, your C# code don't need to use ExecuteNonQuery, which returns just the number of rows that you have changed, added or deleted, but you use ExecuteScalar which returns the first column of the first row of the last SELECT command executed by your stored procedure. (It is the SELECT LAST_INSERT_ID())
Also, to complete the answer, you don't really need a stored procedure for this kind of simple work. The advantages should be minimal while the problems related to a different piece of software to maintain are self evident.
Your C# code could be (removed the usage of your DbConnection class because it is not clear what it does)
public int CreateClienteInfo(ClienteInfo CI)
{
int result;
string cmdText = #"INSERT INTO clienti ( nome_cliente ) VALUES ( nome_cliente );
Select LAST_INSERT_ID();";
using(MySqlConnection conn = new MySqlConnection(....connectionstring .....))
using(MySqlCommand cmd = new MySqlCommand(cmdText, conn);
{
conn.Open()
cmd.Parameters.AddWithValue("#nome_cliente", CI.nome_cliente);
result = Convert.ToInt32(cmd.ExecuteScalar())
return result;
}
}
Here you use the possibility to pass batch commands to your MySql engine, meaning two commandtexts with the same MySqlCommand separating them with the semicolon
So I'm trying to learn how to use stored procedures and for the most part I somewhat understand how to use them except for this merge procedure.
What I'm trying to do is use the MERGE procedure to Insert if a row does not already exist and return the ID ELSE IF NOT EXISTS then just return the ID.
Here is currently what I have so far.
CREATE PROCEDURE dbo.authors_InsertOrUpdate
-- Add the parameters for the stored procedure here
#FirstName nvarchar(50),
#LastName nvarchar(50),
#id int = NULL OUTPUT
AS
MERGE Authors AS target
USING (SELECT #FirstName, #LastName) AS source (FirstName, LastName)
ON target.FirstName = source.FirstName AND target.LastName = source.LastName
WHEN MATCHED THEN
UPDATE
SET #id = SCOPE_IDENTITY()
WHEN NOT MATCHED THEN INSERT
(FirstName, LastName) VALUES (source.FirstName, source.LastName);
SET #id = SCOPE_IDENTITY()
Everything works fine as far as inputting the values/information into the database and finding an existing one but I'm not getting my ID value back correctly. Here is my method that uses it..
private static int storeAuthors(string firstName, string lastName)
{
String commandText = "dbo.authors_InsertOrUpdate";
using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["AmazonCrawler.Properties.Settings.database"].ToString()))
{
using (SqlCommand command = new SqlCommand(commandText, connection))
{
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add(new SqlParameter("#FirstName", firstName));
command.Parameters.Add(new SqlParameter("#LastName", lastName));
SqlParameter authorId = new SqlParameter("#id", SqlDbType.Int);
authorId.Direction = ParameterDirection.Output;
command.Parameters.Add(authorId);
connection.Open();
command.ExecuteNonQuery();
connection.Close();
return Convert.ToInt32(authorId.Value);
}
}
}
Please help me fix this!
Thanks in advance.
---------EDIT------------
Updated the procedure to this yet I'm still receiving a null value in my code when i try to return the value
CREATE PROCEDURE dbo.authors_InsertOrUpdate
-- Add the parameters for the stored procedure here
#FirstName nvarchar(50),
#LastName nvarchar(50),
#id int OUTPUT
AS
MERGE Authors AS target
USING (SELECT #id, #FirstName, #LastName) AS source (id, FirstName, LastName)
ON target.FirstName = source.FirstName AND target.LastName = source.LastName
WHEN NOT MATCHED THEN
INSERT (FirstName, LastName) VALUES (source.FirstName, source.LastName);
SET #id = SCOPE_IDENTITY()
Edit reference 1
Edit reference 2
Edit reference 3
ALTER PROCEDURE dbo.authors_InsertOrUpdate
-- Add the parameters for the stored procedure here
#FirstName nvarchar(50),
#LastName nvarchar(50),
#id int OUTPUT
AS
MERGE Authors AS target
USING (SELECT #id, #FirstName, #LastName) AS source (ID, FirstName, LastName)
ON target.FirstName = source.FirstName AND target.LastName = source.LastName
WHEN MATCHED THEN
UPDATE SET #id = target.ID
WHEN NOT MATCHED THEN
INSERT (FirstName, LastName) VALUES (source.FirstName, source.LastName);
SET #id = SCOPE_IDENTITY()
SELECT #id
Still receiving same error listed in reference 2.
=( no solution yet.
here is my passed in values with a return of zero as an id
and here is my database
Edit reference 4
Tried changing my parameters when putting in the values to this as I thought perhaps closing it before getting the value was causing the problem but no luck
private static int storeAuthors(string firstName, string lastName)
{
String commandText = "dbo.authors_InsertOrUpdate";
using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["AmazonCrawler.Properties.Settings.database"].ToString()))
{
using (SqlCommand command = new SqlCommand(commandText, connection))
{
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add(new SqlParameter("#FirstName", firstName));
command.Parameters.Add(new SqlParameter("#LastName", lastName));
SqlParameter authorId = new SqlParameter("#id", SqlDbType.Int);
authorId.Direction = ParameterDirection.Output;
command.Parameters.Add(authorId);
connection.Open();
command.ExecuteNonQuery();
int value = Convert.ToInt32(authorId.Value);
connection.Close();
return value;
}
}
}
In the UPDATE case, you aren't generating a new IDENTITY, you are just querying, so SCOPE_IDENTITY() won't return the right value. In the UPDATE case, SCOPE_IDENTITY() may appear to work, at first, but it is actually returning the value from the last INSERT in scope (I think), which could be the last execution of the stored proc.
I recommend using the OUTPUT clause. I tried to make something work with your approach, and with #usr's suggestion as well, but the only thing I could make work was to use the OUTPUT clause. See below:
CREATE PROCEDURE dbo.authors_InsertOrUpdate
-- Add the parameters for the stored procedure here
#FirstName nvarchar(50),
#LastName nvarchar(50),
#id int OUTPUT
AS BEGIN
DECLARE #MergeOutput table
(
ACTION VARCHAR(10),
ID INT
);
MERGE Authors AS target
USING (SELECT #id, #FirstName, #LastName) AS source (id, FirstName, LastName)
ON target.FirstName = source.FirstName AND target.LastName = source.LastName
WHEN NOT MATCHED THEN
INSERT (FirstName, LastName) VALUES (source.FirstName, source.LastName)
WHEN MATCHED THEN UPDATE
SET #Id = target.Id
OUTPUT $action, INSERTED.ID INTO #MergeOutput;
SELECT #Id = Id FROM #MergeOutput
END
GO
DECLARE #id2 INT
exec dbo.authors_InsertOrUpdate 'Melvin', 'Smith', #id = #id2 OUTPUT
SELECT #id2
GO
I tried to make something work using SCOPE_IDENTITY(), and using SET #Id = target.Id as in #usr's answer but I kept getting the last inserted value returned, even for an update.
SELECT * INTO #o FROM sys.objects
DECLARE #id INT
MERGE #o AS target
USING (SELECT OBJECT_ID('tempdb..#o') AS object_id) AS source (object_id)
ON target.object_id = source.object_id
WHEN MATCHED THEN UPDATE SET #id = target.object_id;
SELECT #id
You can use columns of the MERGE query pretty freely. It is a very powerful statement.
I am creating a web application using ASP.net C#. I have a booking form and I need to insert data into a table using a Stored Procedure. The table has several columns, out of which second column is a computed column. The Stored Procedure is set up to insert the data and fetch the value from the second column after insert. Below is the code for Stored Procedure:
Create Procedure sp_InsertCashPooja
#FirstName varchar(100),
#LastName varchar(100),
#TelNo bigint,
#Star char(50),
#Rasi char(50),
#Gothram char(50),
#PDMID int,
#PayMode bit,
#PujaName char(50),
#DonateAmt decimal(19,2),
#RcptNo varchar(25) output
as
Begin
SET NOCOUNT ON;
BEGIN TRY
BEGIN TRANSACTION
if #PujaName != 'DONATION'
Begin
INSERT INTO PoojaDetails (FirstName, LastName, TelNo, Star, Rasi, Gothram, PoojaDietyMasterID, PayMode) values (#FirstName,#LastName,#TelNo,#Star,#Rasi,#Gothram,#PDMID,#PayMode)
End
if #PujaName = 'DONATION'
Begin
DECLARE #isDonate int = 0;
INSERT INTO PoojaDetails (FirstName, LastName, TelNo, Star, Rasi, Gothram, PoojaDietyMasterID, PayMode, isDonate, DonateAmount) values (#FirstName,#LastName,#TelNo,#Star,#Rasi,#Gothram,#PDMID,#PayMode, #isDonate, #DonateAmt)
End
Select #RcptNo = max(ReceiptNo) from PoojaDetails
Return #RcptNo
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF (##TRANCOUNT > 0)
ROLLBACK TRANSACTION
END CATCH
SET NOCOUNT OFF;
End
I would like to insert data on the click of a button: I was able to figure out the below code....
protected void btnSave_Click(object sender, EventArgs e)
{
frmFirstName = txtFirstName.Text.Trim().ToUpper();
frmLastName = txtLastName.Text.Trim().ToUpper();
frmPhoneNo = Convert.ToInt32(txtPhoneNo.Text.Trim());
frmNakshatra = Convert.ToString(cmbNakshatra.SelectedItem).Trim();
frmRasi = Convert.ToString(cmbRasi.SelectedItem).Trim();
frmGothram = Convert.ToString(cmbGothram.SelectedItem).Trim();
frmPujaName = Convert.ToString(cmbPujaName.SelectedItem).Trim();
using (SqlConnection connection = new SqlConnection())
{
if (frmPayMode == "Cash")
{
if (frmPujaName == "DONATION")
{
SqlDataAdapter CashAdapter = new SqlDataAdapter();
CashAdapter.InsertCommand = new SqlCommand("sp_InsertCashPooja", connection);
CashAdapter.InsertCommand.CommandType = CommandType.StoredProcedure;
Please help.... I want to capture the returning RcptNo and later intend to call another ASPX page and pass the value using a Query String.
Thanks
Use simple SqlCommand for calling your SP
connection.Open();
var cmd = new SqlCommand("sp_InsertCashPooja", connection);
cmd.Parameters.AddWithValue("FirstName", frmFirstName);
// Add all the others parameters in same way
var id = (int)cmd.ExecuteScalar();
connection.Close();
Change the return variable to:
Select #RcptNo = SCOPE_IDENTITY()
It will return the identity number created for the inserted record within this procedure.
use sql parameter..
connection = ConfigurationManager.AppSettings["mycon"];
SqlParameter[] para = new SqlParameter[2];
para[0] = new SqlParameter("#stored procedure column name", string name);
para[1] = new SqlParameter("#stored procedure column name", string name);
This is my stored procedure code
ALTER procedure [Proc_Add_User]
(#UserId varchar(20),
#UserName varchar(100),
#Page_Name varchar(20),
#AccessIndicator int,
#CreatedBy varchar(50),
#returnStatus varchar(50) output)
as
DECLARE #intErrorCode INT
DECLARE #Page_Indicator INT
begin
BEGIN TRAN
Set #Page_Indicator = (select Page_Indicator from Pages where Page_Name=#Page_Name);
if (select count(*) from Users where UserId=#UserId and UserName=#UserName) > 0 begin
if (select count(*) from User_Credentials where Page_Indicator=#Page_Indicator and
UserId=#UserId ) > 0
set #returnStatus='User already has access'
else
insert into User_Credentials(UserId,Page_Indicator,Access_Indicator,CreatedBy)
values (#UserId,#Page_Indicator,#AccessIndicator,#CreatedBy)
SELECT #intErrorCode = ##ERROR
IF (#intErrorCode <> 0) GOTO PROBLEM
end
else begin
insert into Users(UserId,UserName,CreatedBy)
values(#UserId,#UserName,#CreatedBy)
SELECT #intErrorCode = ##ERROR
IF (#intErrorCode <> 0) GOTO PROBLEM
insert into User_Credentials(UserId,Page_Indicator,Access_Indicator,CreatedBy)
values (#UserId,#Page_Indicator,#AccessIndicator,#CreatedBy)
SELECT #intErrorCode = ##ERROR
IF (#intErrorCode <> 0) GOTO PROBLEM
end
COMMIT TRAN
if(#returnStatus is null)
set #returnStatus='Success';
PROBLEM:
IF (#intErrorCode <> 0) BEGIN
set #returnStatus= 'Unexpected error occurred!'
ROLLBACK TRAN
end
end
And I am calling this from the code pasted below:
Con.Open();
cmd = new OleDbCommand();
cmd.Connection = Con;
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "Proc_Add_User";
cmd.Parameters.Clear();
cmd.Parameters.Add("#UserId", SqlDbType.VarChar).Value = userLanId;
cmd.Parameters.Add("#UserName", SqlDbType.VarChar).Value = userName;
cmd.Parameters.Add("#Page_Name", SqlDbType.VarChar).Value = pageName;
cmd.Parameters.Add("#AccessIndicator", SqlDbType.Int).Value = accessIndicator;
cmd.Parameters.Add("#CreatedBy", SqlDbType.VarChar).Value = createdBy;
OleDbParameter output = new OleDbParameter("#returnStatus", SqlDbType.VarChar);
output.Direction = ParameterDirection.Output;
cmd.Parameters.Add(output);
int result = cmd.ExecuteNonQuery();
I am getting the error mentioned at the ExecuteNonQuery statement. What's confusing to me is I am able to execute the stored procedure in SSMS but not from my application (front-end). I provided the same values too yet it fails from my app.
I double checked to make sure the order of parameters passed match and are of same data type but still it throws this error. I can paste my stored proc code here if wanted so let me know..Thanks in advance!
EDIT
OOPS! I just realized that all the inserts are all happening and getting committed fine in the database. It's just this error is getting caught inside catch block in my app. Any ideas?
I can not ignore it because based on the return value of ExecuteNonQuery(), I have some statements and also it's not going through the code present after ExecuteNonQuery().
This is most likely because you are using SqlDbType with OleDbParameters:
OleDbParameter output = new OleDbParameter("#returnStatus", SqlDbType.VarChar);
This causes .NET to use the OleDbParameter(String, Object) constructor, setting the value of the parameter to SqlDbType.VarChar which it assumes is an int.
You should use this instead:
OleDbParameter output = new OleDbParameter("#returnStatus", OleDbType.VarChar);
And change your calls to cmd.Parameters.Add to use OleDbType as well.
Alternatively, you could use System.Data.SqlClient instead of OleDb