I have a query, that as far as I know has failed exactly one time. It's a simple select count(*) from one table, no joins. But at least this once, executing that query resulted in no data read from SqlDataReader. Not even null, just nothing. First call to Read returns false. No exception was raised.
Has any one ever heard of that before? Any scenarios you can think of that would cause it?
I'm not even sure what to ask for to look at beyond SQL server logs. It's not something we can duplicate. I am assuming I'll have to chalk it up to a fluke and move on if/until it becomes a chronic problem. Here's a similar query:
SELECT ISNULL(COUNT(*),0)
FROM dbo.TRANSACTIONS T
WHERE (@TransactionId IS NULL OR (T.TransactionId != @TransactionId))
AND T.ParentId = @ParentId
AND (T.TransactionStatus != 4)
Is there a class of error that will thrown an exception out of the query, but not raise it to the application when executing a command or reading from the resulting SqlDataReader?
UPDATE: Here's the code executing this.
using (SqlConnection conn = /*connection created and opened here*/)
{
SqlCommand cmd = new SqlCommand("usp_GetChildTransactions");
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@transactionId", SqlDbType.BigInt).Value = transactionId > 0 ? transactionId : (object)DBNull.Value;
cmd.Parameters.Add("@parentId", SqlDbType.Int).Value = parentId;
using (SqlDataReader rdr = conn.ExecuteReader(cmd))
{
rdr.Read();
itemCount = rdr.IsDBNull(0) ? 0 : rdr.GetInt32(0); //exception thrown here because there is no data
}
}
Update 2: I was hoping to not do this, but maybe the exception handling is part of the problem. Here is the create procedure for this, and also another stored procedure being called in the exception handler. Sorry for the length:
CREATE PROCEDURE dbo.usp_GetChildTransactions (
@transactionId bigint,
@parentId int,
AS
SET NOCOUNT ON
BEGIN TRY
SELECT ISNULL(COUNT(*),0)
FROM dbo.TRANSACTIONS T
WHERE (@TransactionId IS NULL OR (T.TransactionId != @TransactionId))
AND T.ParentId = @ParentId
AND (T.TransactionStatus != 4)
END TRY
BEGIN CATCH
EXEC usp_RethrowException
END CATCH
CREATE PROCEDURE dbo.usp_RethrowException
AS
SET NOCOUNT ON
DECLARE @ErrorMessage NVARCHAR(4000),
@ErrorNumber INT,
@ErrorSeverity INT,
@ErrorState INT,
@ErrorLine INT,
@ErrorProcedure NVARCHAR(200)
SELECT @ErrorNumber = ERROR_NUMBER(),
@ErrorSeverity = ERROR_SEVERITY(),
@ErrorState = ERROR_STATE(),
@ErrorLine = ERROR_LINE(),
@ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-')
if (@ErrorState = 0) set @ErrorState = 1
SELECT @ErrorMessage = ERROR_MESSAGE()
IF (LEFT(@ErrorMessage, 15) = N'( Error Number=')
BEGIN
RAISERROR
(@ErrorMessage,
@ErrorSeverity,
@ErrorState)
END
ELSE
BEGIN
-- First time - Create the message with all error information in a standard string format
SELECT @ErrorMessage = N'( Error Number=%d, Severity=%d, State=%d, Procedure=%s, Line=%d )' + CHAR(13) + @ErrorMessage
RAISERROR
(@ErrorMessage,
@ErrorSeverity,
@ErrorState,
@ErrorNumber,
@ErrorSeverity,
@ErrorState,
@ErrorProcedure,
@ErrorLine)
END
P.S. I did edit this a little, changing names of the procedures, the name of the table only, and removed comments only.
Best Answer
If the query errs in the scope of T-SQL structured error handling, the empty or partial result set is returned to the client followed by the error message in the TDS protocol stream. The implications are that client code should not assume SqlDataReader always returns true and that all rows and result sets must be consumed before the exception is raised in the client code. Consider the following example:
Note that
ExecuteNonQuery
implicitly reads and ignores all rows and result sets (which will raise an exception if the batch erred) butExecuteScaler
andExecuteReader
do not. Also,DataAdaper.Fill
consumes all results with aDataSet
target overload but not with theDataTable
overload,This is one reason I suggest one generally avoid handling errors in T-SQL unless it provides value and specify
SET XACT_ABORT ON
to avoid continuing after errors and leaving transactions open.