I have a one column table to keep track of number of visits. In Global.asax.cs I attempt to increment this value by 1 inside application_start but the field does't get updated. I get no exceptions but number of rows affected is always zero.
I tried the same simple query in SSMS and I get the same thing: 0 rows affected.
There is one int column in that table called NumVisits.
This is part of Application_Start in Global.asax.cs:
Application.Lock();
int iNumVisits = SomeClass.GetNumVisits();
SomeClass.UpdateNumVists(iNumVisits + 1);
Application.UnLock();
This is in SomeClass (BLL):
public static void UpdateNumVists(int iNumVisists)
{
LocaleRepository oLocaleRepository = new LocaleRepository(new SqlDbContext());
oLocaleRepository.UpdateNumVists(iNumVisists);
}
and this is in DAL:
public void UpdateNumVists(int iNumVisits)
{
int iRet = 0;
try
{
dbContext.Open();
List<SqlParameter> spParams = new List<SqlParameter>();
string sQuery = "update Visits set NumVisits = #NumVisits";
spParams.Add(dbContext.CreateSqlParam("#NumVisits", SqlDbType.Int, 0, iNumVisits));
dbContext.ExecuteSqlNonQuery(sQuery, spParams, ref iRet);
}
catch (Exception e)
{
throw e;
}
finally
{
dbContext.Close();
}
return;
}
I use the following for all commands using executeNonQuery:
public void ExecuteSqlNonQuery(string sQuery, List<SqlParameter> spParams, ref int iRet)
{
using (SqlCommand command = new SqlCommand())
{
command.CommandType = CommandType.Text;
command.Parameters.AddRange(spParams.ToArray<SqlParameter>());
command.Connection = DbConnection;
command.CommandText = sQuery;
try
{
iRet = command.ExecuteNonQuery();
}
catch(Exception e)
{ }
}
}
when the update command is executed, iRet is zero. I can't see why a simple update query would not work.
This is the create script I got from SSMS:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Visits](
[NumVisits] [int] NULL
) ON [PRIMARY]
GO
In general there are a few possible reasons why an update would not happen.
First, if the field is also an identity or calculated field, an ordinary update is not going to work. This doesn't look to be your case, but it is good to know for the future.
Next if there is a trigger on the table, it may be preventing the update. SSMS doesn't necessarily script triggers out when you script a table, so I can't tell if you have a trigger.
Third And most common, your application may not be sending what you expect as the update statement or even communicating with the database at all when you expect it to. This is often true when there is a problem of nulls. If your variable is not properly populating, then you may indeed be updating a null value to a null value. Run Profiler to capture exactly what is being sent when you try to do the update. Often when you see the statement that is actually being run, you will see the problem. Sometimes it is a matter of a missing space, sometimes a variable that was not populated that you thought was populated, etc.
Another possibility is that the user running the code has no update rights to the table. You should have gotten a message if this were the case.
If you have run Profiler, try running that exact code in SSMS and see if it updates. Sometimes you get a better error bubble up when you do that. Especially if your error handling in the application code is not well designed.
Of course if the table has no data, you need to do an insert not an update. Or the update might not be finding any records to update, try doing a select using the same conditions and it may turn out there is not record to update.
It seems like there is no data in the table, are there any records in Visits?
Related
I am working on a big solution in C#. This solution uses different oracle databases and is checked in in TFS. Actually I develop a small library which can deploy the SQL Scripts in the solution to the used databases. The deployment of the application is handled with TFS and in the future my tool should be included in the build process. For the Oracle Connection an execution of the statements I use Oracle.ManagedDataAccess.Client.
If there are only several DDL or DML statements in the SQL Files the first tests were successful. But there are a few special special things I have to handle, like SET DEFINE OFF. This is no real SQL Statement and if I run the Script with ORacle SQL*PLus there is no problem. In the beginning I tried to execute the whole script but this doesn't work because multiple statements were put in one line by my application and the only solution I found was the splitting of the file and single execution.
Example:
/*************************************************************************************************************************
##NAME ScriptName.sql
##DESCR ScriptDescription
*************************************************************************************************************************/
Create table tmp_thm_Test
( tmp_thm_Test_id number(8)
, text varchar2(200));
Create table tmp_thm_Test2
( tmp_thm_Test2_id number(8)
, text varchar2(200));
This is an example content for a script. Also there can be Insert (Set Define off is needed while having the & in strings), Update Statements. Also Begin/End Scripts.
If there are only DDL and DML Statements I can split them but Begin/End parts are not splittable. At the beginning I thought I can deploy a script like in the past with SQL*Plus (#Scriptname.sql). Read the whole script an execute it.
Any ideas?
Since I've worked with Oracle in C# before I kind of understand you even with the lack of concrete samples.
SET DEFINE OFF never worked for me in C#, so I simply use query = query.Replace("'","''");. You could also use command parameters like the one below to avoid exceptions.
string query = string.Format(
#"UPDATE CardStatus
SET DateExpired = #dateExpired,
ModifiedBy = #modifiedBy,
ModifiedOn = #modifiedOn
WHERE
IDNo = #idNo");
List<OracleParameter> parameters = new List<OracleParameter>();
OracleParameter pIdNo = new OracleParameter("idNo", OracleDbType.Varchar2);
pIdNo.Value = idNo;
OracleParameter pDateExpired = new OracleParameter("dateExpired", OracleDbType.Date);
pDateExpired.Value = dateExpired;
OracleParameter pModifiedBy = new OracleParameter("modifiedBy", OracleDbType.Varchar2);
pModifiedBy.Value = "SIS";
OracleParameter pModifiedOn = new OracleParameter("modifiedOn", OracleDbType.Date);
pModifiedOn.Value = DateTime.Now;
parameters.Add(pIdNo);
parameters.Add(pDateExpired);
parameters.Add(pModifiedBy);
parameters.Add(pModifiedOn);
bool result = _DAL.ExecuteNonQuery(query, parameters.ToArray());
_DAL is simply my helper class for data access, it manages the query executions placing it inside a single transaction so that I get to decide whether to commit or rollback the changes of single to multiple queries.
public bool ExecuteNonQuery(string query, object[] parameters, [CallerMemberName] string callerMemberName = "")
{
bool success = false;
try
{
using (OracleCommand command = new OracleCommand())
{
command.Connection = _connection;
command.Transaction = _transaction;
command.CommandText = query;
command.CommandType = CommandType.Text;
command.Parameters.AddRange(parameters);
command.ExecuteNonQuery();
}
this.AuditSQL(query, string.Empty);
success = true;
}
catch (OracleException ex)
{
this.AuditSQL(query, ex.Message, callerMemberName);
if (ex.Number == 54) // SELECT .. FOR UPDATE NOWAIT failed.
{
throw new RowLockException();
}
}
return success;
}
I am not sure if you are interested in looking at my _DAL class but I provided you a snippet to give you an idea of what you can do.
EDIT: Since you've mentioned that the scripts already exists then it is easier, you just have to make sure that there is no ' character within the script to make oracle ask for a variable value and all batch queries must have BEGIN and END;.
Example:
BEGIN
-- INSERT
-- UPDATE
-- INSERT
... etc.
END;
Now, what exactly is your problem? You can read the query from the file and replace all SET DEFINE OFF (as well as other statements you deem unnecessary) with string.Empty and replace all ' with '' if needed. Worst case, you would be using regex to identify and remove unwanted statements.
I have a C# project which connects to a TSQL database. The project runs multiple sequential update statements on a single table, eg.:
private void updateRows() {
string update1 = "UPDATE WITH (ROWLOCK) table SET ... WHERE ...;"
string update2 = "UPDATE WITH (ROWLOCK) table SET ... WHERE ...;"
string update3 = "UPDATE WITH (ROWLOCK) table SET ... WHERE ...;"
// execute updates one after the other
}
Ideally, these statements will be batched to avoid making multiple round-trips to/from the database:
string update = "
UPDATE WITH (ROWLOCK) table SET ... WHERE ...;
GO
UPDATE WITH (ROWLOCK) table SET ... WHERE ...;
GO
UPDATE WITH (ROWLOCK) table SET ... WHERE ...;
GO
";
My question is, if the statements are batched, does this increase the chance of deadlock errors occurring due to table scans?
As there is less time between each statement, I imagine that this could increase chances of deadlocks as one update could introduce a row or page lock, which may not be released by the time the next statement is executed. However if the update statements were not batched, then there is more time for row or page locks to be released between each update statement, therefore less chance of deadlocks occurring.
I guess you're not going to like my answer, here are my 2 cents, let me try and explain
first, your rowlock might not work, you might end up getting a table lock of your DDL doesn't allow SQL server to apply row locking in your transaction.
SQL likes set operations, it performs great when you update large datasets in one time.
I have a similar issue, I need to update large volumes of user transactions but I have no spare IO in the systems. I end-up using an 'ETL like' update,
In C# I'm using a bulk insert to get all data in the database in one go. Here is my method.
protected void BulkImport(DataTable table, string tableName)
{
if (!CanConnect)
return;
var options = SqlBulkCopyOptions.FireTriggers | SqlBulkCopyOptions.CheckConstraints |
SqlBulkCopyOptions.UseInternalTransaction;
using (var bulkCopy = new SqlBulkCopy(_con.ConnectionString, options))
{
bulkCopy.DestinationTableName = tableName;
bulkCopy.BulkCopyTimeout = 30;
try
{
lock(table){
bulkCopy.WriteToServer(table);
table.Rows.Clear();
table.AcceptChanges();
}
}
catch (Exception ex)
{
var msg = $"Error: Failed the writing to {tableName}, the error:{ex.Message}";
Logger?.Enqueue(msg);
try
{
var TE= Activator.CreateInstance(ex.GetType(), new object[] { $"{msg}, {ex.Message}", ex });
Logger?.Enqueue(TE as Exception);
}
catch
{
Logger?.Enqueue(ex);
}
}
finally
{
bulkCopy.Close();
}
}
}
Please note that DataTable is not thread safe, you need to lock the DataTable when interacting (insert rows, clear table) with it.
Then I dump the data in a staging table and use a merge statement to bring the data into the database where I need it.
I do +100k records per second on 50 or so tables and have not had any performance or deadlock issues till now.
I have an C# method to execute a SQL job. It executes the SQL job successfully.
And the code works perfect.
And I'm using standard SQL stored procedure msdb.dbo.sp_start_job for this.
Here is my code..
public int ExcecuteNonquery()
{
var result = 0;
using (var execJob =new SqlCommand())
{
execJob.CommandType = CommandType.StoredProcedure;
execJob.CommandText = "msdb.dbo.sp_start_job";
execJob.Parameters.AddWithValue("#job_name", "myjobname");
using (_sqlConnection)
{
if (_sqlConnection.State == ConnectionState.Closed)
_sqlConnection.Open();
sqlCommand.Connection = _sqlConnection;
result = sqlCommand.ExecuteNonQuery();
if (_sqlConnection.State == ConnectionState.Open)
_sqlConnection.Close();
}
}
return result;
}
Here is the sp which executing inside the job
ALTER PROCEDURE [Area1].[Transformation]
AS
BEGIN
SET NOCOUNT ON;
SELECT NEXT VALUE FOR SQ_COMMON
-- Transform Master Data
exec [dbo].[sp_Transform_Address];
exec [dbo].[sp_Transform_Location];
exec [dbo].[sp_Transform_Product];
exec [dbo].[sp_Transform_Supplier];
exec [dbo].[sp_Transform_SupplierLocation];
-- Generate Hierarchies and Product References
exec [dbo].[sp_Generate_HierarchyObject] 'Area1',FGDemand,1;
exec [dbo].[sp_Generate_HierarchyObject] 'Area1',RMDemand,2;
exec [dbo].[sp_Generate_Hierarchy] 'Area1',FGDemand,1;
exec [dbo].[sp_Generate_Hierarchy] 'Area1',RMDemand,2;
exec [dbo].[sp_Generate_ProductReference] 'Area1',FGDemand,1;
exec [dbo].[sp_Generate_ProductReference] 'Area1',RMDemand,2;
-- Transform Demand Allocation BOM
exec [Area1].[sp_Transform_FGDemand];
exec [Area1].[sp_Transform_FGAllocation];
exec [Area1].[sp_Transform_RMDemand];
exec [Area1].[sp_Transform_RMAllocation];
exec [Area1].[sp_Transform_BOM];
exec [Area1].[sp_Transform_RMDemand_FK];
-- Transform Purchasing Document Data
exec [dbo].[sp_Transform_PurchasingDoc];
exec [dbo].[sp_Transform_PurchasingItem];
exec [dbo].[sp_Transform_ScheduleLine];
exec [dbo].[sp_CalculateRequirement] 'Area1'
exec [dbo].[sp_Create_TransformationSummary] 'Area1'
-- Trauncate Integration Tables
exec [dbo].[sp_TruncateIntegrationTables] 'Area1'
END
The problem is, even the job is executed successfully or not it always returns -1. How can I identify whether job is successfully executed or not.
After running msdb.dbo.sp_start_job the return code is mapped to an output parameter. You have the opportunity to control the parameter's name prior to execution:
public int StartMyJob( string connectionString )
{
using (var sqlConnection = new SqlConnection( connectionString ) )
{
sqlConnection.Open( );
using (var execJob = sqlConnection.CreateCommand( ) )
{
execJob.CommandType = CommandType.StoredProcedure;
execJob.CommandText = "msdb.dbo.sp_start_job";
execJob.Parameters.AddWithValue("#job_name", "myjobname");
execJob.Parameters.Add( "#results", SqlDbType.Int ).Direction = ParameterDirection.ReturnValue;
execJob.ExecuteNonQuery();
return ( int ) sqlCommand.Parameters["results"].Value;
}
}
}
You need to know the datatype of the return code to do this - and for sp_start_job, it's SqlDbType.Int.
However, this is only the results of starting the job, which is worth knowing, but isn't the results of running your job. To get the results running of your job, you can periodically execute:
msdb.dbo.sp_help_job #jobName
One of the columns returned by the procedure is last_run_outcome and probably contains what you're really interested in. It will be 5 (unknown) while it's still running.
A job is usually the a number of steps - where each step may or may not be executed according to the outcome of previous steps. Another procedure called sp_help_jobhistory supports a lot of filters to specify which specific invocation(s) and/or steps of the job you're interested in.
SQL likes to think about jobs as scheduled work - but there's nothing to keep you from just starting a job ad-hoc - although it doesn't really provide you with much support to correlate your ad-hoc job with an instance is the job history. Dates are about as good as it gets (unless somebody knows a trick I don't know.)
I've seen where the job is created ad-hoc job just prior to running it, so the current ad-hoc execution is the only execution returned. But you end up with a lot of duplicate or near-duplicate jobs laying around that are never going to be executed again. Something you'll have to plan on cleaning up afterwards, if you go that route.
A note on your use of the _sqlConnection variable. You don't want to do that. Your code disposes of it, but it was apparently created elsewhere before this method gets called. That's bad juju. You're better off just creating the connection and disposing of it the same method. Rely on SQL connection pooling to make the connection fast - which is probably already turned on.
Also - in the code you posted - it looks like you started with execJob but switched to sqlCommand - and kinda messed up the edit. I assumed you meant execJob all the way through - and that's reflected in the example.
From MSDN about SqlCommand.ExecuteNonQuery Method:
For UPDATE, INSERT, and DELETE statements, the return value is the number of rows affected by the command. When a trigger exists on a table being inserted or updated, the return value includes the number of rows affected by both the insert or update operation and the number of rows affected by the trigger or triggers. For all other types of statements, the return value is -1. If a rollback occurs, the return value is also -1.
In this line:
result = sqlCommand.ExecuteNonQuery();
You want to return the number of rows affected by the command and save it to an int variable but since the type of statement is select so it returns -1. If you test it with INSERT or DELETE or UPDATE statements you will get the correct result.
By the way if you want to get the number of rows affected by the SELECT command and save it to an int variable you can try something like this:
select count(*) from jobs where myjobname = #myjobname
And then use ExecuteScalar to get the correct result:
result = (int)execJob.ExecuteScalar();
You need to run stored proceedure msdb.dbo.sp_help_job
private int CheckAgentJob(string connectionString, string jobName) {
SqlConnection dbConnection = new SqlConnection(connectionString);
SqlCommand command = new SqlCommand();
command.CommandType = System.Data.CommandType.StoredProcedure;
command.CommandText = "msdb.dbo.sp_help_job";
command.Parameters.AddWithValue("#job_name", jobName);
command.Connection = dbConnection;
using (dbConnection)
{
dbConnection.Open();
using (command){
SqlDataReader reader = command.ExecuteReader();
reader.Read();
int status = reader.GetInt32(21); // Row 19 = Date Row 20 = Time 21 = Last_run_outcome
reader.Close();
return status;
}
}
}
enum JobState { Failed = 0, Succeeded = 1, Retry = 2, Cancelled = 3, Unknown = 5};
Keep polling on Unknown, until you get an answer. Lets hope it is succeeded :-)
I have an object, say a note, whose parameters I want to save in a table(content, size, width, height, etc).
With having the sql string updateNotes:
UPDATE NOTES
SET
CONTENT=#content,
POSX=#posX,
POSY=#posY,
COLOR=#color,
ORIENTATION=#orientation,
HEIGHT=#height,
WIDTH=#width,
AUTHOR=#autho
WHERE SESSION =#sid
public void save()
{
SQLiteConnection conn = new SQLiteConnection(connectionString);
foreach (Note note in notes)
{
using (SQLiteCommand comm = new SQLiteCommand(updateNotes, conn))
{
comm.Parameters.AddWithValue("#content", note.getNoteItemContent());
comm.Parameters.AddWithValue("#posX", note.getSvi().Center.X);
comm.Parameters.AddWithValue("#posY", note.getSvi().Center.Y);
comm.Parameters.AddWithValue("#sid", sid);
comm.Parameters.AddWithValue("#color", note.getColor());
comm.Parameters.AddWithValue("#orientation", note.getSvi().Orientation);
comm.Parameters.AddWithValue("#height", note.getSvi().ActualHeight);
comm.Parameters.AddWithValue("#width", note.getSvi().ActualWidth);
comm.Parameters.AddWithValue("#author", note.getAuthor());
try
{
conn.Open();
comm.ExecuteNonQuery();
comm.Parameters.Clear();
}
catch (Exception e) { Microsoft.Surface.UserNotifications.RequestNotification("Database error", "Could not write to the database:" + e.Message); }
finally
{
if (conn != null) { conn.Close(); }
listLoaded = false;
}
}
}
}
The method above does indeed update the rows in the table but with one value for all the rows that result from the query.
As a solution I thought of reading the note id first and then iterate (i++), but given that some notes(represented as rows in the table) may be deleted, the id will not necessarily follow a consecutive numbering.
Another solution was to query the database for all the rows(notes) for the given session(sid) and store their id in an array and the update the notes whose id can be found in the array.
Do you know of any other better optimised solution? Or do you reckon I should use the array for storing the id's of the rows to be updated and the apply the query.
Thanks!
We seem to have resolved this in the question's comments but just to be clear on the answer (and hopefully give you something to accept), here is a summary:
When creating note objects, the data constituting those notes is selected from the database. As well as selecting outward facing data such as the note colour and content, we also select the primary key of each note and store this as a property of the note object.
Now, when a note object is updated, you can include its id in the where clause for the corresponding update statement and hence only update the one row corresponding to the modified note.
Glad I could provide some "enlightenment" -- as you put it.
I'm trying to update an MSSQL table using SqlCommand, I think it's a syntax error with my T-SQL, but here is what I have so far:
SqlCommand sqlCmd = new SqlCommand("UPDATE yak_tickets SET email = #emailParam, subject = #subjectParam, text = #textParam, statusid = #statusIDParam, ticketClass = #ticketClassParam WHERE id = #ticketIDParam", sqlConn);
The parameters are working as they should, however, the table never gets updated when I run the code. Any help would be appreciated =)
Here is the rest of the code:
#region Parameters
/* Parameters */
sqlCmd.Parameters.Add("#ticketIDParam", SqlDbType.BigInt);
sqlCmd.Parameters["#ticketIDParam"].Value = ticketID;
sqlCmd.Parameters.Add("#emailParam", SqlDbType.NVarChar);
sqlCmd.Parameters["#emailParam"].Value = ticketToBeSubmitted.getEmail();
sqlCmd.Parameters.Add("#subjectParam", SqlDbType.NVarChar);
sqlCmd.Parameters["#subjectParam"].Value = ticketToBeSubmitted.getSubject();
sqlCmd.Parameters.Add("#textParam", SqlDbType.Text);
sqlCmd.Parameters["#textParam"].Value = ticketToBeSubmitted.getTicketContent();
sqlCmd.Parameters.Add("#statusIDParam", SqlDbType.NVarChar);
sqlCmd.Parameters["#statusIDParam"].Value = ticketToBeSubmitted.getStatus();
sqlCmd.Parameters.Add("#ticketClassParam", SqlDbType.NVarChar);
sqlCmd.Parameters["#ticketClassParam"].Value = ticketToBeSubmitted.getTicketClass();
#endregion
#region Try/Catch/Finally
/* Try/Catch/Finally */
try
{
sqlConn.Open();
sqlCmd.ExecuteNonQuery();
}
catch (SqlException sqlEx)
{
sqlErrorLabel.Text = sqlEx.ToString();
sqlErrorLabel.ForeColor = System.Drawing.Color.Red;
}
finally
{
sqlConn.Close();
}
And the method's signature:
public static void updateTicketInDatabase(Ticket ticketToBeSubmitted, Label sqlErrorLabel, int ticketID)
UPDATE FROM is invalid syntax (edit: OP corrected this). The problem might also be the "text" column. text is a keyword in SQL Server, since it's a datatype. Try putting brackets around it.
UPDATE yak_tickets
SET email = #emailParam,
subject = #subjectParam,
[text] = #textParam,
statusid = #statusIDParam,
ticketClass = #ticketClassParam
WHERE id = #ticketIDParam
Had to use if(!Page.IsPostBack)
Couple of questions:
Is this inside of a transaction thats getting rolledback?
Have you verified that you #ticketIDParam matches a set of rows on the table? Especially if its not just a integer key
Are you updating rows that have no side effects (i.e. your updating to the same values)?
Can you provide the paramaters.Add statements for this query
Is there a trigger or other setting on the table (I assume not, as you did not mention anything).
You said you know the params are working correctly, can you say how you verified this? (profiler, visual inspection, etc).
Sounds like your hosting provider limits your debug options, forcing you to do it the old fashioned way. What if immediately after the update, you put something like:
;SELECT ##ROWCOUNT
then instead of ExecuteNonQuery, do ExecuteScalar, and see if SQL even thinks its updated anything.