Data length in ntext column? - sql-server

How do you find out the length/size of the data in an ntext column in SQL? - It's longer than 8000 bytes so I can't cast it to a varchar. Thanks.

Use DataLength()
SELECT * FROM YourTable WHERE DataLength(NTextFieldName) > 0

The clue's in the question: use DATALENGTH(). Note it has a different behaviour to LEN():
SELECT LEN(CAST('Hello ' AS NVARCHAR(MAX))),
DATALENGTH(CAST('Hello ' AS NVARCHAR(MAX))),
DATALENGTH(CAST('Hello ' AS NTEXT))
returns 5, 16, 16.
In other words, DATALENGTH() doesn't remove trailing spaces and returns the number of bytes, whereas LEN() trims the trailing spaces and returns the number of characters.

Select Max(DataLength([NTextFieldName])) from YourTable

Related

does LEN() have bug?

It seems that LEN() ignores whitespaces at the right side of a variable.
declare #a varchar(100)
set #a = 'John '
print len(#a)
The above code prints 4 whereas it should be 7.
Is this a bug?
This is not a bug, this is the intended behavior. To quote the documentation:
LEN excludes trailing spaces. If that is a problem, consider using the DATALENGTH (Transact-SQL) function which does not trim the string
Not a bug, is right there in the documentation:
Returns the number of characters of the specified string expression, excluding trailing spaces.
Please read the official documentation. It is expected behaviour. https://learn.microsoft.com/en-us/sql/t-sql/functions/len-transact-sql?view=sql-server-ver15#remarks
Thank you folks,
I really didn't know that LEN() ignores trailing spaces!
I think DATALENGTH() does not return a correct answer always.
For NVARCHAR() type it returns twice the number of characters in a string. In fact, it returns the total number of bytes that the string consumes in memory.
In my opinion in order to get the correct length of a string value, we should use a formula like below:
CREATE FUNCTION dbo.Length(#x NVARCHAR(MAX)) RETURNS INT
AS
BEGIN
RETURN (CASE WHEN RIGHT(#x, 1) = ' ' THEN LEN(REPLACE(#x, ' ', '$')) ELSE LEN(#x) END)
END
The function is not efficient. I know. But, it returns a correct answer at least.

Concatenation of two varchar columns in select into

I have a insert into tableA select from someTables and in my select I have two text columns that I concatenate e.g. colA + colB. They have type varchar(n). Should the column in TableA simply be varchar(2n)? Is it bad for performance if say I have varchar(5*n)?
If the two columns are concatenated from varchar(n) is it possible that the result is more than varchar(2n) or e.g. nvarchar(3n)?
When you concatenate 2 (n)varchar values the resulting datatype is the 2 length properties added together, or 8,000 bytes (which ever is lower). If you concatenating a varchar and an nvarchar the varchar will be implicitly cast to an nvarchar first.
Unless at least 1 of the values concatenated is of MAX length, the return datatype will not be converted to a MAX and any trailing characters will be truncated.
Take the below examples, which return the data types of their aliases:
SELECT REPLICATE('A',10) + REPLICATE('B',10) AS varchar20,
REPLICATE(N'A',10) + REPLICATE(N'B',10) AS nvarchar20,
REPLICATE(N'A',10) + REPLICATE('B',5) AS nvarchar15,
REPLICATE('A',5000) + REPLICATE('B',5000) AS varchar8000, --Truncation occurs
REPLICATE(N'A',3000) + REPLICATE('B',3000) AS nvarchar4000, --Truncation occurs
REPLICATE(CONVERT(nvarchar(MAX),N'A'),3000) + REPLICATE('B',3000) AS nvarcharMAX;
And this can be validated using dm_exec_describe_first_result_set:
SELECT [name], system_type_name
FROM sys.dm_exec_describe_first_result_set(N'SELECT REPLICATE(''A'',10) + REPLICATE(''B'',10) AS varchar20,
REPLICATE(N''A'',10) + REPLICATE(N''B'',10) AS nvarchar20,
REPLICATE(N''A'',10) + REPLICATE(''B'',5) AS nvarchar15,
REPLICATE(''A'',5000) + REPLICATE(''B'',5000) AS varchar8000, --Truncation occurs
REPLICATE(N''A'',3000) + REPLICATE(''B'',3000) AS nvarchar4000, --Truncation occurs
REPLICATE(CONVERT(nvarchar(MAX),N''A''),3000) + REPLICATE(''B'',3000) AS nvarcharMAX;',NULL, NULL);
Obviously, if you concatenate 3 (n)varchar values, then the resulting length is the sum of the 3 length values, etc.
Note that I explicitly state 8,000 bytes not 8,000 or 4,000 characters length. Many confuse the length value for varchar and nvarchar to mean the number of characters it can hold, but this is not actually true, it's the number of bytes; for varchar it's 8,000 single bytes and for nvarchar it is 4,000 double bytes. This is far more important now that SQL Server supports UTF-8 collations.
For example, the below returns a value of 2666, as the character I chose at random (◘) uses 3 bytes per character.
SELECT LEN(REPLICATE(CONVERT(varchar(3),N'◘' COLLATE Latin1_General_100_CI_AI_SC_UTF8),8000));

SQL Server - remove left part of string before a specific character

I have a VARCHAR value that looks like this:
5.95 $ Additional fees
How can I remove everything left from character '$' (including that character) ? So that I get the following result:
Additional fees
The '$' is always present.
STUFF and CHARINDEX would be the simpliest way, in my opinion:
SELECT STUFF(YourColumn,1, CHARINDEX('$',YourColumn),'')
FROM (VALUES('5.95 $ Additional fees'))V(YourColumn);
Note that as $ has a whitespace afterwards, the value returned will have a leading whitespace (' Additional fees'). You could use TRIM (or LTRIM and RTRIM on older versions of SQL Server) to remove this, if it isn't wanted.
I haven't assumed that the portion string to be replaced is CHARINDEX('$',YourColumn)+1, as we have one sample. As far as we know, you could also have values such as '10.99$Base Cost'. If the +1 was used, it would return 'ase Cost' for such a value.
Hello do it like below syntax
declare #temp nvarchar(max)='5.95 $ Additional fees'
select SUBSTRING(#temp,charindex('$',#temp)+1,len(#temp)-1)
You can use SUBSTRING get the particular string and CHARINDEX function to get index of special character, in your case $.
DECLARE #Var VARCHAR(100)
SET #Var = '5.95 $ Additional fees'
SELECT SUBSTRING(#Var, CHARINDEX('$', #Var) + 1, LEN(#Var) - LEN(LEFT(#Var, CHARINDEX('$', #Var))))

Remove the trailing zeros after Decimal points without truncating/ approximating the value in SQL server

Have decimals stored as varchar.
I have a column with value 0.0375000. I need to convert this into 0.0375.
When I did
convert(decimal(8, 7), substring(column, 0, 1) + '.' + substring(column, 2, 8)))
I got the result as 0.0375000.
I want to remove all the trailing zeros and the result I want is 0.0375
How can I do this?
If 2012+ The #'s indicate an optional display
Select format(0.0375000,'0.######') Returns 0.0375
Select format(0.037502,'0.######') Returns 0.037502
Sorry didn't see stored as varchar()
Select format(cast(somecolumn as decimal(18,8)),'0.######')
if you only need 4 decimal places, you want decimal 5,4 (assuming your number to the left of the decimal point fits into 1 digit , if you need 2 digits, choose decimal(6,4) for example )
select convert(decimal(5,4), substring(column,0,1)+'.' +substring(column,2,8) )
decimal data type https://msdn.microsoft.com/en-gb/library/ms187746.aspx
--SQL Code for easy removing trailing zeros.
select CONVERT(DOUBLE PRECISION,'2.256000')
--Result will be 2.256

Right pad a string with variable number of spaces

I have a customer table that I want to use to populate a parameter box in SSRS 2008. The cust_num is the value and the concatenation of the cust_name and cust_addr will be the label. The required fields from the table are:
cust_num int PK
cust_name char(50) not null
cust_addr char(50)
The SQL is:
select cust_num, cust_name + isnull(cust_addr, '') address
from customers
Which gives me this in the parameter list:
FIRST OUTPUT - ACTUAL
1 cust1 addr1
2 customer2 addr2
Which is what I expected but I want:
SECOND OUTPUT - DESIRED
1 cust1 addr1
2 customer2 addr2
What I have tried:
select cust_num, rtrim(cust_name) + space(60 - len(cust_name)) +
rtrim(cust_addr) + space(60 - len(cust_addr)) customer
from customers
Which gives me the first output.
select cust_num, rtrim(cust_name) + replicate(char(32), 60 - len(cust_name)) +
rtrim(cust_addr) + replicate(char(32), 60 - len(cust_addr)) customer
Which also gives me the first output.
I have also tried replacing space() with char(32) and vice versa
I have tried variations of substring, left, right all to no avail.
I have also used ltrim and rtrim in various spots.
The reason for the 60 is that I have checked the max length in both fields and it is 50 and I want some whitespace between the fields even if the field is maxed. I am not really concerned about truncated data since the city, state, and zip are in different fields so if the end of the street address is chopped off it is ok, I guess.
This is not a show stopper, the SSRS report is currently deployed with the first output but I would like to make it cleaner if I can.
Whammo blammo (for leading spaces):
SELECT
RIGHT(space(60) + cust_name, 60),
RIGHT(space(60) + cust_address, 60)
OR (for trailing spaces)
SELECT
LEFT(cust_name + space(60), 60),
LEFT(cust_address + space(60), 60),
The easiest way to right pad a string with spaces (without them being trimmed) is to simply cast the string as CHAR(length). MSSQL will sometimes trim whitespace from VARCHAR (because it is a VARiable-length data type). Since CHAR is a fixed length datatype, SQL Server will never trim the trailing spaces, and will automatically pad strings that are shorter than its length with spaces. Try the following code snippet for example.
SELECT CAST('Test' AS CHAR(20))
This returns the value 'Test '.
This is based on Jim's answer,
SELECT
#field_text + SPACE(#pad_length - LEN(#field_text)) AS RightPad
,SPACE(#pad_length - LEN(#field_text)) + #field_text AS LeftPad
Advantages
More Straight Forward
Slightly Cleaner (IMO)
Faster (Maybe?)
Easily Modified to either double pad for displaying in non-fixed width fonts or split padding left and right to center
Disadvantages
Doesn't handle LEN(#field_text) > #pad_length
Based on KMier's answer, addresses the comment that this method poses a problem when the field to be padded is not a field, but the outcome of a (possibly complicated) function; the entire function has to be repeated.
Also, this allows for padding a field to the maximum length of its contents.
WITH
cte AS (
SELECT 'foo' AS value_to_be_padded
UNION SELECT 'foobar'
),
cte_max AS (
SELECT MAX(LEN(value_to_be_padded)) AS max_len
)
SELECT
CONCAT(SPACE(max_len - LEN(value_to_be_padded)), value_to_be_padded AS left_padded,
CONCAT(value_to_be_padded, SPACE(max_len - LEN(value_to_be_padded)) AS right_padded;
declare #t table(f1 varchar(50),f2 varchar(50),f3 varchar(50))
insert into #t values
('foooo','fooooooo','foo')
,('foo','fooooooo','fooo')
,('foooooooo','fooooooo','foooooo')
select
concat(f1
,space(max(len(f1)) over () - len(f1))
,space(3)
,f2
,space(max(len(f2)) over () - len(f2))
,space(3)
,f3
)
from #t
result
foooo fooooooo foo
foo fooooooo fooo
foooooooo fooooooo foooooo

Resources