How do SQL Server table-valued functions report errors? - sql-server

When I query a SQL Server 2008 system dynamic management view which is implemented as a table-valued function and it returns an empty result set, how can I tell that the reason for the empty result set is that an error occurs in the function, and then, what that error is?

There is a much more useful way to force an error inside a function in TSQL than performing a division by zero. What we do at our company is to cast a string (describing the very problem) and convert it to a string.
if #PersonID is null
insert into #Result values(#Right, cast('FT_AclGetAccess must never be called with #PersonID null' as int))
This will result in an error on the application server looking like this:
Conversion failed when converting the varchar value 'FT_AclGetAccess
must never be called with #PersonID null' to data type int.
A little string manipulation on the application server and you get a pretty sane error message for the log file! ;-)

They don't. You cannot use THROW nor RAISERROR inside T-SQL functions. Some devs force a divide by 0 to trigger an error inside UDFs. This works fine, but sometimes confuses the poor soul that has to investigate a divide by 0 error that comes from apparently nowhere.

Related

SSIS Execute SQL Task - Input String in wrong format

I have an Execute SQL Task which tries to execute a stored procedure, like this:
EXEC usp_stored_proc ?, ?, ? OUTPUT, ? OUTPUT;
I have 4 variables mapped to parameters. Ignoring the output parameters, these are both strings mapped to NVARCHAR params (as expected by the stored procedure).
When I run the package, an error tells me that execution failed with the message input string is not in the correct format. However, when I use a breakpoint to find the runtime values of the input parameters (or at least the variables mapped to them) and execute the same line of SQL in SSMS using the runtime values, it works fine.
Can anyone help? I'm at the end of my tether with this. I can't even find out the exact parameter causing the issue although it's probably both as the values follow the same format.
More details:
Connection type: OLE DB
Input Variable: String = schema.table
Mapped Param: NVARCHAR, ParamName = 0, ParamSize = -1
UPDATE
Solved the issue by making a new execute sql component that calls a stripped down procedure. I then slowly added lines of code to the procedure and additional parameters until arriving at the same component I started with and now it works. Comparing the original and rebuilt tasks, I see absolutely no differences (same with the procedure), so I don't know why this issue was occuring.
Try changing the parameter size (ParamSize) to match the parameter size within the stored procedure; if nvarchar(50) then set it to 50.
Solved the issue by making a new execute sql component that calls a stripped down procedure. I then slowly added lines of code to the procedure and additional parameters until arriving at the same component I started with and now it works. Comparing the original and rebuilt tasks, I see absolutely no differences (same with the procedure), so I don't know why this issue was occurring.

SQL Server 2014: Converting a Decimal(18,0) to a Decimal(18,5)

So, due to a mistake made by a previous person working in a particular database, I have a column that should have been a Decimal(18,5), but is instead a Decimal(18,0). (This is, apparently, SQL Server's default if you don't specify the precision and scale.)
I have added a new column that is of the appropriate type, but several attempts at doing a convert, as below, have failed:
UPDATE [Case].[TableThatWasBuiltWrong] SET NewDecimalValue =
CONVERT(DECIMAL(18,5), OldDecimalValue)
This returns an arithmetic overflow error - which, while strictly correct, is not something I am concerned with (I have verified that none of the data goes above 5 decimal places at present, let alone the intended limit of 13).
Given this situation, how do I get the contents of OldDecimalValue into NewDecimalValue?
(Edited to correct to the SQL Server version in actual use)
There should be nothing wrong if your values are less than or equal to 9999999999999.49999
See example:
DECLARE #val DECIMAL(18,0)=9999999999999.49999
SELECT #val dec1,CAST(#val AS DECIMAL(18,5)) dec2
Will result
But crossing this limit, even staying within 18,5 range, will cause an error.
Following is within limit but, will through an exception:
DECLARE #val DECIMAL(18,0)=9999999999999.59999
SELECT #val dec1,CAST(#val AS DECIMAL(18,5)) dec2
will through:

Assigning value from single row result set in ssis giving error in SSIS 2012

I am trying to get CSV IDs from a table from sql server and assign the result to a variable. below is the sql I have put inside the Execute SQL Task
set nocount on
declare #csv varchar(max) = ''
select #csv = #csv + cast(companyid as varchar(10)) + ',' from company where isprocessed = 0
select substring(#csv,1,len(#csv) - 1) as companyids
As you can see its a simple and standard way of getting csv of a field in t-sql. It works perfectly in query window but throwing below error when I run the Task in SSIS 2012
[Execute SQL Task] Error: The value type (__ComObject) can only be
converted to variables of type Object.
[Execute SQL Task] Error: An error occurred while assigning a value to
variable "sCSVCompanyIds": "The type of the value (DBNull) being
assigned to variable "User::sCSVCompanyIds" differs from the current
variable type (String). Variables may not change type during
execution. Variable types are strict, except for variables of type
Object. ".
Below are the settings of Execute SQL Task
In General tab, Resultset project is set to single row
In Result Set tab, Result Name is set to 0 (I also tried by setting it to csvids which is the alias column name in the select list) and Variable Name is set to User::sCSVCompanyIds
I have no clue why its not working. After wasting so much time I am worked out a hard way which by returning the result as Full Row set (same SQL which returns 1 row 1 column always) and add a for each loop container to loop throw the result set (which will always iterates only once for obvious reasons) and assign the result set's fields to the variable. It works for me but there should be easy way of doing it. What I am missing?
The problem is with the nvarchar(max) data type. I assume it will be same for varchar(max) also. Though relevant data types in SSIS are DT_NTEXT and DT_TEXT are present for some reason we are getting this error.
There are multiple options to handle this.
One Of course there might be more appropriate way to handle this by cast/converting the column within the query to fixed length instead of max something like cast(myvarcharmaxfield as varchar(8000)). In my case it doesn't work because I expect bigger length string. I am generating a csv string from a unique identifier column which itself is 36 chars long string and needs 3 extra chars for quoting them with signle quote and a comma as separator which will support only 205 values. So it doesn't work for me.
So I left with no option but to stick the way I implemented already which is in my question
After wasting so much time I am worked out a hard way which by
returning the result as Full Row set (same SQL which returns 1 row 1
column always) and add a for each loop container to loop throw the
result set (which will always iterates only once for obvious reasons)
and assign the result set's fields to the variable
The new way I learnt is from an unaccepted answer from this question
Change the Oledb connection to ADO.NET connection
I think this is fair enough but it requires me to create another connection manager (so that I don't have to change all existing tasks) and use it for this type of taks where I need csv but I did not buy it as I have very little time to doing research on creating connection string of it which appears to me with the type of erros I am getting is different from oledb.

ColdFusion 10 error with Stored Procedures

In a .CFC file, within a CFfunction and with CFargument tags.
<cfscript>
var sp=new storedproc();
sp.setDatasource(variables.datasource);
sp.setProcedure("storedProcedure_INSERT");
sp.addParam(cfsqltype="cf_sql_integer",type="in",value=arguments.one);
sp.addParam(cfsqltype="cf_sql_integer",type="in",value=arguments.two);
sp.addParam(cfsqltype="cf_sql_integer",type="in",value=arguments.three);
sp.addParam(cfsqltype="cf_sql_integer",type="in",value=arguments.four);
sp.addProcResult(name="results",resultset=1);
//writeDump(sp);break; //This dump is reached
var spObj=sp.execute(); //blows up here; this is never reached
writeDump(spObj);break; //This is never reached, either.
var spResults=spObj.getProcResultSets().results;
A shiny nickle to anyone who can tell me why the sp.execute() is blowing up with message
"Cannot find results key in structure.
The specified key, results, does not exist in the structure."
I've used this psuedo-code many, may times in the past, and never had it do this. I'm connected to a MSSQL Server 2012 DB, everything's cricket in CF Admin, and other SPs are working properly. The stack trace doesn't even include any of MY code at all o_O
The error occurred in C:/ColdFusion10/cfusion/CustomTags/com/adobe/coldfusion/base.cfc: line 491
Called from C:/ColdFusion10/cfusion/CustomTags/com/adobe/coldfusion/storedproc.cfc: line 142
Called from //hq-devfs/development$/websites/myProject/cfc/mySOAPWSDLs.cfc: line 123
And SO is blowing up if I try and paste anymore of that. Google has...not been helpful ._.
Short answer: The error means you are trying to retrieve a resultset from the stored procedure, when it does not actually return one. A simple solution is to add a SELECT to the end of your procedure, so it returns a resultset containing the data you need. Then your original code will work:
SELECT ##ROWCOUNT AS NumOfRowsAffected;
Longer answer:
The method you are using, addProcResult(), is the equivalent of <cfprocresult>. It is intended to capture a resultset returned from a stored procedure. (Due to CF's poor choice of attribute names, a lot of people think "resultset" means the storedproc "result" structure, but they are two totally different things). A "resultset" is a query object", in CF parlance.
While all four (4) of the primary sql statements return some result, not all of them return a "query object"
Only SELECT statements generate a "query object"
INSERT/UPDATE/DELETE statements simply return the number of rows affected. They do not generate a "query object".
Since your stored procedure performs an INSERT, it does not generate a "query object". Hence the error when you try and grab the non-existent query here:
sp.addProcResult(name="results",resultset=1);
The simple solution is to add a SELECT statement to the end of your stored procedure, so that it does return a query object. Then your code will work as expected.
As an aside, I suspect you were actually trying to grab the "result" structure, but used the wrong method. The equivalent of <cfstoredproc result=".."> is getPrefix(). Though that would not work here anyway. According to the docs, it does not contain the number of rows affected. Probably because stored procedures can execute multiple statements, each one potentially returning a row count, so there is not just a single value to return.

Error Handling in User Defined Functions

I want to write a non-CLR user-defined function in SQL Server 2005. This function takes an input string and returns an output string. If the input string is invalid, then I want to indicate an error to the caller.
My first thought was to use RAISERROR to raise an exception. However, SQL Server does not allow this inside a UDF (though you can raise exceptions in CLR-based UDFs, go figure).
My last resort would be to return a NULL (or some other error-indicator value) from the function if the input value is in error. However, I don't like this option, as it:
Doesn't provide any useful information to the caller
Doesn't allow me to return a NULL in response to valid input (since it's used as an error code).
Is there any caller-friendly way to halt a function on an error in SQL Server?
It seems that SQL Server UDF's are a bit limited in this (and many other) way.
You really can't do a whole lot about it - that's (for now) just the way it is. Either you can define your UDF so that you can signal back an error condition by means of its return value (e.g. returning NULL in case of an error), or then you would almost have to resort to writing a stored procedure instead, which can have a lot more error handling and allows RAISERROR and so forth.
So either design your UDF to not require specific signaling of error conditions, or then you have to re-architect your approach to use stored procedures (which can have multiple OUTPUT parameters and thus can also return error code along with your data payload, if you need that), or managed CLR code for your UDF's.
Sorry I don't have a better idea - for now, I'm afraid, those are your options - take your pick.
Marc
There's a possible solution given in an answer to a duplicate question here, based on this idea:
return cast('Error message here.' as int);
Which throws something like this:
Msg 245, Level 16, State 1, Line 1
Conversion failed when converting the varchar value 'Error message here.' to data type int.
It works OK for scalar-valued UDFs, but not for table-valued ones.

Resources