I'd like to do something like this
raiserror(concat('Error in case #isFishy =', #isFishy, ' #isSmarmy=', #isSmarmy, ' #isTasty = ', #isTasty), 10, 1)
--or
raiserror('Error in case #isFishy =' + #isFishy + ' #isSmarmy=' + #isSmarmy + ' #isTasty = ' + #isTasty, 10, 1)
But it just isn't working. How do I accomplish this? I'm in SQL Server 2005.
The error message in RAISERROR has actually similar syntax to printf function in C, so assuming your arguments are of the type of integer you would need to use:
raiserror(N'Error in case #isFishy = %d #isSmarmy = %d #isTasty = %d',10,1,#isFishy,#isSmarmy,#isTasty)
check out BOL for details and other options
I use raiserror a lot. We have some stored procedures that are called from a .Net app each night for batch processing, and the .Net app wants to log the procedure output this way. I don't know why, but I generally have to build the string before calling raiserror.
Related
I update a counter (no autoincrement ... not my database ...) with this FDQuery SQL:
UPDATE CountersTables
SET Cnter = Cnter + 1
OUTPUT Inserted.Cnter
WHERE TableName = 'TableName'
I execute FDQuery.ExecSQL and it works: 'Cnter' is incremented.
I need to retrieve the new 'Counter' value but the subsequent command
newvalue := FDQuery.FieldByName('Cnter').AsInteger
Fails with error:
... EDatabaseError ... 'CountersTables: Field 'Cnter' not found.
What is the way to get that value?
TFDQuery.ExecSQL() is meant for queries that don't return records. But you are asking your query to return a record. So use TFDQuery.Open() instead, eg:
FDQuery.SQL.Text :=
'UPDATE CountersTables' +
' SET Cnter = Cnter + 1' +
' OUTPUT Inserted.Cnter' +
' WHERE TableName = :TableName';
FDQuery.ParamByName('TableName').AsString := 'TableName';
FDQuery.Open;
try
NewValue := FDQuery.FieldByName('Cnter').AsInteger;
finally
FDQuery.Close;
end;
If the database you are connected to does not support OUTPUT, UPDATE OUTPUT into a variable shows some alternative ways you can save the updated counter into a local SQL variable/table that you can then SELECT from.
You have also the RETURNING Unified support Ok, doc only shows INSERT SQL but UPDATE works too.
And I should use a substitution variable for tablename
I’m using static SQL for 99% of the time, but a recent scenario led me to write a dynamic SQL and I want to make sure I didn’t miss anything before this SQL is released to production.
The tables’ names are a combination of a prefix, a 2 letters variable and a suffix and column name is a prefix + 2 letters variable.
First I’ve checked that #p_param is 2 letters length and is “whitelisted”:
IF (LEN(#p_param) = 2 and (#p_param = ‘aa’ or #p_param = ‘bb’ or #p_param = ‘cc’ or #p_param = ‘dd’ or #p_param = ‘aa’)
BEGIN
set #p_table_name = 'table_' + #p_param + '_suffix';
set #sql = 'update ' + QUOTENAME(#p_table_name) + ' set column_name = 2 where id in (1,2,3,4);';
EXEC sp_executesql #sql;
--Here I’m checking the second parameter that I will create the column name with
IF (LEN(#p_column) = 2 and (#p_column = 'ce' or #p_column = 'pt')
BEGIN
Set #column_name = 'column_name_' + #p_column_param;
set #second_sql = 'update ' + QUOTENAME(#p_table_name) + ' set ' +
QUOTENAME(#column_name) + ' = 2 where id in (#p_some_param);';
EXEC sp_executesql #second_sql, N'#p_some_param NVARCHAR(200)', #p_some_param = #p_some_param;
END
END
Is this use case safe? Are there any pitfalls I should be a ware of?
Seems like you've lost some things in the translation to meaningless names to prepare your query to post here, so it's kinda hard to tell. However, the overall approach seems OK to me.
Using a whitelist with QUOTENAME for the identifiers will protect you from SQL injections using the identifiers parameters, and passing the value parameters as a parameter to sp_executeSql will protect you from SQL injections using the value parameters, so I would say you are doing fine on that front.
There are a couple of things I would change, though.
In addition to testing your tables and columns names against a hard coded white list, I would also test then against information_schema.columns, just to make sure that the procedure will not raise an error in case a table or column is missing.
Also, Your whitelist conditions can be improved - Instead of:
IF (LEN(#p_param) = 2 and (#p_param = ‘aa’ or #p_param = ‘bb’ or #p_param = ‘cc’ or #p_param = ‘dd’ or #p_param = ‘aa’)
You can simply write:
IF #p_param IN('aa', 'bb', 'cc','dd')
I would like to take the code below and create a common function to pass in one or more expressions and to return back an error of my choosing.
Example Code:
IF #Variable1 IS NULL AND #Variable IS NULL or #Variable3 is not null
BEGIN
-- EITHER DATASET NAME OR ID MUST BE SUPPLIED.
SET #_msg = 'There was an error'
SET #_returnValue = -1
GOTO ERROR_HANDLER
END
ERROR_HANDLER:
-- CREATE THE CLOSING MESSAGE.
IF #_returnValue <> 0
RAISERROR(#_msg, 18, 2) WITH SETERROR
RETURN #_returnValue
From the above, it would be nice to say something like this below where I could reuse the proc/function and make the code less clutered.
exec ValidateMultipleConditions #Variable1 + 'IS NULL AND ' + #Variable + 'IS NULL or ' + #Variable3 + ' is not null'
Anyway, I think with dynamic SQL being passed in this way I could do something where an complete expression could be sent evaluated, validated and then the code continues or stops with an error.
I wanted to see if the community had better ways of doing this or if I'm on the right path.
Thanks.
I'm not quite sure if I get your question right. But you can easily create an procedure (if it's needed).
CREATE PROCEDURE dbo.errorout #message nvarchar(100), #sev int, #state int
AS
BEGIN
RAISERROR(#message,#sev,#state) WITH NOWAIT
END
But I won't use this at all. I would call RAISERROR() in the place where it occurs, as it will give you more accurate line numbers and procedures in the errorlog.
where a.system_nr =''''5300'''' and
a.external_status_cd = '''''''' and
a.cust_acct_id = b.rel_cust_acct_id and
b.cust_acct_id = c.cust_acct_id and
c.cust_acct_id = d.cust_acct_id and
d.acct_status_cd = ''''OPEN'''' and
d.time_mnth_gen_id =''''' + #BegDate + ''''' and
a.cust_acct_id = e.cust_acct_id and
e.tran_dt >=''''' + #BegDate + ''''' and
e.tran_dt<=''''' + #EndDate + ''''' and
d.portfolio_cd = ''''HEQ'''' and
a.time_mnth_gen_id =''''' + #BegDate + ''''' '')'
Here is the where condition which is already written and I need to make changes.
Can you please tell me why they are using '''''+#begdate'''''? Can i use '+Bedate'?
I mean why they are using ''''' each side?
Try this in SQL Server:
select '''''someval'''''
You notice that item gives:
''someval''
In SQL Server '' will equate to a single quote character, so the above line is
select [open string][single quote][single quote]someval[single quote][single quote][close string]
Without seeing the rest of the SQL, my guesses would be:
for use in dynamic SQL as #BegDate is a variable and you have the statement ending with a single quote
the data contains a bunch of single quotes
You should not be able to just '+BegDate' because it's a variable and stripping the # would cause it to be evaluated as a field.
If you meant to just reduce the number of single quotes, I would imagine the original author put them there for a reason. You can run the query with the original single quotes and again with the reduced single quotes and see if you get the same result set.
I have the following CLR function:
[Microsoft.SqlServer.Server.SqlFunction]
public static SqlString PrintText(SqlString text)
{
// Put your code here
return new SqlString(text.Value);
}
And I want to get an enter symbol when passing '\r\n'
to it. But insteat I get '\r\n'
Could you please tell me what's wrong with this code.
Thank you.
In T-SQL you don't write a line break as \r\n. Instead you just use a line break:
'this
is a
string
in SQL
with
line breaks'
If you pass a string with \r\n to the C# code, nothing magical happens, it doesn't automatically get converted. The backslash character is just a character like any other. It's when you use the backslash in a literal string in the code that the compiler uses it as an escape code, and puts the control characters in the actual string.
You could always:
return new SqlString(text.Value.Replace("\\r\\n", "\r\n"));
From the SQL side, you could insert char(13) + char(10) into your T-SQL literals like so:
DECLARE #text varchar(100)
SET #test = 'this' + char(13) + char(10)
+ 'is a' + char(13) + char(10)
+ 'string' + char(13) + char(10)
+ 'in SQL' + char(13) + char(10)
+ 'with' + char(13) + char(10)
+ 'line breaks'
Though this works, it is much more verbose than Guffa's answer.
However, this technique can also be used to insert any character of the default code page into a string. The Function CHAR(int) accepts any integer between 0 and 255. Values outside that range cause it to return null. The function NCHAR(int) accepts values up to 65535 and inserts the corresponding unicode characters into a unicode string. Functions ASCII(char) and UNICODE(nchar) perform the inverse operations.