Dynamic query does not run on EXEC - sql

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.

Related

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))

Charindex not working when using two variables

I'm using two cursors in SQL Server 2008 R2 to work with two lists of values returned from Select statements. I read the first value from list tofind1 into a variable find1 and a value from list tosearch1 into a variable search1.
The underlying variable types in the tables are nvarchar of length 10 and 255 respectively. The variables are defined as the same length nvarchar.
I'm testing it with two strings similar to this:
find1 = 'bill'
search1 = 'bill,fred,mary'
When I try charindex(find1, search1) it returns 0. This is incorrect.
When I try charindex('bill',search1) it returns 1. This is correct.
When I just run the following code, both pairs of statements work fine:
DECLARE #search1 nvarchar(255);
DECLARE #find1 nvarchar(10);
set #find1 = 'bill'
set #search1 = 'bill,fred,mary'
select charindex(#find1,#search1)
select charindex('bill','bill,fred,mary')
if 'bill,fred,mary' like '%bill%'
print 'Yay'
if #search1 like '%' + #find1 + '%'
print 'Yes'
Is there something about selecting values into a cursor then assigning it to a variable that messes with its data type so CHARINDEX and LIKE won't work? I've trying using CAST functions to triply make sure that the variables are all seen as nvarchar.

Why variable with nvarchar(max) work incorrect

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

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?

SQL Server substring breaking on words, not characters

I'd like to show no more than n characters of a text field in search results to give the user an idea of the content. However, I can't find a way to easily break on words, so I wind up with a partial word at the break.
When I want to show: "This student has not submitted his last few assignments", the system might show: "This student has not submitted his last few assig"
I'd prefer that the system show up to the n character limit where words are preserved, so I'd like to see:
"This student has not submitted his last few"
Is there a nearest word function that I could write in T-SQL, or should I do that when I get the results back into ASP or .NET?
If you must do it in T-SQL:
DECLARE #t VARCHAR(100)
SET #t = 'This student has not submitted his last few assignments'
SELECT LEFT(LEFT(#t, 50), LEN(LEFT(#t, 50)) - CHARINDEX(' ', REVERSE(LEFT(#t, 50))))
It will not be catastrophically slow, but it will definitely be slower than doing it in the presentation layer.
Other than that — just cutting off the word and appending an ellipsis for longer strings is no bad option either. This way at least all truncated strings have the same length, which might come in handy if you are formatting for a fixed-width output.
I agree with doing this outside of the database that way other applications with different length restrictions can make their own decisions on what to show/hide. Perhaps that can be a parameter to the database call though.
Here's a quick stab at a solution:
DECLARE #OriginalData NVARCHAR(MAX)
,#ReversedData NVARCHAR(MAX)
,#MaxLength INT
,#DelimiterPosition INT ;
SELECT #OriginalData = 'This student has not submitted his last few assignments'
,#MaxLength = 45;
SET #ReversedData = REVERSE(
LEFT(#OriginalData, #MaxLength)
);
SET #DelimiterPosition = CHARINDEX(' ', #ReversedData);
PRINT LEFT(#OriginalData, #MaxLength - #DelimiterPosition);
/*
This student has not submitted his last few assignments
1234567890123456789012345678901234567890123456789012345
*/
I recommend doing that kind of logic outside database. With C# it could look similar to this:
static string Cut(string s, int length)
{
if (s.Length <= length)
{
return s;
}
while (s[length] != ' ')
{
length--;
}
return s.Substring(0, length).Trim();
}
Of cause you could do this with T-SQL, but that is bad idea (bad performance etc.). If you really need to put it inside DB I would use CLR-based stored procedure instead.
I'd like to add to the solutions already offered that word breaking logic is a lot more complicated than it seems on the surface. To do it well you are going to need to define a number of rules for what constitutes a word. Consider the following:
Spaces - No brainer.
Hyphens - Well that depends. In Over-exposed proably, in re-animated probably not. Then what about dates such as 01-02-1985?
Periods - No brainer. Oh wait, what about the one in myemail#myisp.com or $79.95?
Commas - In numbers such as 1,239 no, but in sentences yes.
Apostrophes - In O'Reily no, in SQL is an 'Enterprise' Database tool yes.
Do special characters alone constitute words?: In Item 1 : Buy TP is the colon counted as a word?
I found an answer on this site and modified it:
the cast (150) must be greater than the number of characters you're returning (100)
LEFT (Cast(myTextField As varchar(150)),
CHARINDEX(' ', CAST(flag_myTextField AS VARCHAR(150)), 100) ) AS myTextField_short
I'm not sure how fast this will run, but it will work....
DECLARE #Max int
SET #Max=??
SELECT
REVERSE(RIGHT(REVERSE(LEFT(YourColumnHere,#Max)),#Max- CHARINDEX(' ',REVERSE(LEFT(YourColumnHere,#Max)))))
FROM YourTable
WHERE X=Y
I wouldn't advice to do that either, but if you must, you can do something like this:
DECLARE #text nvarchar(max);
DECLARE #end_char int;
SELECT #text = 'This student has not submitted his last few assignments', #end_char = 50 ;
WHILE #end_char > 0 AND SUBSTRING( #text, #end_char+1, 1 ) <> ' '
SET #end_char = #end_char - 1
SELECT #text = SUBSTRING( #text, 1, #end_char ) ;
SELECT #text