SQL Server 2005 collation issue - sql

I have two tables, and they are using different collations. It is not allowed to concatenate columns from tables with different collations, for example the following SQL is not allowed,
select table1column1 + table2column2 from ...
My question is, how to change the collation of a table without destroying the data of the table?
thanks in advance,
George

You can change columns collation on the fly if you need to.
E.g.
select table1column1 collate database default + table2column2 collate database default from ...
"Database default" could be whatever the collation you are wanting to use.
You can alter the collation of a column permanently with
ALTER TABLE ... ALTER COLUMN Table1Column1
varchar(50) COLLATE Latin1_General_CI_AS NOT NULL
GO

Related

How to determine if SQLite column created with COLLATE NOCASE

A column in a SQLite db must be COLLATE NOCASE. I assume there is no way to add that capability to an existing table, so I'm prepare to recreate the table with it. How can I determine if the existing column is COLLATE NOCASE in order to avoid recreating the table every time it is opened?
How can I determine if the existing column is COLLATE NOCASE
The query
SELECT sql FROM sqlite_master WHERE type='table' AND tbl_name='my_table'
will give you the CREATE TABLE statement for that table. You could inspect the DDL to determine if the column is already defined as COLLATE NOCASE.
You might not need to do that at all if it is sufficient to change the collations in the query. I mean you can just overwrite it in the query. It won't affect constraints or index, but depending on your use case, it might be good enough.
To be clear: the collate clause in the table definition is just a default for the queries. You can overwrite this in the queries.
e.g.
WHERE column = 'term' COLLATE NOCASE
or
ORDER BY column COLLATE NOCASE
However, not that SQLite's LIKE doesn't honor collate clause (use pragma case_sensitive_like instead).
The easiest and most general way is store a version number somewhere (in another table, or with PRAGMA user_version).
If you want to check the column itself, use a query with a comparison that is affected by the column's collation:
SELECT Col = upper(Col)
FROM (SELECT Col
FROM MyTable
WHERE 0 -- don't actually return any row from MyTable
UNION ALL
SELECT 'x' -- lowercase; same collation as Col
);

Is the LIKE operator case-sensitive with SQL Server?

In the documentation about the LIKE operator, nothing is told about the case-sensitivity of it. Is it? How to enable/disable it?
I am querying varchar(n) columns, on an Microsoft SQL Server 2005 installation, if that matters.
It is not the operator that is case sensitive, it is the column itself.
When a SQL Server installation is performed a default collation is chosen to the instance. Unless explicitly mentioned otherwise (check the collate clause bellow) when a new database is created it inherits the collation from the instance and when a new column is created it inherits the collation from the database it belongs.
A collation like sql_latin1_general_cp1_ci_as dictates how the content of the column should be treated. CI stands for case insensitive and AS stands for accent sensitive.
A complete list of collations is available at https://msdn.microsoft.com/en-us/library/ms144250(v=sql.105).aspx
(a) To check a instance collation
select serverproperty('collation')
(b) To check a database collation
select databasepropertyex('databasename', 'collation') sqlcollation
(c) To create a database using a different collation
create database exampledatabase
collate sql_latin1_general_cp1_cs_as
(d) To create a column using a different collation
create table exampletable (
examplecolumn varchar(10) collate sql_latin1_general_cp1_ci_as null
)
(e) To modify a column collation
alter table exampletable
alter column examplecolumn varchar(10) collate sql_latin1_general_cp1_ci_as null
It is possible to change a instance and database collations but it does not affect previously created objects.
It is also possible to change a column collation on the fly for string comparison, but this is highly unrecommended in a production environment because it is extremely costly.
select
column1 collate sql_latin1_general_cp1_ci_as as column1
from table1
All this talk about collation seem a bit over-complicated. Why not just use something like:
IF UPPER(##VERSION) NOT LIKE '%AZURE%'
Then your check is case insensitive whatever the collation
If you want to achieve a case sensitive search without changing the collation of the column / database / server, you can always use the COLLATE clause, e.g.
USE tempdb;
GO
CREATE TABLE dbo.foo(bar VARCHAR(32) COLLATE Latin1_General_CS_AS);
GO
INSERT dbo.foo VALUES('John'),('john');
GO
SELECT bar FROM dbo.foo
WHERE bar LIKE 'j%';
-- 1 row
SELECT bar FROM dbo.foo
WHERE bar COLLATE Latin1_General_CI_AS LIKE 'j%';
-- 2 rows
GO
DROP TABLE dbo.foo;
Works the other way, too, if your column / database / server is case sensitive and you don't want a case sensitive search, e.g.
USE tempdb;
GO
CREATE TABLE dbo.foo(bar VARCHAR(32) COLLATE Latin1_General_CI_AS);
GO
INSERT dbo.foo VALUES('John'),('john');
GO
SELECT bar FROM dbo.foo
WHERE bar LIKE 'j%';
-- 2 rows
SELECT bar FROM dbo.foo
WHERE bar COLLATE Latin1_General_CS_AS LIKE 'j%';
-- 1 row
GO
DROP TABLE dbo.foo;
You have an option to define collation order at the time of defining your table. If you define a case-sensitive order, your LIKE operator will behave in a case-sensitive way; if you define a case-insensitive collation order, the LIKE operator will ignore character case as well:
CREATE TABLE Test (
CI_Str VARCHAR(15) COLLATE Latin1_General_CI_AS -- Case-insensitive
, CS_Str VARCHAR(15) COLLATE Latin1_General_CS_AS -- Case-sensitive
);
Here is a quick demo on sqlfiddle showing the results of collation order on searches with LIKE.
The like operator takes two strings. These strings have to have compatible collations, which is explained here.
In my opinion, things then get complicated. The following query returns an error saying that the collations are incompatible:
select *
from INFORMATION_SCHEMA.TABLES
where 'abc' COLLATE SQL_Latin1_General_CP1_CI_AS like 'ABC' COLLATE SQL_Latin1_General_CP1_CS_AS
On a random machine here, the default collation is SQL_Latin1_General_CP1_CI_AS. The following query is successful, but returns no rows:
select *
from INFORMATION_SCHEMA.TABLES
where 'abc' like 'ABC' COLLATE SQL_Latin1_General_CP1_CS_AS
The values "abc" and "ABC" do not match in a case-sensitve world.
In other words, there is a difference between having no collation and using the default collation. When one side has no collation, then it is "assigned" an explicit collation from the other side.
(The results are the same when the explicit collation is on the left.)
Try running,
SELECT SERVERPROPERTY('COLLATION')
Then find out if your collation is case sensitive or not.
You can change from the property of every item.
You can easy change collation in Microsoft SQL Server Management studio.
right click table -> design.
choose your column, scroll down i column properties to Collation.
Set your sort preference by check "Case Sensitive"

SQL collation conflict with temp table and procedure params coming from Delphi app

I've been working with MS SQL a few years now, and I've never encountered anything like this on my previous job. But where I work now, I got an error that I'd really like to know the cause from.
I made a stored procedure and called it in my Delphi 5 (yeah I know) app with some parameters. This worked fine on two databases (copies from different times). But now I tried it on another DB (again a copy), but it gave me the following error:
Cannot resolve the collation conflict between "Latin1_General_CI_AS" and
"SQL_Latin1_General_CP1_CI_AS" in the equal to operation.
I got this by creating a temp table and then trying to insert some data. I'm not even joining. And the funny thing is: when I remove the whole WHERE clause, it works. When I leave it (although it only compares parameters with one table), it fails.
create table #TOP (EDAID int, ParentID char(30), ChildID char(30),
Position int, OrgQty_modified_manually bit)
This fails:
insert into #TOP
select EDAID, ParentID, ChildID, Position, OrgQty_modified_manually
from EDA_SOBOM
where OrderNr = #OrderNr
and Position = #Position
and LN = #LN
and DL = #DL
and rtrim(ChildID) = #CurrentPart
and rtrim(ParentID) = #ParentID
This works:
insert into #TOP
select EDAID, ParentID, ChildID, Position, OrgQty_modified_manually
from EDA_SOBOM
The procedure parameters are declared like this:
#PartID char(30), #Position int, #OrderNr char(8),
#LN char(2), #DL char(2), #ParentID char(30), #Modified bit output
I found a solution here: Cannot resolve the collation conflict between "SQL_Latin1_General_CP1_CI_AS" and "Latin1_General_CI_AS" in the equal to operation.
So I added this right after the CREATE:
ALTER TABLE #TOP
ALTER COLUMN ParentID
VARCHAR(30) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL
ALTER TABLE #TOP
ALTER COLUMN ChildID
VARCHAR(30) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL
And that made the whole thing work again...but I don't get it why the WHERE clause with only parameter comparisons fails...can parameters have collation too?
DB has collation SQL_Latin1_General_CP1_CI_AS.
Table EDA_SOBOM also has collation SQL_Latin1_General_CP1_CI_AS on the char columns.
I found this by the following query:
SELECT col.name, col.collation_name
FROM sys.columns col
WHERE object_id = OBJECT_ID('EDA_SOBOM')
Is there another place where collation can be set apart from the DB level and column level?
I wonder what's going on...
Collation conflicts come up on any operators that compare strings of different collations, i.e. the equals in your select.
TempDb takes the server default collation, whereas your real Dbs may have a different one, causing any temp tables created using DDL to have a collation difference.
You can add "collate database_default" clauses after your equality operators which should fix it.
Or you could create your temp table using:
select top 0 EDAID, ParentID, ChildID, Position, OrgQty_modified_manually
into #top
from EDA_SOBOM
This will force the temp table columns to take the data type (& collation) from your database.
There is a server level collation setting that acts as the default for all system dbs.
There is a database level collation, as you said.
And columns and expressions can have a collation defined.
Many problems arise when a database has a different collation from the system databases especially tempdb.
Without repeating Peter Wishart's answer, which I agree with, I would just add that when developing a product you should decide what level of collation flexibilty you are going to allow. To avoid problems you have to design code around that choice. If you are not going to require your database objects to be consistent with the server collation, then you have to apply collation modifiers or control the collations used when tables are created in tempdb, or system tables are used or when compares are done. That can be a lot of code in a large product.
There is another collation that is often overlooked in SQLServer. That is the default collation used in any .Net SPs or Functions. That collation is defined based on the SQLServer process's windows user profile. It's often not called a collation in docs, It's part of the windows regional settings. Called LCID in the registry.
So even if your database, sqlserver, table, column collations all match, you can still have mismatches if you do string compares in CLR stored procedure code, unless you write that code to avoid them.
To resolve the collation conflict add "COLLATE DATABASE_DEFAULT" keywords around “=” operator.
SELECT col.name, col.collation_name
FROM sys.columns col
WHERE object_id COLLATE DATABASE_DEFAULT = OBJECT_ID('EDA_SOBOM') COLLATE DATABASE_DEFAULT

SQL Server 2000 DTS - Cannot resolve collation conflict for equal to operation

I have a SQL Server 2000 DTS package.
One of the steps of this package has the following SQL:
SELECT *
FROM [Crocus_Limited$OrderRequestDetail]
WHERE (rep_updated > GETDATE() -2)
AND NOT EXISTS
(SELECT OrderID
FROM NavisionUpgrade.navision4.dbo.[WEBOrderDetails] rd
WHERE rd.OrderID = [Crocus_Limited$OrderRequestDetail].OrderID
AND rd.NavisionItemNo = [Crocus_Limited$OrderRequestDetail].NavisionItemNo )
It is failing- giving me error:
cannot resolve collation conflict for equal to operation.
This DTS basically moves data from one DB to another (located in different geographical locations)
how can i alter the above query to resolve this?
One or both of your join columns has on of the char datatypes (char,nchar,varchar,nvarchar) which is stored in incompatible collations in each database.
You can specify the collation to use in any string comparison. The easiest way to do it is to specify the default collation of the machine on which the query is running (I'm guessing that NavisionItemNo is the problem column):
...AND rd.NavisionItemNo collate database_default = [Crocus_Limited$OrderRequestDetail].NavisionItemNo collate database_default )
EDIT
Is OrderID a varchar column too? If so, try
...WHERE rd.OrderID collate database_default = [Crocus_Limited$OrderRequestDetail].OrderID collate database_default
AND rd.NavisionItemNo collate database_default = [Crocus_Limited$OrderRequestDetail].NavisionItemNo ) collate database_default
as the two former posts mention you have to use the collate attribute to every nonumeric column but have a look a the collation of the target db and use this collation (e.g. SQL_Latin_CI_AS). Be aware that a table can have it's own collation even a column can have annother collation, so have a deep look in your definitions.
Peace and good luck
Ice

SQL Server 2005 date comparison - collation issues

In my DB, I have a table that was created from an Excel sheet, via a Linked Server option.
I am now trying to compare its contents versus one of my main tables.
The tables I am comparing are in the same database.
There is a date column in both tables, both types are datetime and have Collation of SQL_Latin1_General_CP1_CI_AS, the same as the DB.
The server collation is Latin1_General_CI_AS
However when I try to run a query comparing the dates between the tables, I get the error:
Cannot resolve the collation conflict between
"Latin1_General_CI_AS" and
"SQL_Latin1_General_CP1_CI_AS" in the
equal to operation.
I have tried with and without the COLLATE option, using both collation settings.
My query is:
select * , hxl.holiday_dt,
datediff(d, h.holiday_dt collate SQL_Latin1_General_CP1_CI_AS,
hxl.holiday_dt collate SQL_Latin1_General_CP1_CI_AS)
from holiday h, Holiday_XL hxl
where h.currency_cd=hxl.currency_cd
In fact any query involving both tables gives exactly the same collation error, eg this one:
select count(*)
from Holiday_XL c
where c.currency_cd in (select distinct h.currency_cd from holiday h)
Thanks in advance for any thoughts.
Regards,
Chris
The error is being reported on the currency comparison h.currency_cd=hxl.currency_cd, not on datediff, so try force collation on the currencies.
Collation is only relevant for character (char, varchar, nvarchar) and text types.
Under the Collation and give the collation table level there are lot of tips and code available in net search with topic of collation.
Still if you have problem. Insert the content of the excel in to #table or permenant table and provide Collation to that table while selecting for comparison