How to test tsql variable contents as expression [duplicate] - tsql

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
How to execute mathematical expression stored in a varchar variable
Using pure TSQL, is it possible to evaluate an expression defined to a variable?
Here is a simple example, its only for proof in concept:
DECLARE #TestValue AS VARCHAR (2) = '7';
DECLARE #myRule AS VARCHAR (100) = 'case when (#val#>0 and #val#<10) then 1 else 0 end';
SET #myRule = replace(#myRule, '#val#', #TestValue);
PRINT #myRule;
-- case when (7>0 and 7<10) then 1 else 0 end
--How to evaluate the expression in #myRule for True/False?

--Based on How to execute mathematical expression stored in a varchar variable
DECLARE #out AS BIT, #sql AS NVARCHAR (4000);
SET #sql = N'SELECT #out = ' + #myRule;
EXECUTE sp_executesql #sql, N'#out bit OUTPUT', #out OUTPUT;
PRINT 'sp_executesql=' + CAST (#out AS VARCHAR (10));
--sp_executesql=1
I've tested this approach and it seems to be sound. Not sure if there are performance considerations, but for now I'll mark as answered.

Related

Firebird's execute statement with bigint named input parameter

Let's say I have a code like this:
execute block
as
declare var_mask bigint;
declare var_dummy int;
begin
var_mask = bin_shl(1, (64 - 1));
execute statement ('
select first 1 null
from rdb$database
where bin_and(cast(0 as bigint), :var_mask) <> cast(0 as bigint)
')
(var_mask := var_mask)
into :var_dummy
;
end
This one gives nice arithmetic exception, numeric overflow, or string truncation.
numeric value is out of range..
To make it work I have to do explicit cast of the variable:
execute block
as
declare var_mask bigint;
declare var_dummy int;
begin
var_mask = bin_shl(1, (64 - 1));
execute statement ('
select first 1 null
from rdb$database
where bin_and(cast(0 as bigint), cast(:var_mask as bigint)) <> cast(0 as bigint)
')
(var_mask := var_mask)
into :var_dummy
;
end
Does anybody know why? The type information should carry, isn't it?
Because BIN_AND describes the second parameter as INTEGER, even when you pass a BIGINT to the first one. Whether this is good or bad is subject to discussion.
To add to Adriano's answer the type information actually does not carry - more here, from me actually :).

Dynamic SQL Not Converting VARCHAR To INT (shouldn't anyway)

I'm receiving an error:
Conversion failed when converting the varchar value 'INSERT INTO TableRowCount (IntFieldID, DecimalField) SELECT 'to data type int"
Using the following code:
DECLARE #start INT -- #start is an INT
SET #start = 1 -- INT
DECLARE #sql NVARCHAR(MAX)
SET #sql = 'INSERT INTO TableRowCount (IntFieldID, DecimalField)
SELECT ' + #start +', COUNT(*)
FROM dbo.somewhere' -- location is irrelevant
EXECUTE(#sql) -- this is where it fails
If I remove IntFieldID and the #start, it will work with an insert (though it defeats the purpose). I've tried including a SELECT CAST(' + #start + ' AS INT), which seems a little redundant since #start is an INT already (casting an INT as an INT), but that doesn't work either. I also tried beginning with an N' DYNAMIC-SQL, which didn't work, I tried using three ''' around everything (didnt' work), and in a few places that I read online, responses suggested putting the variable in the string, which generated the error:
Must declare scalar variable #start
(no surprise, as that didn't sound correct).
A better way than trying to concatenate an integer is to pass it in as a strongly-typed parameter:
DECLARE #start INT = 1;
DECLARE #sql NVARCHAR(MAX) = N'INSERT ...
SELECT #start, COUNT(*) FROM ' + #conn;
EXEC sp_executesql #sql, N'#start INT', #start;
You need to convert your #Start to a varchar.
DECLARE #sql NVARCHAR(MAX)
SET #sql = 'INSERT INTO TableRowCount (IntFieldID, DecimalField)
SELECT ' + CAST(#start as nvarchar(20)) +', COUNT(*)
FROM ' + #conn
SQL Server implicitly converts between datatypes on concatenation or addition based on some fairly complex criteria. Suffice to say if you try to combine an int and a string it will always attempt to convert the string to an int unless you tell it otherwise explicitly.
Below is a conversion chart for your reference from MSDN.

PostgreSQL: Treat TEXT column as hexadecimal number [duplicate]

This question already has answers here:
Convert hex in text representation to decimal number
(9 answers)
Closed 5 years ago.
I have a table that has a TEXT column which contains hexadecimal numbers.
I now need to represent them as the integers that they really are, but found no
way of doing that (something like to_hex() in reverse).
I am aware that I can convert literal hexadecimal values like this:
SELECT x'DEADBEEF';
But how do I apply something like this if the value to be converted comes from a
column? Concatenating 'x' to the column name obviously doesn't work, because then
it is no longer a string literal.
I found a very ugly function in the PostgreSQL mailing lists which
pieces together a query string such that the function argument is then again a
literal, and then executes that function, but the approach is just downright
perverse---there has to be a better way. At least I hope so, given that the
message is almost ten years old…
Of course, I know that having the value in question stored as an integer in the
database in the first place would be the way to go. But this is not possible in
this case, so I'm stuck with trying to decypher those strings…)
The following functions are roughly the same as the function in that post from the mailing list. In fact, I took them from the mailing list too, but from a newer post. I can't see anything wrong with them. I've used it just once while migrating a small dataset.
Feel free to downvote if you can point anything "perverse" that could potentially derive from its use.
With INTEGER datatype:
CREATE OR REPLACE FUNCTION hex_to_int(hexval varchar) RETURNS integer AS $$
DECLARE
result int;
BEGIN
EXECUTE 'SELECT x''' || hexval || '''::int' INTO result;
RETURN result;
END;
$$
LANGUAGE 'plpgsql' IMMUTABLE STRICT;
With BIGINT datatype:
CREATE OR REPLACE FUNCTION hex_to_bigint(hexval varchar) RETURNS bigint AS $$
DECLARE
result bigint;
BEGIN
EXECUTE 'SELECT x''' || hexval || '''::bigint' INTO result;
RETURN result;
END;
$$
LANGUAGE 'plpgsql' IMMUTABLE STRICT;
Hm, there might be a simpler way to do it.
CREATE FUNCTION from_hex(text) RETURNS integer AS $$
DECLARE
x bytea;
BEGIN
x := decode($1, 'hex');
return (get_byte(x, 0) << 24) | (get_byte(x, 1) << 16) |
(get_byte(x, 2) << 8) | get_byte(x, 3);
END
$$ LANGUAGE plpgsql;
Note that as written, this only works for 8-digit hexadecimal numbers.

T-SQL VARCHAR(MAX) Truncated

DECLARE #str VARCHAR (MAX);
SELECT #str = COALESCE(#str + CHAR(10), '') +
'EXECUTE CreateDeno ' + CAST(ID AS VARCHAR)
FROM GL_To_Batch_Details
WHERE TYPE = 'C' AND
Deno_ID IS NULL;
--PRINT #str;--SELECT #str;
**EXEC(#str);**
EDITED
Does EXECUTE statement truncate strings to 8,000 chars like PRINT? How can I execute a dynamic SQL statement having more than 8,000 chars?
Any suggestion would be warmly appreciated.
PRINT is limited to 8k in output.
There is also an 8k limit in SSMS results pane.
Go to
tools -> options -> query results
to see the options.
To verify the length of the actual data, check:
SELECT LEN(#str)
When concatenating strings and the result is of type VARCHAR(MAX) and is over 8000 characters, at least one parameter and/or element being used in the concatenation need to be of the VARCHAR(MAX) type otherwise truncation will occur in the resultant string and will not be executable in an EXEC statement.
Example:
DECLARE #sql AS VARCHAR(MAX);
/* DECLARE #someItem AS VARCHAR(100); -- WILL CAUSE TRUNCATION WHEN #sql HAS LEN > 8000 */
DECLARE #someItem AS VARCHAR(MAX); -- All string variables need to be VARCHAR(MAX) when concatenating to another VARCHAR(MAX)
SET #someItem = 'Just assume the resulting #sql variable goes over 8000 characters...';
SET #sql = 'SELECT Something FROM Somewhere WHERE SomeField = ''' + #someItem + '''';
EXEC (#sql);
--PRINT #sql;
More information on MSDN.
"If the result of the concatenation of strings exceeds the limit of
8,000 bytes, the result is truncated. However, if at least one of the
strings concatenated is a large value type, truncation does not
occur."
The default length of a varchar is 30 characters:
CAST (ID AS VARCHAR)
Is it possible that id is longer than 30 characters?
The PRINT command is certainly limited to 8000 chars, irrespective of the length of the output (or whether it is varchar(max)). To work around this you need to output the string in chunks of <8000 chars
Update: In answer to your edit, exec doesn't limit the string length. I've put together the following example to show this:
DECLARE #str VARCHAR (MAX);
;WITH CTE_Count AS
(
select counter = 1
union all
select counter = counter+1
from CTE_Count
Where counter < 2000
)
SELECT
#str=COALESCE(#str + CHAR (10) ,
'' ) + 'select value=' + CAST (counter AS VARCHAR)
from
CTE_Count
Option (MAXRECURSION 0)
PRINT len(#str);--SELECT #str;
exec (#str)
Running this prints the length as 34892 chars, and all 2000 execute statements do run (be warned, it may take a few mins!)
It happens when you concatenate literals if one is not a varchar(max) the result ill be "implicit casted" to varchar(8000).
To generate a literal varchar(max) all parts must be varchar(max).
Note: It happened to me doing updates on varchar(max) columns, never tested with the EXEC command.
Also as noted in previous answers the print command holds a limit but you can try selecting that variable instead of printing it. (also ther's a limit on that select length you can configure on MS-SMS)
I also wanted to see what I was sending to Exec, and was confused by the PRINT limit. Had to write a proc to print in chunks.
CREATE PROCEDURE [dbo].[KFX_PrintVarcharMax]
#strMax varchar(max)
AS
BEGIN
SET NOCOUNT ON;
DECLARE
#index int = 0,
#start int = 1,
#blkSize int = 2000;
WHILE #Start < LEN(#strMax)
BEGIN
IF #start + #blkSize >= LEN(#strMax)
BEGIN
-- If remainder is less than blocksize print the remainder, and exit.
PRINT SUBSTRING(#strMax, #start, #blkSize)
BREAK;
END
-- Else find the next terminator (beyond the blksize)
SET #index = CHARINDEX(CHAR(10), #strMax, #start + #blkSize);
if #index >= #start
BEGIN
PRINT SubString(#strMax, #start, #index - #start + 1)
SET #start = #index + 1;
SET #blkSize = CASE WHEN #start + 2000 < LEN(#strMax) THEN 2000
ELSE LEN(#strMax) - #start + 1 END
END
ELSE
BEGIN
-- No char(10) found. Just print the rest.
PRINT SUBSTRING(#strMax, #start, LEN(#strMax))
BREAK;
END
END
END

TSQL How do you output PRINT in a user defined function?

Basically I want to use PRINT statement inside a user defined function to aide my debugging.
However I'm getting the following error;
Invalid use of side-effecting or time-dependent operator in 'PRINT'
within a function.
Can this not be done?
Anyway to aid my user defined function debugging?
Tip:
generate error.
declare #Day int, #Config_Node varchar(50)
set #Config_Node = 'value to trace'
set #Day = #Config_Node
You will get this message:
Conversion failed when converting the varchar value 'value to trace'
to data type int.
No, sorry. User-defined functions in SQL Server are really limited, because of a requirement that they be deterministic. No way round it, as far as I know.
Have you tried debugging the SQL code with Visual Studio?
I got around this by temporarily rewriting my function to something like this:
IF OBJECT_ID ('[dbo].[fx_dosomething]', 'TF') IS NOT NULL
drop function [dbo].[fx_dosomething];
GO
create FUNCTION dbo.fx_dosomething ( #x numeric )
returns #t table (debug varchar(100), x2 numeric)
as
begin
declare #debug varchar(100)
set #debug = 'printme';
declare #x2 numeric
set #x2 = 0.123456;
insert into #t values (#debug, #x2)
return
end
go
select * from fx_dosomething(0.1)
I have tended in the past to work on my functions in two stages. The first stage would be to treat them as fairly normal SQL queries and make sure that I am getting the right results out of it. After I am confident that it is performing as desired, then I would convert it into a UDF.
Use extended procedure xp_cmdshell to run a shell command. I used it to print output to a file:
exec xp_cmdshell 'echo "mytextoutput" >> c:\debuginfo.txt'
This creates the file debuginfo.txt if it does not exist. Then it adds the text "mytextoutput" (without quotation marks) to the file. Any call to the function will write an additional line.
You may need to enable this db-server property first (default = disabled), which I realize may not be to the liking of dba's for production environments though.
No, you can not.
You can call a function from a stored procedure and debug a stored procedure (this will step into the function)
On my opinion, whenever I want to print or debug a function. I will copy the content of it to run as a normal SQL script. For example
My function:
create or alter function func_do_something_with_string(#input nvarchar(max)) returns nvarchar(max)
as begin
-- some function logic content
declare #result nvarchar(max)
set #result = substring(#input , 1, 10)
-- or do something else
return #result
end
Then I just copy and run this out of the function to debug
declare #input nvarchar(max) = 'Some string'
-- some function logic content
declare #result nvarchar(max)
set #result = substring(#input , 1, 10)
-- this line is added to check while debugging
print #result
-- or do something else
-- print the final result
print #result
You can try returning the variable you wish to inspect.
E.g. I have this function:
--Contencates seperate date and time strings and converts to a datetime. Date should be in format 25.03.2012. Time as 9:18:25.
ALTER FUNCTION [dbo].[ufn_GetDateTime] (#date nvarchar(11), #time nvarchar(11))
RETURNS datetime
AS
BEGIN
--select dbo.ufn_GetDateTime('25.03.2012.', '9:18:25')
declare #datetime datetime
declare #day_part nvarchar(3)
declare #month_part nvarchar(3)
declare #year_part nvarchar(5)
declare #point_ix int
set #point_ix = charindex('.', #date)
set #day_part = substring(#date, 0, #point_ix)
set #date = substring(#date, #point_ix, len(#date) - #point_ix)
set #point_ix = charindex('.', #date)
set #month_part = substring(#date, 0, #point_ix)
set #date = substring(#date, #point_ix, len(#date) - #point_ix)
set #point_ix = charindex('.', #date)
set #year_part = substring(#date, 0, #point_ix)
set #datetime = #month_part + #day_part + #year_part + ' ' + #time
return #datetime
END
When I run it.. I get:
Msg 241, Level 16, State 1, Line 1
Conversion failed when converting date and/or time from character string.
Arghh!!
So, what do I do?
ALTER FUNCTION [dbo].[ufn_GetDateTime] (#date nvarchar(11), #time nvarchar(11))
RETURNS nvarchar(22)
AS
BEGIN
--select dbo.ufn_GetDateTime('25.03.2012.', '9:18:25')
declare #day_part nvarchar(3)
declare #point_ix int
set #point_ix = charindex('.', #date)
set #day_part = substring(#date, 0, #point_ix)
return #day_part
END
And I get '25'. So, I am off by one and so I change to..
set #day_part = substring(#date, 0, #point_ix + 1)
Voila! Now it works :)