Handling more than 8000 chars in stored proc parameter - sql-server

I have a SQL Stored procedure that sends a mail. It's signature looks like this:
CREATE PROCEDURE SendMail
#From varchar(40),
#To varchar(255),
#Subject varchar(255),
#Body varchar(max),
#CC varchar(255) = null,
#BCC varchar(255) = null
AS...
When the message is for example 5000 characters it work. When it is 12 000, I get an error that [ODBC SQL Server Driver]String data, right truncation.
According to the help files varchar(max) can handle 2^31-1 bytes / characters.
So I tried changing #Body varchar(max) to #Body varchar(30000) and I get an error that
The size (30000) given to the type 'varchar' exceeds the maximum allowed for any data type (8000).
So the max is 8000 and not 2^31-1 bytes?
How can I handle more than 8000 characters?

You need to use nvarchar(max), instead of varchar(4000) or varchar(max). This can store up to 2 GB of text, which will solve your problem...
For more information see http://technet.microsoft.com/en-us/library/ms186939.aspx

Text fields cannot be larger than 8060 Bytes (8K) due to SQL Server Page Size which is 8K...
varchar has a maximum #of chars of 8000
nvarchar has a maximum #of chars of 4000 (each char-->2 bytes)
You cannot declare a parameter varchar(30000)
You should use varchar(max) or nvarchar(max)
the first has 2^31 chars (approx 2billions), the latter has 2^30 chars (approx 1billion)
Also, please note that SQL Server has a Stored Proc Named sp_send_dbmail that you can use to sen emails...

Try using NVARCHAR(MAX) instead of VARCHAR(MAX).

Use the BLOB data type. I use it occasionally for very long fields but it cannot be compared. I do not believe there is a max length on BLOB.

Max. capacity is 2 GByte of space - so you're looking at just over 1 billion 2-byte characters that will fit into a NVARCHAR(MAX) field.
Using the other answer's more detailed numbers, you should be able to store
(2 ^ 31 - 1) / 2 = 1'037'741'823 double-byte characters
1 billion, 37 million, 741 thousand and 823 characters to be precise
in your NVARCHAR(MAX) column (unfortunately, that last half character is wasted...)
SOURCE

REPLICATE returns the input type irrespective of later assignment. It's annoying, but to avoid silent truncation, try this example:
declare #x varchar(max) set #x = replicate (cast('a' as varchar(max)),
10000) select #x, len(#x)
This is because SQL Server performs the REPLICATE operation before it considers what you're assigning it to or how many characters you're trying to expand it to. It only cares about the input expression to determine what it should return, and if the input is not a max type, it assumes it is meant to fit within 8,000 bytes.

Related

SQL Server. DataType for password hash (sha 512)

Wondering which data type to select for my SQL Server to store the sha512 password hash. (hashed value will always have fixed length)
declare #hashedPasswd varchar(max)
set #hashedPasswd = convert(varchar(max), HASHBYTES('SHA2_512', 'any password with any langth'), 1)
select len (#hashedPasswd)
always returns length of 130.
What is the best choice for datatype of the column?
Variants are nvarchar(max), nvarchar(130), varchar, char.
If I understand correctly, nvarchar is a waste of space in my case, because It will be only ASCII symbols in hashed value.
Please assist.
SHA2_512 is 64 bytes long and internaly a varbinary so I would suggest using this datatype instead.
For more safty I also would recommend to use an additional salt for password encryption and decryption. You can find a useful description here:
https://www.mssqltips.com/sqlservertip/4037/storing-passwords-in-a-secure-way-in-a-sql-server-database/
Best regards, Stephan
According to the documentation, hasbytes returns a varbinary therefore your data type is varbinary.
Your length of 130 is only because you are casting to a varchar and is an inefficient way to store it. From the documentation, sha512 returns 64 bytes, therefore your length required is 64.
declare #hashedPasswd varbinary(max)
set #hashedPasswd = HASHBYTES('SHA2_512', 'any password with any length')
select len (#hashedPasswd)

Using varchar(8000) column value in HashByte md5 function

I want to create a hash using all the row values in SQL. In that table one of the column length is varchar(8000).I put a hashbyte function like below -
Hashbyte('MD5',column1+column2+....) -- column1 having varchar(8000) length and it contains string of length 8000.
Then it gives me same hashbyte where the rows having same value in column1 even if other columns contains different data
Then I converted column1 to varchar(max) in hashbyte function, I got different hashbyte for each row.
Hashbyte('MD5',convert(varchar(max),column1)+column2+....)
Why the hashbyte('MD5'...) wont take all column values?
If you want to try one more example of having varchar(8000) column issue-
try to calculate the length
create a table having column with varchar(8000) and calculate length of all column values. It will give you 8000 only. Next convert the varchar(8000) to varchar(max) it will give you correct result.
len(column1+column2...) --> 8000
len(convert(varchar(max),column1)+column2...) --> actual length
adding any string with varchar(8000) is such an issue?
You're under the misconception that a varchar(8000) concatenated to a varchar(8000) (or even any other length <= 8000) results in a varchar(MAX). This is not true. To get a MAX length you must define at least one of the values in the expression as a MAX.
This is confirmed in the remarks in + (String Concatenation) (Transact-SQL):
Remarks
...
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.
As a result you need to convert one of the values first to MAX and then the rest would implicitly be cast to a MAX as well. If you don't explicitly convert (at least) one of the expressions, then the value will be truncated, as the documentation states.
Obviously this applies to nvarchar as well, where truncation occurs at 4,000 characters (which is still 8,000 bytes).

Why does SQL Server give me a column twice the size I requested?

After executing a CREATE TABLE for a temporary table I was verifying that the size of the field fits what I need to use.
To my surprise, SQL Server (Azure SQL) is reporting that the table now has double the size. Why is this?
This is what I executed, in order:
CREATE TABLE #A ( Name NVARCHAR(500) not null )
EXEC tempdb..sp_help '#A'
An NVARCHAR column in SQL Server always stores every character with 2 bytes.
So if you're asking for 500 characters (at 2 bytes each), obviously this results in column size of 1000 bytes.
That's been like this in SQL Server forever - this isn't new or Azure specific.
NVARCHAR shows 2 bytes per character. So if the size is 500 it shows size as 1000. It is to store unicode format data.

SQL Server 2014 Hashbytes of a nvarchar(max) result is nvarchar(max)

Using SQL Server 2014 I have a table that has a nvarchar(max) column called [ASCII File] which can contain an ASCII text file of many K. I then want to do a MD5 hashbytes on that file and the resultant hash should always be 20 bytes.
Well when I do a select of hashbytes('MD5', [ASCII File]) I get query completed with errors
Msg 8152, Level 16, State 10, Line 4
String or binary data would be truncated.
I get the same message when I try
left(hashbytes('MD5', [ASCII File]), 50)
I get the same message when I try
convert(varchar(50), hashbytes('MD5', [ASCII File]))
It seems like since the column I am doing the hashbytes on is nvarchar(max), the result of the hashbytes function also is nvarchar(max).
Can you tell me how I can get the result to be the expected 20 long and not something so long it has to be truncated?
It seems like since the field I am doing the hashbytes on is nvarchar(max) the result of the hashbytes is nvarchar(max).
No, that is not possible, especially since the return value of HASHBYTES is a VARBINARY. Also, since your tests were just SELECT statements and not an INSERT statement, there is no way for the return value to get a truncation error. The truncation error is coming from the input value. As stated in that linked MSDN page for HASHBYTES (for SQL Server 2012 and 2014):
Allowed input values are limited to 8000 bytes. The output conforms to the algorithm standard: 128 bits (16 bytes) for MD2, MD4, and MD5; 160 bits (20 bytes) for SHA and SHA1; 256 bits (32 bytes) for SHA2_256, and 512 bits (64 bytes) for SHA2_512.
That really says it all: the input is limited to 8000 bytes, and the output is a fixed number of bytes, based on the specified algorithm.
The updated documentation, for SQL Server 2016 (which has removed the 8000 byte limitation), states:
For SQL Server 2014 and earlier, allowed input values are limited to 8000 bytes.
You can run a simple test:
DECLARE #Test NVARCHAR(MAX) = REPLICATE(CONVERT(NVARCHAR(MAX), N't'), 50000);
SELECT LEN(#Test);
SELECT HASHBYTES('MD5', #Test);
Returns:
50000
Msg 8152, Level 16, State 10, Line 3
String or binary data would be truncated.
If you want to pass in more than 8000 bytes to a hash function in a version of SQL Server prior to 2016, then you need to use SQLCLR. You can either write your own function, or you can download and install the Free version of the SQL# SQLCLR library (which I created), and use the Util_Hash and Util_HashBinary functions:
DECLARE #Test NVARCHAR(MAX) = REPLICATE(CONVERT(NVARCHAR(MAX), N't'), 50000);
SELECT LEN(#Test);
SELECT SQL#.Util_Hash('MD5', CONVERT(VARBINARY(MAX), #Test));
SELECT SQL#.Util_HashBinary('MD5', CONVERT(VARBINARY(MAX), #Test));
Returns:
50000
40752EB301B41EEAEB309348CE9711D6
0x40752EB301B41EEAEB309348CE9711D6
UPDATE
In the case of using a VARCHAR(MAX) column or variable but with 8000 or fewer characters (or an NVARCHAR(MAX) column or variable with 4000 or fewer characters), there will be no issue and everything will work as expected:
DECLARE #Test VARCHAR(MAX) = REPLICATE('t', 5000);
SELECT LEN(#Test) AS [Characters],
HASHBYTES('MD5', #Test) AS [MD5];
Returns:
5000 0x6ABFBA10B49157F2EF8C85862B6E6313
In SQL Server 2016 we don't have any more the problem of length of input parameter for HASHBYTES function.
DECLARE #Test NVARCHAR(MAX);
SET #Test = REPLICATE(CONVERT(NVARCHAR(MAX), N't'), 50000000);
SELECT LEN(#Test);
SELECT HASHBYTES('SHA2_512', #Test);
HASHBYTES (Transact-SQL)
If you are trying to convert a large varbinary or image file already in sql then there are some built in functions that can do this (possibly from 2014 onwards), this simple function will work for both varbinary(max) and older Image fields..
/****** Object: UserDefinedFunction [dbo].[MD5Bin] Script Date: 16/07/2018 11:04:26 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- ==================================================
-- Author: Darren Steven
-- Create date: 16/07/2018
-- Description: Hashes a binary or image field with MD5
-- ==================================================
CREATE FUNCTION [dbo].[MD5Bin](#value varbinary(max))
RETURNS varchar(32)
AS
BEGIN
RETURN SUBSTRING(master.sys.fn_sqlvarbasetostr(master.sys.fn_repl_hash_binary(#value)),3,32);
END
GO
then simply call the function with your select:
SELECT dbo.MD5Bin(imageFieldName) from dbo.yourTable
The input length limit of 8,000 bytes for the HASHBYTES (Transact-SQL) function is removed in sql 2016
Based on algorithm below are the output data size
128 bits (16 bytes) for MD2, MD4, and MD5;
160 bits (20 bytes) for SHA and SHA1;
256 bits (32 bytes) for SHA2_256
512 bits (64 bytes) for SHA2_512.

Sql server nvarchar size?

I have to store maximum of 5000 character in sql server. Which one I can use to achieve this?
Should I use,
nvarchar(5000);
or
nvarchar(max);
nvarchar has a maximum (declareable) length of 4000. So since you want to store more than 4000 characters, you will have to use nvarchar(max).
nchar and nvarchar on MSDN

Resources