SQL lock row,table on insert and select - sql

I am trying to achieve some sort of lock on sql.
To explain what am i doing simple:
One table with Id int autoincrement as PK, and one field Data varchar(max) non-clustered IX
Now i have some C# code that simlpy checks if the item isn't in the db, makes an insert
The sql code that i am using behind is like:
INSERT INTO {0}.{1} WITH (TABLOCKX) VALUES(#data...)
and the select one is:
SELECT Id FROM {0}.{1} WITH (TABLOCKX) WHERE(Data = #data)
But i can see that there are items with the same value inserted multiple times
TABLOCK creates deadlocks, and i dont want to use unique index because its very slow.
Is there a way to achieve this with locking?

I'm not sure it is what you want, I hope that this reply is helpful.
private void test(string aConnectionString, string aData)
{
using (SqlConnection sqlConnection = new SqlConnection(aConnectionString))
{
sqlConnection.Open();
SqlCommand sqlCommand = sqlConnection.CreateCommand();
SqlTransaction sqlTransaction = sqlConnection.BeginTransaction(System.Data.IsolationLevel.ReadCommitted);
sqlCommand.Connection = sqlConnection;
sqlCommand.Transaction = sqlTransaction;
try
{
sqlCommand.CommandText = #"IF NOT EXISTS(SELECT Id FROM {0}.{1} WHERE Data = #Data)
BEGIN
INSERT INTO {0}.{1}
SELECT #Data
END";
sqlCommand.Parameters.Add("#Data", System.Data.SqlDbType.VarChar).Value = aData;
sqlTransaction.Commit();
}
catch (Exception ex)
{
sqlTransaction.Rollback();
}
}
}

Related

SqlDataAdapter.update() not updating database

I am searching for (PostId,UserId) into PostLikes table using SqlDataAdapter, if the row is found , I am using SqlCommandBuilder.GetDeleteCommand() to generate the delete instruction and deleting the underlying row, if the row is not found, then I use SqlCommandBuilder.GetInsertCommand() to generate the insert command and inserting the row to the table using SqlDataAdapter.Update(). But the row is not getting inserted to the table in database. Here is what I have done so far
SqlConnection con = new SqlConnection(connectionStrings);
SqlDataAdapter sqlDataAdapter=new SqlDataAdapter("select * from PostLikes where PostId like "
+postlike.PostId+" and UserId like "
+postlike.UserId,con);
DataSet ds = new DataSet();
sqlDataAdapter.Fill(ds, "Result");
con.Open();
SqlCommandBuilder sqlCommandBuilder = new SqlCommandBuilder(sqlDataAdapter);
if(ds.Tables["Result"].Rows.Count==1)
{
sqlDataAdapter.DeleteCommand = sqlCommandBuilder.GetDeleteCommand(true);
msg = "Data is deleted";
}
else
{
sqlDataAdapter.InsertCommand = sqlCommandBuilder.GetInsertCommand(true);
msg = "Data is inserted";
}
sqlDataAdapter.Update(ds, "Result");
and the tablePostLikes(LikeId,PostId,UserId)
There are a couple of issues:
You are looking to reuse the same command to both detect whether the row exists, and to supply to the SqlAdapter for the SqlCommandBuilder.
You should parameterise the initial select query to protect against SqlInjection attacks (and there is a minor performance benefit). The CommandBuilder will automatically parameterize the Insert / Delete commands
After creating the Insert / Delete commands with the SqlCommandBuilder, you then need to change the underlying dataset in order for any changes to be made to the table during the Update.
Note that many of the Sql objects are IDisposable and should be disposed ASAP - using scopes help here.
.
var postId = 1;
var userId = 1;
string msg;
using (var con = new SqlConnection(#"data source=..."))
using (var selectCommand = new SqlCommand(
"select LikeId, PostId, UserId from PostLikes WHERE PostId=#PostId AND UserId=#UserId", con))
using (var sqlDataAdapter = new SqlDataAdapter(selectCommand))
using (var ds = new DataSet())
{
con.Open();
selectCommand.Parameters.AddWithValue("#PostId", postId);
selectCommand.Parameters.AddWithValue("#UserId", userId);
sqlDataAdapter.Fill(ds, "Result");
using (var sqlCommandBuilder = new SqlCommandBuilder(sqlDataAdapter))
{
if (ds.Tables["Result"].Rows.Count == 1)
{
sqlDataAdapter.DeleteCommand = sqlCommandBuilder.GetDeleteCommand(true);
ds.Tables["Result"].Rows[0].Delete();
msg = "Data will be deleted";
}
else
{
sqlDataAdapter.InsertCommand = sqlCommandBuilder.GetInsertCommand(true);
// Null because LikeId is Identity and will be auto inserted
ds.Tables["Result"].Rows.Add(null, postId, userId);
msg = "Data will be inserted";
}
sqlDataAdapter.Update(ds, "Result");
}
}
I've assumed the following Schema:
CREATE TABLE PostLikes
(
LikeId INT IDENTITY(1,1) PRIMARY KEY,
PostId INT,
UserId INT
)
And I've assumed you want to 'toggle' the insertion or deletion of a row with the postId, userid combination.

Retrieve value of a table and using the value in stored procedure

I'm beginner in SQL Server 2012; I need to generate a product ID in a stored procedure, I generated part of the ID in C#, that part of ID includes Industrialist ID and I pass this to my stored procedure. In the stored procedure I need the last product of my Industrialist number and save in to as SQL variable on my stored procedure. How can I do this?
There are many ways to pass items between SQL and C#, you could use an output parameter where you will populate the parameter within the stored procedure.
string variableName;
using (var conn = new SqlConnection("**connection string**"))
using (var cmd = new SqlCommand("storedProcedureName", conn))
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("inputParameter", inputParameter);
var outputParameter = new SqlParameter(){
ParameterName="ParameterName"
,Direction = ParameterDirection.Output
,SqlDbType = SqlDbType.VarChar
,DbType = DbType.VarChar
};
conn.Open();
try
{
cmd.ExecuteNonQuery();
variableName = string.Format("{0}", outputParameter.Value);
}
catch{}
finally
{
conn.Close();
}
}
You could return the value using something along the lines of RETURN #returnValue in your procedure, or you could return it within a table.
string variableName;
using (var conn = new SqlConnection("**connection string**"))
using (var cmd = new SqlCommand("storedProcedureName", conn))
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("inputParameter", inputParameter);
conn.Open();
try
{
using (var dbReader = cmd.ExecuteReader())
{
if (dbReader.Read())
{
variableName = string.Format("{0}", dbReader["ColumnName"]);
}
}
}
catch{}
finally
{
conn.Close();
}
}
A generic example of a stored procedure which might work:
CREATE PROC [dbo].[storedProcedureName]
#InputParameter VARCHAR
,#OutputParameter VARCHAR OUTPUT
AS BEGIN
DECLARE #insertedId INT;
BEGIN TRANSACTION
INSERT INTO TableName (...Column Names...)
VALUES (... Values...)
SET #insertedId = SCOPE_IDENTITY()
COMMIT
SELECT #OutputParameter = ColumnName
FROM TableName
WHERE IdColumnName = #insertedId
END
EDIT: Possibly more relevant:
CREATE PROC [dbo].[storedProcedureName]
#IndustrialistId INT
,#OutputParameter VARCHAR OUTPUT -- This might be an int, but it's unclear what you want
AS BEGIN
DECLARE #productId INT;
SELECT #productId = MAX(ProductId)
FROM Products
WHERE IndustrialistId = #IndustrialistId;
SET #OutputParameter = CONVERT(VARCHAR,#IndustrialistId) + '-' + CONVERT(VARCHAR,#productId)
END
If you were to provide some code it might be easier for someone to give you a more tailored response. None of the above code has been syntax checked etc. so should be considered more pseudo code but hopefully it gives you something to work with.

MS Access ##identity query issue

Can someone please let me know the issue with the below query? I am running on MS Access and its giving
Syntax error in query expression 'id = ##IDENTITY'
Code:
public DosageBO SaveDosage(DosageBO dosage)
{
try
{
using (IDbConnection connection = OpenConnection())
{
StringBuilder sql = new StringBuilder();
sql.AppendLine("INSERT INTO dosage_master ( medicine_type, dosage, remarks, updateby, updatedate )");
sql.AppendLine("VALUES (#type, #dose, #remarks, #updateby, NOW());");
var parameters = new
{
type = dosage.MedicineType,
dose = dosage.Dosage,
remarks = dosage.Remarks,
updateby = Environment.UserName
};
connection.Execute(sql.ToString(), parameters);
return connection.Query<DosageBO>("SELECT medicine_type as MedicineType, dosage, remarks FROM dosage_master WHERE id = ##IDENTITY").FirstOrDefault();
}
}
catch
{
throw;
}
}
SELECT ##Identity is a specialized query. And ##Identity is only valid in that context. If you attempt to use ##Identity elsewhere, as in a WHERE clause, the db engine will throw an error.
You will have to retrieve the value from SELECT ##Identity, save it, and then use that saved value in your other query.
Remove the ) at the end
WHERE id = ##IDENTITY)
^---here
Are you inserting a row in this batch prior to the select query?
To my knowledge ##IDENTITY is only available directly after inserting a row causing an identity value to be generated i.e an insert to an autoincremental identity column.
Edit again:
Try enclosing it in a subquery e.g id = (SELECT ##IDENTITY)

SQL Server CE : if exist update else insert

How can I INSERT a row if does not yet exist in a SQL Server CE database table and UPDATE it if it exists?
I have tried lot of SQL queries and keep getting errors. This is not working.
IF EXISTS (SELECT * FROM Table1 WHERE Column1='SomeValue')
UPDATE Table1 SET (...) WHERE Column1='SomeValue'
ELSE
INSERT INTO Table1 VALUES (...)
Update:
I have found this which is working for me. Any other good suggestion is welcome.
INSERT INTO Table1 VALUES (...)
SELECT (........)
WHERE NOT Exists (SELECT ........)
-- INSERT with Default value if not exist. Next, UPDATE it
UPDATE Table1 SET (...) WHERE Column1='SomeValue'
I know you've tagged sql and sql-server-ce but in case you're open to using c# code to fix this.. :
Using c# and result sets this is what I did for my mobile app using SQL CE:
// UpdateRow is a struct/class to hold the data for each row
// SqlCeConn = connection string for db
SqlCeCommand cmd = new SqlCeCommand("Table1", SqlCeConn);
cmd.CommandType = CommandType.TableDirect;
cmd.IndexName = "Column1";
using (SqlCeResultSet rsltSet = cmd.ExecuteResultSet(ResultSetOptions.Scrollable | ResultSetOptions.Updatable))
{
if (UpdateRow.Column1> 0) // or != "" if it's string etc
{
if (rsltSet.Seek(DbSeekOptions.FirstEqual, UpdateRow.Column1))
FoundRecord = true;
}
rsltSet.Read();
if (FoundRecord)
{
// Update
rsltSet.SetInt32(1, UpdateRow.Column1);
rsltSet.SetInt32(2, UpdateRow.Column2);
// etc
rsltSet.Update();
}
else
{
// Insert new record
SqlCeUpdatableRecord record = rsltSet.CreateRecord();
record.SetInt32(0, UpdateRow.Column1);
record.SetInt32(1, UpdateRow.Column2);
// etc
rsltSet.Insert(record, DbInsertOptions.PositionOnInsertedRow);
}
}
cmd.Dispose();
}
catch (Exception e)
{
// Deal with exception
}

error in the syntax of cmd parameters

hi guyz in this method i m just adding the values to the db.
temp is a object.
the field value and variables in the object re havin the same name..
dono y this error s comin
plz
help me out...
public virtual void Save_input_parameter_details(Test_Unit_BLL temp )
{
SqlConnection con;
con = new SqlConnection("Data Source=VV;Initial Catalog=testingtool;User ID=sa;Password=sa;");
con.Open();
SqlCommand cmd, cmd2, cmd3;
//try
//{
for (int i = 0; i < temp.No_Input_parameters; i++)
{
cmd2 = new SqlCommand("insert into Input_parameter_details values(#Input_Parameter_name,#Input_Parameter_datatype,#noparams,#class_code", con);
cmd2.Parameters.AddWithValue("#Input_Parameter_datatype", temp.Input_Parameter_datatype[i]);
cmd2.Parameters.AddWithValue("#Input_Parameter_name", temp.Input_Parameter_name[i]);
cmd2.Parameters.AddWithValue("#noparams", temp.No_Input_parameters);
cmd2.Parameters.AddWithValue("#class_code",temp.class_code);
cmd2.ExecuteNonQuery();
}
//}
//catch (Exception ex)
// {
// MessageBox.Show("error"+ex);
// }
}
It may be failing based on the unknown actual sequence of columns its trying to push the data into. You are implying the first X number of columns. You may need to be explicit in your SQL such as :
insert into YourTable ( Fld1, Fld2, Fld3 ) values (#ParmVal1, #ParmVal2, #ParmVal3 );
Then do your parameters.add with values... Additionally, you MAY want to make sure your added parameters are in the same sequence as your SQL statement lists them too.
Do the columns in the table line up with the parameters as you have them listed (1st = input_parameter_name, 2nd = input_parameter_datatype, etc.)?