size limit for nvarchar(max) - sql-server

i am trying to execute a sql query string which has thousands of characters in t-sql. data type using to hold this query is nvarchar(max). but using this i can hold only 67594 characters. but my query has more characters than this.
does anybody has any idea why nvarchar(max) is holding up to 67594 characters only? the nvarchar(max) should hold up to 2GB data. isn't it?
the procedure which i am running is:
create procedure test
(
#snapshotid1 nvarchar(10),
#snapshotid2 nvarchar(10),
#version1 nvarchar(100),
#version2 nvarchar(100)
) AS DECLARE
#sql nvarchar(max),
#whereClause1 nvarchar(500),
#whereClause2 nvarchar(500),
#fromClause1 nvarchar(100),
#fromClause2 nvarchar(100)
BEGIN
set #sql = '';
set #sql = #sql + N'
select v1.v1_brand, version1total, version2total, version1total - version2total as varience from (
select "C - Brand" as v1_brand,
case ' + #period + ' when ''Jan'' then sum(Period1InvoicedAmount)
when ''Feb'' then
.
.
.
END
regards
Subash Amara

Can't reproduce. But! don't think a proper TSQL concatenation can't handle large string. You probably have a problem somewhere in your script. It depends what error are you getting.
Here's a little proof of concept you can run.
First write this script:
DECLARE #sql nvarchar(max) = ''
SET #sql = N'SELECT fld = newid()'
-- (PLACEHOLDER) --
SELECT DATALENGTH(#sql)
EXEC(#sql)
Then run this script and copy the rows...
SELECT TOP 2000
q = 'SET #sql = #sql + N''UNION ALL SELECT fld = newid()'''
FROM sys.syscolumns t1, sys.syscolumns t2
...and paste it instead of -- (PLACEHOLDER) --, so you have...
DECLARE #sql nvarchar(max) = ''
SET #sql = N'SELECT fld = newid()'
SET #sql = #sql + N'UNION ALL SELECT fld = newid()'
SET #sql = #sql + N'UNION ALL SELECT fld = newid()'
...(totals 2000 lines)...
SET #sql = #sql + N'UNION ALL SELECT fld = newid()'
SELECT DATALENGTH(#sql)
EXEC(#sql)
When you execute this, you will see that data length is 120040 (well above 67594) and it outputs 2001 rows, as it should.
However, if you try to PRINT or SELECT your dynamically created string in SSMS, like:
PRINT #sql
SELECT #sql
...you will get truncated results.

Related

Inserting stored procedure parameter to table only inserts first letter

I have stored procedure where I have parameter with datatype sql_variant. This parameter is then converted and inserted into parameter that is nvarchar(MAX) datatype. Inserting dates and floats are working fine. Then as example inserting into varchar(60) cell doesn't seem to work and only inserts first letter. When I add SELECT statements for the parameters in stored procedure it shows after executing the information to be inserted correctly and it only fails the actual insertion to table.
How to insert whole nvarchar to varchar(60) or similar cell?
Here are important parts of the code without too much extra:
CREATE PROCEDURE proc_name
#param1 nvarchar(30),
#param2 nvarchar(30),
#param3 sql_variant
AS
BEGIN
SET NOCOUNT ON;
DECLARE #update_param nvarchar(MAX);
SET #update_param = CONVERT(nvarchar(MAX), #param3);
-- Lots of not important stuff here such as getting datatype from INFORMATION_SCHEMA
DECLARE #Sql nvarchar(MAX);
SET #Sql = N' DECLARE #variable ' + QUOTENAME(#datatype) + N' = #update_param '
+ N' UPDATE table_name'
+ N' SET ' + #param1 + N' = #variable '
+ N' WHERE something = ' + #param2
Exec sp_executesql #Sql, N'#update_param nvarchar(MAX)', #update_param
Adding SELECT #Sql to the procedure gives following result:
DECLARE #variable [varchar] = #update_param
UPDATE table_name
SET column_name = #variable
WHERE something = thingsome
When #param1 = column_name, #param2 = thingsome
Edit: I read multiple questions on this topic and they all told to declare nvarchar length. Here I have it declared as nvarchar(MAX).
Edit2: Added code bits.
Edit3: After adding code and help in comments the answer is that there is length undeclared for #datatype in #Sql
This doesn't answer the question at hand, however, the SP you have is open to injection. Raw string concatenation like that is a dangerous game to play. This is far safer:
CREATE PROCEDURE proc_name
#param1 nvarchar(30),
#param2 nvarchar(30),
#param3 sql_variant
AS
BEGIN
SET NOCOUNT ON;
DECLARE #update_param nvarchar(MAX);
SET #update_param = CONVERT(nvarchar(MAX), #param3);
-- Lots of not important stuff here such as getting datatype from INFORMATION_SCHEMA
DECLARE #Sql nvarchar(MAX);
SET #Sql = N' DECLARE #variable ' + QUOTENAME(#datatype) + N' = #dupdate_param' --Where is the value of #datatype coming from?
+ N' UPDATE table_name'
+ N' SET ' + QUOTENAME(#param1) + N' = #variable '
+ N' WHERE something = #dparam2;'
Exec sp_executesql #Sql, N'#dupdate_param nvarchar(MAX), #dparam2 nvarchar(30)',#dupdate_param = #update_param, #dparam = #param2;
GO

get value into output variable when executing stored procedure through dynamic query

i am using sql server 2008 r2, I have created a dynamic stored procedure because this is my requirement to implement to filteration of data based on certain conditions. I am not able to get value into #RecordCount
ALTER PROCEDURE [dbo].[sp_getAssetListAudit]
#type nvarchar(20),
#typeid nvarchar(5),
#clientId nvarchar(5),
#PageIndex nvarchar(5),
#PageSize nvarchar(5),
#RecordCount nvarchar(5) output
AS
BEGIN
SET NOCOUNT ON;
DECLARE #SQL nvarchar(max)
SET #SQL ='
select ROW_NUMBER() OVER ( ORDER BY ad.arid ASC )
AS rownum, ad.arid,ad.ast_code,ad.ast_descp,isnull(cat.name,'''') ''cat'',ISNULL(loc.name,'''') ''loc'',isnull(gp.name,'''') ''grp'',
isnull(cc.name,'''') ''cc'' ,
ad.ast_qty ''qty'' into #Results
from tbl_AssetDetails ad
left join tbl_Category cat on ad.ast_cat = cat.catid
left join tbl_Subcategory scat on ad.ast_subcat = scat.subcatid
left join tbl_Location loc on loc.lid = ad.ast_loc
left join tbl_Group gp on gp.gid = ad.ast_grp
left join tbl_CostCenter cc on cc.ccid = ad.ast_costcen
where ad.ast_status not in (3,-1) AND ad.clientId = '+#clientId+' AND '
IF(#type='cat')
SET #SQL = #SQL +' ad.ast_cat='+#typeid+' AND '
IF(#type='subcat')
SET #SQL = #SQL +' ad.ast_subcat='+#typeid+' AND '
IF(#type='loc')
SET #SQL = #SQL +' ad.ast_loc='+#typeid+' AND '
IF(#type='grp')
SET #SQL = #SQL +' ad.ast_grp='+#typeid+' AND '
IF(#type='cc')
SET #SQL = #SQL +' ad.ast_costcen='+#typeid+' AND '
IF(#type='ast')
SET #SQL = #SQL +' ad.arid='+#typeid+' AND '
SET #SQL = #SQL +' 1=1 '
SET #SQL =#SQL + ' SELECT '+#RecordCount+' = count(*) FROM #Results '
SET #SQL = #SQL +' SELECT * FROM #Results WHERE rownum
BETWEEN('+#PageIndex+' -1) * '+#PageSize+' + 1 AND((('+#PageIndex+' -1) * '+#PageSize+'+ 1) + '+#PageSize+') - 1 '
SET #SQL = #SQL + ' drop table #Results '
EXEC sp_executesql #SQL, #RecordCount OUTPUT
END
I am not able to get value into #RecordCount and subsequently no result set.
You need to assign the value to the OUTPUT parameter:
SET #SQL =#SQL + ' SELECT #RecordCount = count(*) FROM #Results; '
You also need to pass the parameter definitions to sp_executesql as in #wannuanguo's answer. You should use also use parameters for #typeid, #pageindex, etc. too instead of literals in the query.
Add the second parameter for sp_executesql:
EXEC sp_executesql #SQL, N'#RecordCount nvarchar(5) output',#RecordCount OUTPUT

SQL Server 2012: dynamic SQL limitation ( > 4000 chars) (split)

I have this dynamic SQL in a stored procedure:
Declare #template nvarchar(max)
Declare #publishdetails nvarchar(max)
set #publishdetails= ',B.[PublishedBy]=suser_name(),
B.[PublishedDate]=GETDATE() '
set #template='if NOT EXISTS(select * from ' +#DestinationDB+ '.[CLs] where id='+ str(#slid)+')
insert into ' +#DestinationDB+ '.CLs (id,slid,slversion) VALUES ( '+ str(#id)+','+ str(#slid)+','+str(#slversion)+')
update B set
B.[Clientid]=A.clientid,
--.........
B.[CreatedDate] = A.CreatedDate,
B.[ModifiedDate] = A.ModifiedDate,
B.[CreatedBy] = A.CreatedBy,
B.[ModifiedBy] = A.ModifiedBy '+#publishdetails+ --Added publishdetails
'FROM ' + #SourceDB + '.[CLs] as A, '+ #DestinationDB+ '.[CLs] as B
where A.slversion = '+ str(#slversion)+' and A.id='+str(#slid) + 'B.slversion = '+ str(#slversion)+' and B.id='+str(#slid)
print 'template is: ' + #template
exec sp_Executesql #template
When exec sp_Executesql #template is executing, it fails. Because #template is > 4000 chars and is truncated. How can I split it in chunks and execute it the correct way?
You don't need to split the text into parts. You do need to make sure that truncation doesn't occur whilst you're concatenating strings:
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.
So, make sure that the first concatenation is working with a large value type (and thus produces a large value type as its result) and every subsequent concatenation should be saved from truncation:
set #template=CONVERT(nvarchar(max),'if NOT EXISTS(select * from ' ) + #DestinationDB + ...
(In this way, you don't have to insert conversions everywhere)
This generates an error:
declare #t nvarchar(max)
set #t = 'select LEN(''' + REPLICATE('A',3000) + REPLICATE('B',3000) + REPLICATE('C',3000) + ''')'
exec sp_executesql #t
And this produces the result 9000:
declare #t nvarchar(max)
set #t = CONVERT(nvarchar(max),'select LEN(''') + REPLICATE('A',3000) + REPLICATE('B',3000) + REPLICATE('C',3000) + ''')'
exec sp_executesql #t
I suggest to use this approach:
Declare #template nvarchar(max) = N''
set #template = #template +N'.... -- Or SELECT instead of SET
Update#1
I run this simple query on my test DB:
DECLARE #query nvarchar(max) = N'',
#i int = 1
WHILE 1000 > #i
BEGIN
SET #query = #query + N'SELECT ##version;'
SET #i = #i+1
END
SELECT LEN (#query)
EXEC sp_executesql #query
I got batch with length of 16983 characters. And execution goes well - no truncation. I guess the problem is inside #SourceDB + '.[CLs] and #DestinationDB+ '.[CLs] tables. Somewhere there you got data truncation.
Try to PRINT your query and run it manually.

Column name not working when placed inside a variable in SQL Server [duplicate]

create procedure sp_First
#columnname varchar
AS
begin
select #columnname from Table_1
end
exec sp_First 'sname'
My requirement is to pass column names as input parameters.
I tried like that but it gave wrong output.
So Help me
You can do this in a couple of ways.
One, is to build up the query yourself and execute it.
SET #sql = 'SELECT ' + #columnName + ' FROM yourTable'
sp_executesql #sql
If you opt for that method, be very certain to santise your input. Even if you know your application will only give 'real' column names, what if some-one finds a crack in your security and is able to execute the SP directly? Then they can execute just about anything they like. With dynamic SQL, always, always, validate the parameters.
Alternatively, you can write a CASE statement...
SELECT
CASE #columnName
WHEN 'Col1' THEN Col1
WHEN 'Col2' THEN Col2
ELSE NULL
END as selectedColumn
FROM
yourTable
This is a bit more long winded, but a whole lot more secure.
No. That would just select the parameter value. You would need to use dynamic sql.
In your procedure you would have the following:
DECLARE #sql nvarchar(max) = 'SELECT ' + #columnname + ' FROM Table_1';
exec sp_executesql #sql, N''
Try using dynamic SQL:
create procedure sp_First #columnname varchar
AS
begin
declare #sql nvarchar(4000);
set #sql='select ['+#columnname+'] from Table_1';
exec sp_executesql #sql
end
go
exec sp_First 'sname'
go
This is not possible. Either use dynamic SQL (dangerous) or a gigantic case expression (slow).
Create PROCEDURE USP_S_NameAvilability
(#Value VARCHAR(50)=null,
#TableName VARCHAR(50)=null,
#ColumnName VARCHAR(50)=null)
AS
BEGIN
DECLARE #cmd AS NVARCHAR(max)
SET #Value = ''''+#Value+ ''''
SET #cmd = N'SELECT * FROM ' + #TableName + ' WHERE ' + #ColumnName + ' = ' + #Value
EXEC(#cmd)
END
As i have tried one the answer, it is getting executed successfully but while running its not giving correct output, the above works well
You can pass the column name but you cannot use it in a sql statemnt like
Select #Columnname From Table
One could build a dynamic sql string and execute it like EXEC (#SQL)
For more information see this answer on dynamic sql.
Dynamic SQL Pros and Cons
As mentioned by MatBailie
This is much more safe since it is not a dynamic query and ther are lesser chances of sql injection . I Added one situation where you even want the where clause to be dynamic . XX YY are Columns names
CREATE PROCEDURE [dbo].[DASH_getTP_under_TP]
(
#fromColumnName varchar(10) ,
#toColumnName varchar(10) ,
#ID varchar(10)
)
as
begin
-- this is the column required for where clause
declare #colname varchar(50)
set #colname=case #fromUserType
when 'XX' then 'XX'
when 'YY' then 'YY'
end
select SelectedColumnId from (
select
case #toColumnName
when 'XX' then tablename.XX
when 'YY' then tablename.YY
end as SelectedColumnId,
From tablename
where
(case #fromUserType
when 'XX' then XX
when 'YY' then YY
end)= ISNULL(#ID , #colname)
) as tbl1 group by SelectedColumnId
end
First Run;
CREATE PROCEDURE sp_First #columnname NVARCHAR(128)--128 = SQL Server Maximum Column Name Length
AS
BEGIN
DECLARE #query NVARCHAR(MAX)
SET #query = 'SELECT ' + #columnname + ' FROM Table_1'
EXEC(#query)
END
Second Run;
EXEC sp_First 'COLUMN_Name'
Please Try with this.
I hope it will work for you.
Create Procedure Test
(
#Table VARCHAR(500),
#Column VARCHAR(100),
#Value VARCHAR(300)
)
AS
BEGIN
DECLARE #sql nvarchar(1000)
SET #sql = 'SELECT * FROM ' + #Table + ' WHERE ' + #Column + ' = ' + #Value
--SELECT #sql
exec (#sql)
END
-----execution----
/** Exec Test Products,IsDeposit,1 **/

Can I specify a database name dynamically in a trigger?

I need to get data from a table in a database who's database name will be determined as a variable during a trigger. I then, knowing this variable need to get a seqno from a table in the determined database for a item which was also determined as a variable during a trigger.
I am trying this route as I assume I need to build the SQL statement before I set it to a variable.
This is not working and I need to know the best way on how I can do this:
DECLARE #SU_SEQNO INTEGER, #SU_NAME VARCHAR(50), #SU_OWNER VARCHAR(15), #SUD_SEQNO INTEGER, #SQL NVARCHAR(500)
SET #SU_OWNER = 'XXX'
SET #SU_NAME = '1ABC234'
SET #SQL ='SELECT #SUD_SEQNO=SEQNO FROM ' + (#SU_OWNER) + '.SU_MAIN
WHERE UNITNAME= ' + #SU_NAME
SET #SUD_SEQNO = (EXECUTE (#SQL))
Thanks alot for any help with this
From: Get result from dynamic SQL in stored procedure
SET #SQL = N'SELECT DISTINCT #FiscalYear = FiscalYear FROM ' + #DataSource;
EXEC sp_executesql #SQL, N'#FiscalYear INT OUTPUT', #FiscalYear OUTPUT;
PRINT #FiscalYear;
I'd re-engineer to use the sp_executesql method as shown above. That should do the trick.
I have amended the code, and it works
declare #su_owner varchar(15) = 'DBTEST'
declare #SU_SEQNO INTEGER=1, #SUD_SEQNO INTEGER=0, #SQL NVARCHAR(500)
DECLARE #ParmDefinition NVARCHAR(500), #SU_NAME_INPUT VARCHAR(50)='SU123'
SET #SU_NAME_INPUT = (SELECT UNITNAME FROM SU_MAIN WHERE SEQNO=#SU_SEQNO)
SET #SU_NAME = (SELECT UNITNAME FROM SU_MAIN WHERE SEQNO=#SU_SEQNO)
SET #SQL = N'SELECT #sud_seqnoOUT=MAX(SEQNO) FROM ' + quotename(#su_owner) + '.[dbo].[SU_MAIN] WHERE UNITNAME]=#SU_NAME_INPUT' ;
SET #ParmDefinition = N'#SU_NAME_INPUT VARCHAR(50),#sud_seqnoOUT INT OUTPUT'
EXEC sp_executesql #SQL,#ParmDefinition,#SU_NAME_INPUT = #SU_NAME,
#sud_seqnoOUT = #SUD_SEQNO OUTPUT

Resources