Dynamic sql is truncated with exec attempted - tsql

Here is my SP:
alter PROCEDURE [dbo].[_spwf_Deps](#process_name varchar(64), #deps varchar(250))
AS
BEGIN
DECLARE #loop varchar(max);
SET #deps = REPLACE(#deps, ',', '= 1 AND ');
SET #loop = '''WHILE (select count(*) from dbo.tblDeps where Process_name =
''''#process_name'''' and #deps = 1) = 0 BEGIN WAITFOR DELAY ''''00:00:02'''' break; END;''';
SET #loop = REPLACE(REPLACE(#loop, '#process_name', #process_name), '#deps', #deps);
print #loop;
exec (#loop);
END;
The PRINT statement prints things out just fine and I can exec that string.
But the exec command fails with this error:
Incorrect syntax near 'WHILE (select count(*) from dbo.tblDeps where Process_name = 'proc' and dep_one= 1 AND dep_two = 1) = 0 BEGIN WAITF'.
As you can see, the string is truncated and hence the error.
#loop is varchar(MAX) so everything should fit.
Any ideas/suggestions welcome.
Thanks!

I was able to closely recreate the error. Remove the extra quotes from #loop and you should be good.
SET #loop = 'WHILE (select count(*) from dbo.tblDeps where Process_name =
''#process_name'' and #deps = 1) = 0 BEGIN WAITFOR DELAY ''00:00:02'' break; END;';

Related

SQL Server 2012: check if is uniqueidentifier and convert to string

I am trying to test if a variable is a uniqueidentifier and if it is then convert it to string but it fails:
Declare #Item VARCHAR(50)
Declare #OutString varchar(max) ;
--#Outstring is populated from various tables each cell separated by ','
--Getting the #Item from the #Outstring and convert it if its uid
DECLARE #Pos INT
DECLARE #Loop BIT
SELECT #Loop = CASE WHEN LEN(#OutString) > 0 THEN 1 ELSE 0 END
WHILE (SELECT #Loop) = 1
BEGIN
SELECT #Pos = CHARINDEX(',', #OutString, 1)
IF #Pos > 0
BEGIN
SELECT #Item = SUBSTRING(#OutString, 1, #Pos - 1)
SELECT #OutString = SUBSTRING(#OutString, #Pos + 1, LEN(#OutString) - #Pos)
IF (TRY_CONVERT(UNIQUEIDENTIFIER, #Item) IS NOT NULL)
BEGIN
CONVERT(NVARCHAR(50), #Item) AS #Item --ERROR LINE incorrect syntax
END
END
END
it is either
select #Item = convert(nvarchar(50), #Item)
or
select #Item = cast(#Item as nvarchar(50))
The syntax error is because you have said the action but not what SQL should do with it.
Do you want the string to be returned?
SELECT CONVERT(NVARCHAR(50), #Item) AS Item
Appended to #Item? (but this variable is inside your loop?)
SET #Item += CONVERT(NVARCHAR(50), #Item)
Not sure what you want to do once you have converted the string. Maybe you need another variable to add the string on (like above except not SET #Item)
Use
SELECT convert(nvarchar(50), #Item ) as Item

postgresql execute dynamic sql command

I'm slowly learning more about PostgreSQL, as we are attempting to move to it from MSSQL Server.
In MSSQL I have the following code:
DECLARE ServiceabilityParameters
CURSOR FORWARD_ONLY READ_ONLY STATIC LOCAL FOR
SELECT WorkbookParameterType.ID,
WorkbookParameterType.Name,
WorkbookParameter.DefaultValue,
WorkbookParameter.CommandText
FROM WorkbookParameter
JOIN WorkbookParameterType ON WorkbookParameterType.ID = WorkbookParameter.WorkbookParameterTypeID
JOIN WorkbookParameterDirectionType ON WorkbookParameterDirectionType.ID = WorkbookParameter.WorkbookParameterDirectionTypeID
AND WorkbookParameterDirectionType.Writable = 1
WHERE WorkbookParameter.WorkbookID = #WorkbookID
OPEN ServiceabilityParameters
FETCH NEXT FROM ServiceabilityParameters INTO #WorkbookParameterTypeID, #WorkbookParameterTypeName, #WorkbookDefaultValue, #WorkbookCommandText
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #ActualValue NVARCHAR(256) = NULL
IF #WorkbookCommandText IS NOT NULL
BEGIN
EXEC sp_executesql #statement = #WorkbookCommandText,
#params = N'#ApplicationContainerID INT, #Value NVARCHAR(256) OUTPUT',
#ApplicationContainerID = #ApplicationContainerID,
#Value = #ActualValue OUTPUT
END
IF #ActualValue IS NULL AND #WorkbookDefaultValue IS NOT NULL
BEGIN
SET #ActualValue = #WorkbookDefaultValue
END
INSERT #InputParameters (
ID, Name, Value
) VALUES (
#WorkbookParameterTypeID, #WorkbookParameterTypeName, #ActualValue
)
FETCH NEXT FROM ServiceabilityParameters INTO #WorkbookParameterTypeID, #WorkbookParameterTypeName, #WorkbookDefaultValue, #WorkbookCommandText
END
CLOSE ServiceabilityParameters
DEALLOCATE ServiceabilityParameters
I'm trying to work out how to do the sp_executesql part in a PostgreSQL function. I believe that I can do the rest, but most of the examples that I have found show a simple select with maybe a few variables, whereas I need to execute another function, with parameters, where the function name is text in a table.
Many Thanks.
In case you want to execute a function with parameters
EXECUTE 'SELECT Value FROM ' || v_workbookCommandText || '(ApplicationContainerID :=$1)'
INTO v_actualValue
USING v_applicationContainerID;
In case you need select records a function, you can using INOUT refcursor variable
EXECUTE 'SELECT Value FROM ' || v_workbookCommandText || '(ApplicationContainerID :=$1, refcur:= $2)'
INTO v_actualValue
USING v_applicationContainerID, my_cursor;
I think what you want to do is EXECUTE 'some string', like this:
EXECUTE 'SELECT count(*) FROM mytable WHERE inserted_by = $1 AND inserted <= $2'
INTO c
USING checked_user, checked_date;
Another option is to create and use your own PL/PGSQL functions.

Dynamic SQL with nvarchar(max) variable (again)

I have read about a dozen articles here and I am still stumped with this issue.
I am building a dynamic select statement that will update a view on a monthly schedule.
set ansi_nulls on
go
set quoted_identifier on
go
alter procedure [dbo].[Proc_Name_SP]
as
begin
set nocount on
set quoted_identifier off
declare #dbname varchar(10), #schema_id int, #schema_name varchar(10),
#jacro varchar(10), #rec_cnt int, #tot_rec int
declare #SQL_Main nvarchar(max), #SQL_Final nvarchar(max),
#SQL_schema nvarchar(2000), #SQL_Union nvarchar(max)
declare iteration cursor global static for
-- Begin statement for cursor array
select distinct db, code
from linkedserver.db.schema.Directory
where current_stage = 'live'
order by db
-- End statement for cursor array
-- get total number of cursor iterations to know when to stop
-- "union" statements
select #tot_rec = count(*) from (select distinct db, code
from [linkedserver].db.schema.Directory
where current_stage = 'live') as cur
-- begin loop
open iteration
fetch first from iteration into #dbname, #jacro
while ##fetch_status=0
begin
-- the schema used is not consistent. Because of the linked server it was
-- necessary to get the Schema_ID from the sys.tables and then pull the
-- schema name from sys.schema
set #SQL_schema = 'select #sch_id = schema_id from [linkedserver].'+#dbname+'.sys.tables where name = ''Manuscript'''
execute sp_executesql #SQL_schema, N'#sch_id int OUTPUT', #sch_id = #schema_id output
--print #schema_id
set #SQL_schema ='select #sch_name = name from [linkedserver].'+#dbname+'.sys.schemas where schema_id = '+cast(#schema_id as varchar)+''
execute sp_executesql #SQL_schema, N'#sch_name nvarchar(10) OUTPUT', #sch_name = #schema_name output
--print #schema_name
--building Select statement
set #SQL_Main ='
select jcode.Code as BILLING_ACRO
,s.start_dt as BILLING_DATE
,cmpt_ms_nm as MANUSCRIPT
,isnull(jcode.billing_type, ''reviewed'') as Billing_type
from [linkedserver].'+#dbname+'.'+#schema_name+'.Manuscript as m
join [linkedserver].'+#dbname+'.'+#schema_name+'.Step as s on m.ms_id = s.ms_id and m.ms_rev_no = s.ms_rev_no
join (select j_id, Code, billing_type from [linkedserver].db.schema.Directory where db = '''+#dbname+''') as jcode on jcode.j_id = m.j_id
where jcode.Code = '''+#jacro+'''
and m.ms_rev_no = 0
and s.stage_id = 190
and isnull(cmpt_ms_nm, '''') <> ''''
and s.step_id = (select min(s2.step_id)
from [linkedserver].'+#dbname+'.'+#schema_name+'.Step as s2
where s2.stage_id = 190
and s2.ms_id = m.ms_id
and s2.ms_rev_no = m.ms_rev_no)
'
set #rec_cnt = isnull(#rec_cnt, 0) + 1
if #SQL_Union is null
begin
set #SQL_Union = #SQL_Main
end
else if #tot_rec <> #rec_cnt
begin
set #SQL_Union = #SQL_Union + ' union ' + #SQL_Main
end
else
begin
set #SQL_Union = #SQL_Union + #SQL_Main
end
--print #rec_cnt
fetch next from iteration into #dbname, #jacro --next database
end -- while ##FETCH_STATUS=0
close iteration
deallocate iteration
-- build new view
print len(#SQL_Union)
set #SQL_Final = '
ALTER VIEW [dbo].[View_Name_VW]
AS
'+#SQL_Union+'
'
execute sp_executesql #SQL_Final
--grab string variables to table for troubleshooting
insert into Output_SQL(SQL_Final, SQL_Final_Len, SQL_Union, SQL_Union_Len)
select #SQL_Final, LEN(#SQL_Final), #SQL_Union, LEN(#SQL_Union)
set nocount off
end
go
I have read that others have had problems with this type of truncation and I have tried multiple suggestions but in the end the I am getting capped at 68274 in this code with nvarchar(max). For troubleshooting, I am saving the results of the variables and the len of these variables to a table to eliminate the SSMS cap on the display of strings.
I have tried cast(#varible as nvarchar(max)) on the right side of the = sign. I have changed the data type lengths (as the select that is being built is not that large, it is just large after it has been union for each unique customer)
I am open to any suggestions as I have tried many variations of datatype declarations for these variables.

TSQL Replace Doubled Characters

Let's say I have data:
heloo
cuube
triniity
How to write script that will replace those "doubled" characters with only one? So the result from the above data set would be:
helo
cube
trinity
Usually I post some script where I tried to achieve this, but this time I can't think of any.
This should work:
CREATE PROCEDURE remove_duplicate_characters(#string VARCHAR(100))
AS
DECLARE #result VARCHAR(100)
SET #result=''
SELECT #result=#result+MIN(SUBSTRING(#string ,number,1)) FROM
(
SELECT number FROM master..spt_values WHERE type='p' AND number BETWEEN 1 AND len(#string )) AS t GROUP BY SUBSTRING(#string,number,1) ORDER BY MIN(number)
)
SELECT #result
GO
You then call it like this:
EXEC remove_duplicate_characters 'heloo'
Source
This script does not depend on having access to master functions, and just relies on t-sql string functions.
declare #word varchar(100) = 'aaaacuuuuuubeeeee', #result varchar(100) = ''
declare #letter char, #idx int = 0, #lastletter char = ''
while(#idx <= len(#word))
begin
select #letter = substring(#word,#idx,1)
if (#letter != #lastletter)
begin
select #result = concat(#result,#letter)
end
select #lastletter = #letter,#idx = #idx + 1
end
select #result

RAISERROR within Case statement

Can you not raise errors within a case statement in T-SQL? I always have problems with SQL case statements :/
begin try
declare #i int
--set #i = (select COUNT(1) from table_name)
select Item_Num =
CASE (select COUNT(1) from table_name)
when 1 then (select Item_Num from table_name)
when 0 then (raiserror('No records in database', 0, 0))
ELSE (raiserror('Multiple records in database', 0, 0))
END
from table_name
end try
begin catch
declare #errormsg nvarchar(1024),
#severity int,
#errorstate int;
select #errormsg = error_message(),
#severity = error_severity(),
#errorstate = error_state();
raiserror(#errormsg, #severity, #errorstate);
end catch
Think of Case/When as operating on a single piece of data. If you think of it this way, a lot of your problems will go away.
If/Then is used to control the flow of logic.
Something like this should work for you.
declare #i int
set #i = (select COUNT(1) from table_name)
If #i = 1
Begin
Print "1 row"
End
Else If #i = 0
Begin
Print "no rows"
End
Else
Begin
Print "too many rows"
End
You can raise an error from the case expression by converting an error string to int.
select case (select count(*) from mytable)
when 1 then 100
when 2 then 200
else convert(int, 'ERROR')
end
This gives an error message like
Conversion failed when converting the varchar value 'ERROR' to data type int.
which is about as good as you're going to get.
Not all failed conversions give the input string in the error message. Conversions to datetime, for example, do not. So if your case expression returns a datetime, you still have to trigger the error with a string-to-integer conversion:
select case (select count(*) from mytable)
when 1 then getdate()
else convert(datetime, convert(int, 'ERROR'))
end
It gets worse: if you are returning a date, you can't explicitly convert that from int, so you have to resort to
convert(date, convert(char(1), convert(int, 'ERROR')))
It's pretty horrible, but in my opinion the only thing more important than clean code is informative error messages, so I live with it.
As I said in the comment, I think it would be easier to simply create a flag that you check outside the scope of the CASE statement. Something along the lines of:
--- code before the TRY...
BEGIN TRY
DECLARE #i int
-- declare a variable to act as a flag
DECLARE #my_flag as int
-- than change your statement to simply fill the value of the flag
CASE (SELECT COUNT(1) FROM table_name)
WHEN 1 THEN SET #my_flag = 1
WHEN 0 THEN SET #my_flag = 0
ELSE SET #my_flag = -1
END
IF (NOT #my_flag in (-1, 0))
BEGIN
SET #Item_Num = (SELECT Item_Num FROM table_name) -- consider a filter here
END
ELSE
BEGIN
IF (-1 = #my_flag) RAISERROR('too many records', 0, 0)
IF (0 = #my_flag) RAISERROR('no records', 0, 0)
END
END TRY
BEGIN CATCH
--- rest of the code goes here....
With #TempTable as
(
Select * from ...
-- record set that determines if I should error out
)
SELECT CASE WHEN COUNT(1) > 0
THEN 'Multiple records in database'
ELSE 0
END AS [Value]
FROM #TempTable
datatype mismatch will kill the method if you're trying to error out this whole call with TSQL. Worked in my case because it was a heavy process that I needed to know was transferred correctly. If My record set was >1 then I know I should fail this. Useful if you're using SSIS or multiple methods within a .NET environment