SQL Server 2012 Trailing spaces ignored in = boolean - sql-server-2012

In SQL Server 2012 when searching for a where a column name = 'C CANTU ' it is returning rows where the column in question = 'C CANTU'.
This is with the collation SQL_Latin1_General_CP850_BIN2.
In SQL Server 2005 these two values are not equal, and these rows are not returned.
Is there a setting in SQL Server 2012 for this? They are nvarchar columns. Same scripts ran against each. We are just working on trying to upgrade and found inconsistencies in the final result because of this.

Basically what is happening is that the column is being adjusted with additional trailing spaces to make the lengths match. A couple of workarounds:
DECLARE #p NVARCHAR(255) = N'C CANTU ';
SELECT col FROM dbo.table
WHERE col LIKE LEFT(#p,1) + '%' AND col + 'x' = #p + 'x'; -- will seek
SELECT col FROM dbo.table
WHERE col = #p
AND DATALENGTH(col) = DATALENGTH(#p); -- will seek (and add a filter)
Note that LEN() will ignore trailing spaces, but DATALENGTH() will not.
As I mentioned in the comments, I strongly recommend correcting any queries you come across where nvarchar columns are compared to string literals without using the N prefix.

Related

Can you concatenate a string value multiple times in a PostgreSql SELECT statement?

In Sql Server you can run the following query to combine values from multiple rows into a single string variable.
DECLARE #x NVARCHAR(MAX) = N'';
SELECT TOP 5 #x = #x + name + ', '
FROM sys.objects
SELECT #x
Which results in
"sysrscols, sysrowsets, sysclones, sysallocunits, sysfiles1,"
Is there a way to do something similar in PostgreSql? I've tried creating a PostgreSql function that does a SELECT INTO stringVariable but that is only resulting in a single row's worth of values.
Not exactly. The code you have in SQL Server is not actually guaranteed to work. In Postgres, you would use:
select string_agg(name, ', ')
from sys.objects;
If you have a sys.objects table.
Note that in SQL Server, you should do the same thing:
select string_agg(name, ', ')
from sys.objects;
as demonstrated in this SQL Fiddle.
As a note: Your use of the variable is directly contradicted by the documentation:
If a SELECT statement returns more than one row and the variable references a non-scalar expression, the variable is set to the value returned for the expression in the last row of the result set.
That said, I know what you are doing actually works in practice. But I would recommend using string_agg() or in earlier versions using XML for string concatenation.

SQL Server : Nvarchar to Varchar

I have a table with two columns, one is of type Varchar and the other in NVarchar.
I want to update all the rows so VarcharField = NVarcharField.
It won't let me because some of the rows contain chars that are not allowed in varchar column with the current code page.
How can I find these rows?
Is it possible to remove any char that doesn't fit the specific code page I'm using?
SQL Server 2012.
You can find the rows by attempting to convert the nvarchar() col to varchar():
select nvarcharcol
from t
where try_convert(varchar(max), nvarcharcol) is null;
Try this..
to find the rows with values that are not supported by varchar
declare #strText nvarchar(max)
set #strText = 'Keep calm and say தமிழன்டா'
select cast(#strText as varchar(max)) col1 , N'Keep calm and say தமிழன்டா' col2
Here #strText has non-english chars, When you try to cast that into varchar the non-english chars turns into ????. So the col1 and col2 are not equal.
select nvar_col
from tabl_name
where nvar_col != cast(nvar_col as varchar(max))
Is it possible to remove any char that doesn't fit the specific code page I'm using?
update tabl_name
set nvar_col = replace(cast(nvar_col as varchar(max)),'?','')
where nvar_col != cast(nvar_col as varchar(max))
Replace ? with empty string and update them.
If Gordon's approach doesn't work because you get question marks from TRY_CONVERT instead of the expected NULL, try this approach:
SELECT IsConvertible = CASE WHEN NULLIF(REPLACE(TRY_CONVERT(varchar(max), N'人物'), '?',''), '') IS NULL
THEN 'No' ELSE 'Yes' END
If you need it as filter for the rows that can't be converted:
SELECT t.*
FROM dbo.TableName t
WHERE NULLIF(REPLACE(TRY_CONVERT(varchar(max), t.NVarcharField), '?',''), '') IS NULL

Concatenate sql values to a variable

On a SQL Server 2008 I'm trying to get a comma separated list of all selected values into a variable.
SELECT field
FROM table
returns:
+-------+
| field |
+-------+
| foo |
+-------+
| bar |
+-------+
I'd like to get:
"foo, bar, "
I tried:
DECLARE #foo NVARCHAR(MAX)
SET #foo = ''
SELECT #foo = #foo + field + ','
FROM TABLE
PRINT #foo
Which returns nothing. What am I doing wrong?
You'll need to change NULLs
SELECT #foo = #foo + ISNULL(field + ',', '')
FROM TABLE
or remove them
SELECT #foo = #foo + field + ','
FROM TABLE
WHERE field IS NOT NULL
That happens if you have even a SINGLE field in the table that is NULL. In SQL Server, NULL + <any> = NULL. Either omit them
SELECT #foo = #foo + field + ','
FROM TABLE
WHERE field is not null
Or work around them
SELECT #foo = #foo + isnull(field + ',', '')
FROM TABLE
You can write the whole thing without the leading SET statement which is more common. This query below returns "foo,bar" with no trailing comma
DECLARE #foo NVARCHAR(MAX)
SELECT #foo = isnull(#foo + ',', '') + field
FROM TABLE
WHERE field is not null
PRINT #foo
Don't forget to use LTRIM and RTRIM around #foo (when data type is char/varchar) in the concatenation other it will not give expected results in SQL 2008 R2.
As per the comment Lukasz Szozda made on one of the answers here, you should not use your indicated method to aggregate string values in SQL Server, as this is not supported functionality. While this tends to work when no order clause is used (and even if no exception to this tendency has ever been documented), Microsoft does not guarantee that this will work, and there's always a chance it could stop working in the future. SQL is a declarative language; you cannot assume that behaviour that is not explicitly defined as being the correct behaviour for interpreting a given statement will continue working.
Instead, see the examples below, or see this page for a review of valid ways to achieve the same result, and their respective performance: Optimal way to concatenate/aggregate strings
Doing it in a valid way, whichever way you end up using, still has the same considerations as in the other answers here. You either need to exclude NULL values from your result set or be explicit about how you want them to be added to the resulting string.
Further, you should probably use some kind of explicit ordering so that this code is deterministic - it can cause all sorts of problems down the line if code like this can produce a different result when running on the same data, which may happen without an explicit ordering specified.
--Null values treated as empty strings
SET #Foo =
STUFF /*Stuff is used to remove the seperator from the start of the string*/
( (SELECT N','/*separator*/ + ISNULL(RTRIM(t.Field), '' /*Use an emptry string in the place of NULL values*/) /*Thing to List*/
FROM TABLE t
ORDER BY t.SomeUniqueColumn ASC /*Make the query deterministic*/
FOR XML PATH, TYPE).value(N'.[1]',N'varchar(max)')
,1
,1 /*Length of separator*/
,N'');
--Null values excluded from result
SET #Foo =
STUFF /*Stuff is used to remove the seperator from the start of the string*/
( (SELECT N','/*separator*/ + RTRIM(t.Field) /*Thing to List*/
FROM TABLE t
WHERE t.Field IS NOT NULL
ORDER BY t.SomeUniqueColumn ASC /*Make the query deterministic*/
FOR XML PATH, TYPE).value(N'.[1]',N'varchar(max)')
,1
,1 /*Length of separator*/
,N'');

sql- exceeding variable size in a exec?

I inherited some partially complete sql code that I can't get to work.
it accesses multiple databases, so it first searches for proper database using a userID number, then inserts that database name into a query. the part i'm having a problem with (extremely abbreviated) is...
DECLARE #sql AS VARCHAR(8000)
SET #sql = 'INSERT INTO ['+#DatabaseName+'].dbo.[customer]
( -- containing about 200 columns. )
VALUES(...)'
PRINT #sql
EXEC(#sql)
i would get errors in the middle of a column name, sometimes saying it's expecting a parenthesis or quote. i started deleting white space so that, ie, [first name],[last name] were on the same line and not two different lines and that would get me a little further down the query. i don't have much more white spaces i can delete and i'm only just getting into the Values(...) portion of it. the weird thing is. i copy and pasted just the columns portion and put it into Word and it comes up as being only about 3,000 characters, including white space.
am i missing something?
if it means anything, i'm running microsoft sql server 2005, and using the sql server management studio for editing
thanks!
See here: SQL Server: When 8000 Characters Is Not Enough for a couple of solutions
extremely abbreviated
Well, that doesn't really help since you have likely abbreviated away the cause of the issue.
If I were to guess, I have seen cases where NCHAR or CHAR variables/columns were involved. These expand to their full length when used in string concatenation and it will cause the final statement to be too long.
For what it's worth for style or otherwise, use NVarchar(Max) always for SQL Server 2005 and onwards. In fact, that is the expected type if you use sp_executesql.
If you check for fixed-width N/CHAR columns and switch to nvarchar(max), you may see the problem go away.
EDIT: Test showing NVarchar(Max) holding well in excess of 8000 bytes.
declare #sql nvarchar(max)
-- this CTE sets up the columns, 1 as field1, 2 as field2 etc
-- it creates 2000 columns
;with CTE(n, t) AS (
select 1, convert(nvarchar(max),'1 as field1')
union all
select n+1, convert(nvarchar(max),RIGHT(n, 12) + ' as field'+RIGHT(n, 12))
from cte
where N < 2000)
select #sql = coalesce(#sql+',','') + t
from CTE
option (maxrecursion 2000) -- needed, the default of 100 is not nearly enough
-- add the SELECT bit to make a proper SQL statement
set #sql = 'select ' + #sql
-- check the length : 33786
select LEN(#sql)
-- check the content
print #sql
-- execute to get the columns
exec (#sql)
Use an nvarchar(max) datatype for #sql.

What is the easiest way using T-SQL / MS-SQL to append a string to existing table cells?

I have a table with a 'filename' column.
I recently performed an insert into this column but in my haste forgot to append the file extension to all the filenames entered. Fortunately they are all '.jpg' images.
How can I easily update the 'filename' column of these inserted fields (assuming I can select the recent rows based on known id values) to include the '.jpg' extension?
The solution is:
UPDATE tablename SET [filename] = RTRIM([filename]) + '.jpg' WHERE id > 50
RTRIM is required because otherwise the [filename] column in its entirety will be selected for the string concatenation i.e. if it is a varchar(20) column and filename is only 10 letters long then it will still select those 10 letters and then 10 spaces. This will in turn result in an error as you try to fit 20 + 3 characters into a 20 character long field.
MattMitchell's answer is correct if the column is a CHAR(20), but is not true if it was a VARCHAR(20) and the spaces hadn't been explicitly entered.
If you do try it on a CHAR field without the RTRIM function you will get a "String or binary data would be truncated" error.
Nice easy one I think.
update MyTable
set filename = filename + '.jpg'
where ...
Edit: Ooh +1 to #MattMitchell's answer for the rtrim suggestion.
If the original data came from a char column or variable (before being inserted into this table), then the original data had the spaces appended before becoming a varchar.
DECLARE #Name char(10), #Name2 varchar(10)
SELECT
#Name = 'Bob',
#Name2 = 'Bob'
SELECT
CASE WHEN #Name2 = #Name THEN 1 ELSE 0 END as Equal,
CASE WHEN #Name2 like #Name THEN 1 ELSE 0 END as Similiar
Life Lesson : never use char.
I wanted to adjust David B's "Life Lesson". I think it should be "never use char for variable length string values" -> There are valid uses for the char data type, just not as many as some people think :)
The answer to the mystery of the trailing spaces can be found in the ANSI_PADDING
For more information visit: SET ANSI_PADDING (Transact-SQL)
The default is ANSI_PADDIN ON. This will affect the column only when it is created but not to existing columns.
Before you run the update query, verify your data. It could have been compromised.
Run the following query to find compromised rows:
SELECT *
FROM tablename
WHERE LEN(RTRIM([filename])) > 46
-- The column size varchar(50) minus 4 chars
-- for the needed file extension '.jpg' is 46.
These rows either have lost some characters or there is not enough space for adding the file extension.