Why variable with nvarchar(max) work incorrect - sql

Currently, I have a function to get list of columns of 1 table with detail attribute. And off course, there are some tables with a lot of columns. So, the output will be over 10.000 characters.
Here I test like this:
declare #aa nvarchar(max)
set #aa = dbo.fnGetColumnList('Table_Name')
print #aa
The result always has around 4000 characters. It looks like the SQL has truncated it.
What I know that when we declare nvarchar(max), SQL will supports up to 2^32-1 (2GB) for this string. But why it just has around 4000 characters?
When I execute like this:
select dbo.fnGetColumnList('Table_Name')
the result is correct.
And here is the code for the function:
-- get column list from table Mapping
ALTER FUNCTION [dbo].[fnGetColumnList] ( #tblName varchar (30))
RETURNS nvarchar(max)
AS
BEGIN
Declare #sql nvarchar(max)
set #sql = ''
SELECT #sql = #sql + case
when CHARINDEX('char', LOWER([DBType])) > 0 then ', ['+[DBColumn]+']' + ' ['+[DBType]+']' + ' ('+convert(varchar(10),[Length])+') NULL' + CHAR(13)
when CHARINDEX('char', LOWER([DBType])) > 0 then ', ['+[DBColumn]+']' + ' ['+[DBType]+']' + ' NULL' + CHAR(13)
ELSE ', ['+[DBColumn]+']' + ' ['+[DBType]+']' + ' NULL' + CHAR(13)
end FROM dbo.Mapping WHERE [DBTable] = #tblName
return #sql
END
Please advance.

This is almost always a variable assignment type problem, as explained in:
For Nvarchar(Max) I am only getting 4000 characters in TSQL?
If it's not that, then it's probably just the settings of Print to display too few characters:
nvarchar(max) still being truncated
Having looked at the updated code, it seems like it's the second issue, your print is truncating as it's not set to show enough characters.
You should see this by running
SELECT LEN(#aa)
You'll get a number larger than 4000, showing the value is held correctly in the variable.

As explained in Microsoft's nvar and nvarchar docs:
A common misconception is to think that with nchar(n) and nvarchar(n), the n defines the number of characters. However, in nchar(n) and nvarchar(n), the n defines the string length in byte-pairs (0-4,000). n never defines numbers of characters that can be stored. This is similar to the definition of char(n) and varchar(n).

There is an option in SQL Management Studio:
Tools > Options... > Query Results > SQL Server > Results to Text > Maximum number of characters displayed in each column

Related

How to convert charater string to uniqueidentifier and use Stuff function?

I'm using function to generate the result. What am I facing now is I pass the ItemGuid as parameter and currently I am using STUFF to find the ItemCode and concatenate result. However I getting an error said that Conversion failed when converting from a character string to uniqueidentifier.
My current result before using STUFF:
From Date : 01-01-2021 to 31-03-2021 Item No: IN ('a70014a3-2e00-41f0-9c3e-6fb8c4f2ab60','26dd67c1-fe37-41fa-b8c5-ff033928a291')
My expected result:
From Date : 01-01-2021 to 31-03-2021 Item No: IN ('ITM001','ITM021')
Please see my fiddle. SQL Fiddle
Parameter used: SELECT[dbo].[func_ReportCriteria2]('2021-01-01','2021-03-31','''a70014a3-2e00-41f0-9c3e-6fb8c4f2ab60'',''26dd67c1-fe37-41fa-b8c5-ff033928a291''') AS 'RESULT 2'
--using STUFF
CREATE FUNCTION [dbo].[func_ReportCriteria2]
(#FromDate DateTime
,#ToDate DateTime
,#Item_List NVARCHAR(MAX)
)
RETURNS nvarchar(max)
AS
BEGIN
DECLARE #CRITERIA NVARCHAR(MAX)
DECLARE #sqlCommand NVARCHAR(MAX)
DECLARE #ItemResult NVARCHAR(MAX)
SET #sqlCommand = ''
IF(ISNULL(#Item_List,'') != '')
BEGIN
--find ItemCode and concatenate based on ItemGuid
--error occur here
SET #ItemResult = STUFF( (SELECT ',' + ItemCode
FROM Item
WHERE ItemGuid IN (#Item_List)
FOR XML PATH('')), 1, 1, '')
SET #sqlCommand = 'Item No: IN ('+ #ItemResult +') '
END
SET #CRITERIA = 'From Date : ' + CONVERT(NVARCHAR(19),#FromDate,105) + ' to ' + CONVERT(NVARCHAR(19),#ToDate,105)
+ CHAR(13)+CHAR(10) + #sqlCommand
RETURN #CRITERIA
END
Please check if this fit your need:
declare #FromDate CHAR(10) ,#ToDate CHAR(10), #Item_List nvarchar(MAX)
select
#FromDate = '2021-01-01',-- make sure to convert the DATE from the table to NVARCHAR using style 120
#ToDate = '2021-03-31',-- make sure to convert the DATE from the table to NVARCHAR using style 120
#Item_List = '''a70014a3-2e00-41f0-9c3e-6fb8c4f2ab60'',''26dd67c1-fe37-41fa-b8c5-ff033928a291'''
------------------ Solution --------------------
-- Important! No reason for scalar function! Use it inline your query directly
SELECT
N'From Date : ' + #FromDate + ' to ' + #ToDate + N' Item No: IN (' + STRING_AGG(''''+ItemName+'''',',') + N')'
FROM Item
WHERE ItemGuid in (
-- Your string includes quotes which we must clear befor CONVERT to GUIDE
SELECT REPLACE([value],'''','') FROM STRING_SPLIT(#Item_List, ',')
)
Note! concat input text to string might be NOT safe and NOT recommended. It has a potential to SQL Injection!
Note: You probably plan a dynamic query, which will SELECT data from the table using the above string, which you asked to build. In this case, this seems like "XY problem" since you probably do not need to build this string at all. You can use the same approach to split string input and execute a direct SELECT query from your table. If we knew what is your final requested result is, then we could help in this as well, but the approach is the same is above code
Try apply convert itemguid
Change WHERE ItemGuid IN (#ItemList)
To WHERE cast(ItemGuid as NVARCHAR(MAX)) IN (#ItemList)

Extract substring from string if certain characters exists SQL

I have a string:
DECLARE #UserComment AS VARCHAR(1000) = 'bjones marked inspection on system UP for site COL01545 as Refused to COD won''t pay upfront :Routeid: 12 :Inspectionid: 55274'
Is there a way for me to extract everything from the string after 'Inspectionid: ' leaving me just the InspectionID to save into a variable?
Your example doesn't quite work correctly. You defined your variable as varchar(100) but there are more characters in your string than that.
This should work based on your sample data.
DECLARE #UserComment AS VARCHAR(1000) = 'bjones marked inspection on system UP for site COL01545 as Refused to COD won''t pay upfront :Routeid: 12 :Inspectionid: 55274'
select right(#UserComment, case when charindex('Inspectionid: ', #UserComment, 0) > 0 then len(#UserComment) - charindex('Inspectionid: ', #UserComment, 0) - 13 else len(#UserComment) end)
I would do this as:
select stuff(#UserComment, 1, charindex(':Inspectionid: ', #UserComment) + 14, '')
This works even if the string is not found -- although it will return the whole string. To get an empty string in this case:
select stuff(#UserComment, 1, charindex(':Inspectionid: ', #UserComment + ':Inspectionid: ') + 14, '')
Firstly, let me say that your #UserComment variable is not long enough to contain the text you're putting into it. Increase the size of that first.
The SQL below will extract the value:
DECLARE #UserComment AS VARCHAR(1000); SET #UserComment = 'bjones marked inspection on system UP for site COL01545 as Refused to COD won''t pay upfront :Routeid: 12 :Inspectionid: 55274'
DECLARE #pos int
DECLARE #InspectionId int
DECLARE #IdToFind varchar(100)
SET #IdToFind = 'Inspectionid: '
SET #pos = CHARINDEX(#IdToFind, #UserComment)
IF #pos > 0
BEGIN
SET #InspectionId = CAST(SUBSTRING(#UserComment, #pos+LEN(#IdToFind)+1, (LEN(#UserComment) - #pos) + 1) AS INT)
PRINT #InspectionId
END
You could make the above code into a SQL function if necessary.
If the Inspection ID is always 5 digits then the last argument for the Substring function (length) can be 5, i.e.
SELECT SUBSTRING(#UserComment,PATINDEX('%Inspectionid:%',#UserComment)+14,5)
If the Inspection ID varies (but is always at the end - which your question slightly implies), then the last argument can be derived by subtracting the position of 'InspectionID:' from the overall length of the string. Like this:
SELECT SUBSTRING(#UserComment,PATINDEX('%Inspectionid:%',#UserComment)+14,LEN(#usercomment)-(PATINDEX('%Inspectionid:%',#UserComment)+13))

Check a word starting with specific string [SQL Server]

I try to search on a string like Dhaka is the capital of Bangladesh which contain six words. If my search text is cap (which is the starting text of capital), it will give me the starting index of the search text in the string (14 here). And if the search text contain in the string but not starting text any of the word, it will give me 0. Please take a look at the Test Case for better understanding.
What I tried
DECLARE #SearchText VARCHAR(20),
#Str VARCHAR(MAX),
#Result INT
SET #Str = 'Dhaka is the capital of Bangladesh'
SET #SearchText = 'cap'
SET #Result = CASE WHEN #Str LIKE #SearchText + '%'
OR #Str LIKE + '% ' + #SearchText + '%'
THEN CHARINDEX(#SearchText, #Str)
ELSE 0 END
PRINT #Result -- print 14 here
For my case, I need to generate #Str with another sql function. Here, we need to generate #Str 3 times which is costly (I think). So, is there any way so that I need generate #Str only one time? [Is that possible by using PATINDEX]
Note: CASE condition appear in the where clause at my original query. So, It is not possible to set the #Str value in variable then use it in the where clause.
Test Case
Search Text: Dhaka, Result: 1
Search Text: tal, Result: 0
Search Text: Mirpur, Result: 0
Search Text: isthe, Result: 0
Search Text: is the, Result: 7
Search Text: Dhaka Capital, Result: 0
Simply add a leading space to the strings to ensure that you always find only the beginning of a word:
DECLARE #SearchText VARCHAR(20),
#Str VARCHAR(MAX),
#Result INT
SET #Str = 'Dhaka is the capital of Bangladesh'
SET #SearchText = 'Dhaka Capital'
SET #Result = CHARINDEX(' ' + #SearchText, ' ' + #Str)
PRINT #Result -- print 14 here
I have tested the above query against your test cases and it seems to work.
To compute the function only once per row in SELECT make it table valued function. Or if it's impossible for some reason use CROSS APPLY
SELECT .. a, b,
FROM ..
CROSS APPLY (SELECT my_scalar_fn(a,b) as Str) arg
WHERE CASE WHEN arg.Str LIKE SearchText + '%'
OR arg.Str LIKE + '% ' + SearchText + '%'
THEN CHARINDEX(SearchText, arg.Str)
ELSE 0 END

Dynamic query does not run on EXEC

In the query below, when I use the print statement the full query prints out as expected and I can pick it up and execute no problems. But if instead of printing it, I run it with EXEC, I get an error which
says incorrect syntax by taking some portion of the query and saying that it's an incorrect identifier, as if the executor just sees a partial query and not the full thing. As you can see, I am using varchar(max), which ought to fit the entire query string. Anyone have any ideas here? Thanks!
declare #RollUp varchar = "hello"
DECLARE #SQL VARCHAR(MAX)
SET #SQL =
'INSERT INTO #RESULT
SELECT CONVERT(VARCHAR,"[Member0].[MEMBER_CAPTION]") AS Zeroth,
CONVERT(VARCHAR,"[Member1].[MEMBER_CAPTION]") AS First,
CONVERT(VARCHAR,"[Member2].[MEMBER_CAPTION]") AS Second,
CONVERT(VARCHAR,"[Member3].[MEMBER_CAPTION]") AS Third,
CONVERT(VARCHAR,"[Member4].[MEMBER_CAPTION]") AS Fourth,
CONVERT(VARCHAR,"[Member5].[MEMBER_CAPTION]") AS Fifth,
CONVERT(VARCHAR,"[Member6].[MEMBER_CAPTION]") AS Sixth,
CONVERT(VARCHAR,"[Member7].[MEMBER_CAPTION]") AS Seventh,
CONVERT(MONEY,"[Measures].[MyMeasure]") AS Eighth
FROM OPENROWSET(''MSOLAP'',''DataSource=MyServer;Initial Catalog=Sales'' ,''
WITH MEMBER [Measures].[MyMeasure]
AS (SUM (StrToMember("[Trans Date].[Year - Quarter - Month - Date].[Month].&["+ Format(Now(),"yyyyMM") + "]").lag(12)
:StrToMember("[Trans Date].[Year - Quarter - Month - Date].[Month].&["+ Format(Now(),"yyyyMM") + "]").lag(1)
,[Measures].[Revenue]))
SELECT NON EMPTY([Measures].[MyMeasure]) on 0,
NON EMPTY({[Commission Category Current].[EP Business Line].[Business Line].members *
[Sales].[Product].members *
[Territory].[Territories].[Territory].members *
[Purchasing Site].[Customers].[Customer].members *
[Purchasing Site].[Cust ID].Children *
[Site].[Customers].[Customer].members *
[Site].[Cust ID].Children} *
[Territory].[Countries].[Territory RollUp].&[''' + #RollUp + ''']
) on 1 FROM SALES
)'''
DECLARE #SQL1 VARCHAR(MAX)= Replace(Replace(#SQL, '[''', '['), ''']', ']')
print #sql1
EXEC #SQL1
The table #Result is not known when you run exec. It is defined in the outer scope but not inherited in the inner scope. You cannot use temporary tables like this, unless they are global temporary tables (preceded by ## instead of just #).
Also, you should never use varchar() in SQL Server without a length. The default length depends on the context and might not be long enough. In other words, you should have a length for varchar() in the convert() statements.
I fixed by breaking up the query in 4 substrings, then at the end just do
EXEC (#STR1 + #STR2 + #STR3 + #STR4).
I'm not sure how a varchar(max) doesn't accept a dynamic string that is clearly not greater than 1000 characters long. Unless the editor is adding hidden chars.
In any event, fixed.

How to format the SQL PRINT messages -- a kind of C printf()?

... Well, not exactly only for PRINT. I need to assign a string variable the value that mixes explicit substrings with integer values (and possibly with other type values). The goal is to get the string for logging.
So far, I use the code like:
DECLARE #msg nvarchar(1000)
...
SET #msg = #procname + 'result = ' + CAST(#result AS nvarchar(5))
+ '; error = ' + CAST(#error AS nvarchar(5))
where the #procname is a string like sp_my_proc:, and the #result and the #error are integer variables. The result should look like (no extra spaces around the numbers, just the minimum length):
sp_my_proc: result = 3; error = 0
The above approach works, but... Is there any better way for converting an integer variable to the string than CAST(#result AS nvarchar(5))? (Consider the magic number 5 being a minor detail to be ignored.)
How do you solve the problem of generating such strings in your code?
Thanks, Petr
In SQL-Server you can use STR() function
http://msdn.microsoft.com/ru-ru/library/ms189527.aspx
The default value for 'length' parameter is 10. Since an integer variable is never longer than 10 symbols (you'd have an overflow) this will always work without errors:
declare #test int
set #test = 333333333
select STR(#test)
Also, take a look at
String.Format like functionality in T-SQL?