we are having issues with a stored procedure. When calling it from our website via asp/vb.net it seems to not be executing properly. If I run it from SSMS it works.
I have run the debugger when the call is being placed, the parameters being passed in are correct at the time of the ExecuteNonQuery() call but it is not generating any records in the related tables like it should. If I use the same values seen while debugging our website directly in SSMS, the stored procedure creates the expected records.
Here is our stored procedure:
ALTER PROCEDURE [dbo].[CopyGoals](
#OldVisitID int,
#NewVisitID int,
#CreatedBy NVarChar(30)
) AS BEGIN
declare #GoalMapping As Table(OldGoalID int,NewGoalID int);
Merge Into VisitGoals
Using(
select GoalsID,Goal,ProgressNote,Progress,Completed,CreatedOn,CreatedBy,VisitID
From VisitGoals
Where VisitID = #OldVisitID
) As Src
On 1 = 0
When Not Matched By Target Then
Insert (Goal,ProgressNote,Completed,VisitID,Progress, CreatedOn, CreatedBy)
Values (Src.Goal, Src.ProgressNote, Src.Completed, #NewVisitID, Src.Progress, GetDate(), #CreatedBy)
Output Src.GoalsID As OldGoalID, inserted.GoalsID as NewGoalID
Into #GoalMapping;
Insert Into SubGoals(GoalID,VisitID,GoalText,HasCompleted,WillComplete,GoalStatus)
(
Select GM.NewGoalID, #NewVisitID, SG.GoalText, SG.HasCompleted, SG.WillComplete, SG.GoalStatus
From SubGoals As SG inner join #GoalMapping As GM on SG.GoalID = GM.OldGoalID
Where SG.VisitID = #OldVisitID
)
END
Here is the procedure call from our website page:
Dim conStr As String = ConfigurationManager.ConnectionStrings("ConnectionString").ConnectionString
Dim curUsr As New Supervisor(Context.User.Identity.Name, True)
Using con As New SqlConnection(conStr)
Using cmd As New SqlCommand("CopyGoals", con)
cmd.CommandType = CommandType.StoredProcedure
cmd.Parameters.Add("#OldVisitID", SqlDbType.Int).Value = oldVID
cmd.Parameters.Add("#NewVisitID", SqlDbType.Int).Value = newVID
cmd.Parameters.Add("#CreatedBy", SqlDbType.NVarChar, 30).Value = curUsr.Name
con.Open()
cmd.ExecuteNonQuery()
con.Close()
End Using
End Using
What this procedure should do, and does if run from SSMS, is look at our Goals table with the existing IDs matching a foreign key corresponding to the VisitID in a different table to find all goals associated with that old visit.
It them copies the goal information and inserts it into the Goals table, outputting the old goal IDs and the newly inserted goal IDs into the #GoalMapping table.
It then looks into our SubGoals table and does a similar thing by copying each subgoal related to the goals we just copied. For whatever reason, this procedure does not execute properly when called from our page even when it runs in SSMS when we feed it the same input values as seen during debugging of the page. There are no errors reported in chrome's debugger, we tried wrapping execute in a try with an SQL and normal exception but neither of them tripped. We are pretty stumped. Maybe someone will spot something we haven't over the last few days.
Here is the output from Sql Profiler:
Okay, I finally figured it out. There was a permission issue as Mohsin suggested. I logged in as the user that we use for our ASP.net connection strings and attempted to run the query in question and it failed to generate the records from SSMS. So after some digging I found this question:
Stored Procedure and Permissions - Is EXECUTE enough?
Which lead to this question when I got the same error:
SQL Server principal "dbo" does not exist,
Combined together, the answers to these questions helped me fix the issue.
Related
I am trying to write a paramaterized update query to insert values into an Sql Server Express Database. The query I have written is:
Dim cmd As New SqlCommand
cmd.Connection = conn
cmd.CommandText = "update tblposts set title=#ptitle, pdate=#pd,
content=#pcontent where pid=#p"
cmd.Parameters.AddWithValue("ptitle", txtTitle.Text)
cmd.Parameters.AddWithValue("pcontent", txtcontent.InnerText)
cmd.Parameters.AddWithValue("pd", DateTime.Now.ToString)
cmd.Parameters.AddWithValue("p", postid)
On running cmd.ExecuteNonQuery, I get number of rows affected as 1, but the change is not reflected in the database.
On printing the query using Debug.Write, I get the query not with the parameter values, but the names of the parameters itself (ie. #pcontent, #title etc)
What can be the mistake here?
In you're AddWithValue you need to include the # symbol on the front of the parameter, so:
cmd.Parameters.AddWithValue("#ptitle", txtTitle.Text)
cmd.Parameters.AddWithValue("#pcontent", txtcontent.InnerText)
cmd.Parameters.AddWithValue("#pd", DateTime.Now.ToString)
cmd.Parameters.AddWithValue("#p", postid)
I'm guessing that it's executing correctly but there where clause is blank, so perhaps updating a blank row.
Anyway, try the above and it should update as expected.
Edit to Add
The CommandText will always only have the #value in there, it will not substitue the parameter values into the string.
You would need to loop through the cmd.Parameters collection to write out the values.
I have an audit record table that I am writing to. I am connecting to MyDb, which has a stored procedure called 'CreateAudit', which is a passthrough stored procedure to another database on the same machine called MyOther DB with a stored procedure called 'CreatedAudit' as well.
In other words in MyDB I have CreateAudit, which does the following EXEC dbo.MyOtherDB.CreateAudit.
I call the MyDb CreateAudit stored procedure from my application, using subsonic as the DAL. The first time I call it, I call it with the following (pseudocode):
int openStatus, closeStatus = 0;
openStatus = Convert.ToInt32(SPs.LogAccess(userId, "OPENED"));
closeStatus = Convert.ToInt32(SPs.LogAccess(userId, "CLOSED"));
This is simplified, but this is what LogAccess calls:
ALTER procedure [dbo].[LogAccess]
#UserID uniqueid,
#Action varchar(10),
#Status integer output
as
DECLARE #mStatus INT
EXEC [MyOtherDb].[dbo].[LogAccess]
#UserID = #UserID,
#Action = #Action,
#Status = #mStatus OUTPUT
select #mStatus
In my second stored procedure it is supposed to mark the record that was created by the CreateAudit(recordId, "Opened") with a status of closed.
This works great if I run them independently of one another, or even if I paste them into query analyzer. However when they execute from the application, the record is not marked as "Closed".
When I run SQL profiler I see that both queries ran, and if I copy the queries out and run them from query analyzer the record gets marked as closed 100% of the time!
When I run it from the application, about once every 20 times or so, the record is successfully marked closed - the other 19 times nothing happens, but I do not get an error!
Is it possible for the .NET app to skip over the ouput from the first stored procedure and start executing the second stored procedure before the record in the first is created?
When I add a "WAITFOR DELAY '00:00:00:003'" to the top of my stored procedure, the record is also closed 100% of the time.
My head is spinning, any ideas why this is happening!
Thanks for any responses, very interested in hearing how this can happen.
In your 1st stored proc, try having the EXEC statement wait for a return value from the 2nd stored proc. My suspicion is that your first SP is firing off the 2nd stored proc and then immediately returning control to your .NET code, which is leading to the above commenter's concurrency issue. (That is to say, the 2nd SP hasn't finished running yet by the time your next DB call is made!)
SP1: EXEC #retval = SP2 ....
I have a SQL database that creates a record for every document uploaded by the user to the server. I want to check this table before a user uploads a document to ensure they don't upload a file with name that already exists.
I know how to make the connection and make the SqlCommand to query the table for an existing record. But I don't know how to check the record count from the sqlCommand I made.
Does that make sense?
Using myConnectionCheck As New SqlConnection(myConnectionStringCheck)
Dim myCommandCheck As New SqlCommand()
myCommandCheck.Connection = myConnectionCheck
myCommandCheck.CommandText = "SELECT * FROM Req_Docs WHERE Doc_Name =" & DocName
myConnectionCheck.Open()
myCommandCheck.ExecuteNonQuery()
End Using
Thanks in advance,
Anthony
use if exists for this issue
create procedure isDocExists
#DocName varchar(255),
#isExists bit output
as
set #isExists = 0
if exists (SELECT Doc_Name FROM Req_Docs WHERE Doc_Name =#DocName)
begin
set #isExists=1
end
to check where record is there or not
So many things wrong here:
Race condition between when you check and when you upload
Multiple Documents should legitimately be allowed to have the same name. Use tags, folders, timestamps, or other means to distinguish them.
Sql Injection vulnerability on the name parameter
ExecuteNonQuery() on a SELECT query.
I'll give you the benefit of the doubt on the first two points that you're still gonna allow the upload, and this is just so you can ask the user how they want to relate the documents. Given that, here's how you fix the other two:
Using cn As New SqlConnection(myConnectionStringCheck), _
cmd As New SqlCommand("SELECT COUNT(*) FROM (SELECT TOP 1 1 FROM Req_Docs WHERE Doc_Name= #DocName) t", cn)
cmd.Parameters.Add("#DocName", SqlDbTypes.VarChar, 255).Value = DocName
cn.Open()
Return CInt(cmd.ExecuteScalar())
End Using
ExecuteNonQuery is a function, that returns an integer equal to the number of rows affected by the query.
However, it's usually used for updates.
You might consider ExecuteScalar, which returns the first column of the first row in the result set.
So if you change the query to select count(*) from..., the result of ExecuteScalar will be the number of rows, which you can then test.
if you want count:
SELECT COUNT(*) as count FROM Req_Docs WHERE Doc_Name = 'DocName'
Here is my code:
I should get output of the department id (did) as an integer and the templatefilename (result) that is required.
The errors I get are: Conversion from string "ad" to type 'Integer' is not valid. I'm fairly new to asp.net and cannot see where the did variable picks up the "ad" string.
Any help would be greatly appreciated.
Thanks
When you construct the query to the table departmentsgroupings, you're changing the value of sql, but you aren't creating a new SqlCommand. This means that cmd still contains the old SQL statement (the query to the Modules table) which, when executed, returns "ad".
To fix this, change your code as follows:
sql = ("select departmentsid from departmentsgroupings where groupingid =" & pageid & "")
Set cmd = New SqlCommand(sql, conn)
did = (cmd.ExecuteScalar)
You may have expected the change you made to sql to get passed on automatically to the SqlCommand -- but it doesn't work that way.
Edit: Your code, as written, is vulnerable to SQL injection attacks. If you don't know what these are, you need to read the first answer to this:
How does the SQL injection from the "Bobby Tables" XKCD comic work?
To protect yourself against these kinds of attacks, use parameterized queries.
The mistake is in these lines:
sql = ("select departmentsid from departmentsgroupings where groupingid =" & pageid & "")
did = (cmd.ExecuteScalar) <---- Wrong command executed here.
You presumably meant to execute the code in sql, not cmd again.
I have an application (ASP.NET 3.5) that allows users to rerun a particular process if required. The process inserts records into an MS SQL table. I have the insert in a Try / Catch and ignore the catch if a record already exists (the error in the Title would be valid). This worked perfectly using ADO but after I conveted to LINQ I noticed an interesting thing. If on a re-run of the process there was already records in the table, any new records would be rejected with the same error even though there was no existing record.
The code is as follows:
Dim ins = New tblOutstandingCompletion
With ins
.ControlID = rec.ControlID
.PersonID = rec.peopleID
.RequiredDate = rec.NextDue
.RiskNumber = 0
.recordType = "PC"
.TreatmentID = 0
End With
Try
ldb.tblOutstandingCompletions.InsertOnSubmit(ins)
ldb.SubmitChanges()
Catch ex As Exception
' An attempt to load a duplicate record will fail
End Try
The DataContext for database was set during Page Load .
I resolved the problem by redefining the DataContext before each insert:
ldb = New CaRMSDataContext(sessionHandler.connection.ToString)
Dim ins = New tblOutstandingCompletion
While I have solved the problem I would like to know if anyone can explain it. Without the DataContext redefinition the application works perfectly if there are no duplicate records.
Regards
James
It sounds like the DataContext thinks the record was inserted the first time, so if you don't redefine the context, it rejects the second insert because it "knows" the record is already there. Redefining the context forces it to actually check the database to see if it's there, which it isn't. That's LINQ trying to save a round trip to the database. Creating a new context as you've done forces it to reset what it "knows" about the database.
I had seen a very similar issue in my code were the identity column wasn't an autoincrementing int column, but a GUID with a default value of newguid() - basically LINQ wasn't allowing the database to create the GUID, but inserting Guid.Empty instead, and the second (or later) attempts would (correctly) throw this error.
I ended up ensuring that I generated a new GUID myself during the insert. More details can be seen here: http://www.doodle.co.uk/Blogs/2007/09/18/playing-with-linq-in-winforms.aspx
This allowed me to insert multiple records with the same DataContext.
Also, have you tried calling InsertOnSubmit multiple times (once for each new record) but only calling SubmitChanges once?
gfrizzle seems to be right here...
My code fails with the duplicate key error even though I've just run a stored proc to truncate the table on the database. As far as the data context knows, the previous insertion of a record with the same key is in fact a duplicate key, and an exception is thrown.
The only way that I've found around this is:
db = null;
db = new NNetDataContext();
right after the SubmitChanges() call that executes the previous InsertOnSubmit requests. Seems kind of dumb, but it's the only way that works for me other than redesigning the code.