Implicit conversion of varchar value to varchar - collation conflict - sql-server

I'm getting the following error whilst running this script. I've tried using the following:
COLLATE Latin1_General_CI_AS. Please can it be sorted? Thanks
Msg 457, Level 16, State 1, Line 8
Implicit conversion of varchar value to varchar cannot be performed because the collation of the value is unresolved due to a collation conflict
DECLARE #AccountID INT
SET #AccountID = 12
SELECT TOP 1 ac.AccountID,
co.Email,
ao.AccountOptionID
FROM CRM.acc.Account ac
INNER JOIN CRM.[profile].[Profile] pr
ON pr.ProfileID = ac.ProfileFK
INNER JOIN CRM.[profile].Contact co
ON pr.ProfileID = co.ProfileFK
LEFT JOIN CRM.acc.[AccountOption] ao
ON ao.AccountFK = ac.AccountID
LEFT JOIN (
SELECT OptionID
FROM CRM.acc.[Option]
WHERE [Name] = 'SMS messages') op
ON op.OptionID = ao.OptionFK
WHERE ac.AccountID = #AccountID
UNION ALL
SELECT u.UnsubscribeID,
u.EmailAddress,
u.SentEmailFK
FROM Email.dbo.Unsubscribe u
INNER JOIN (
SELECT CASE
WHEN AccountTypeFK = 2 THEN OnlineBillingEmail
ELSE EmailBillingEmail
END [EmailAddress]
FROM CRM.acc.Account
WHERE AccountID = #AccountID
) ace
ON ace.EmailAddress COLLATE DATABASE_DEFAULT = u.EmailAddress COLLATE DATABASE_DEFAULT
WHERE ISNULL(ace.EmailAddress, '') != ''

Seems that you have different collation types on your database. Then you can have issues when joining tables or to tables in other databases, as in this case. To get around this you can specify the collation of columns or force a collation using the COLLATE clause when joining two columns. Check more info about collation in MSDN
Also you need to specify the COLLATE clause where you are using the problematic column(s). Check this answer, it answer a very similar question
It is better to stick to a single collation globally. Otherwise you will have problems.
See a detailed explanation of the different collation styles
Edited: to check column collation use this snippet
SELECT name, collation_name
FROM sys.columns
WHERE OBJECT_ID IN (SELECT OBJECT_ID
FROM sys.objects
WHERE type = 'U'
AND name = 'your_table_name'
)
AND name = 'your_column_name'
Edited: added snippet to get all columns with different collation on a database
SELECT [TABLE_NAME] = OBJECT_NAME([id]),
[COLUMN_NAME] = [name],
[COLLATION_NAME] = collation
FROM syscolumns
WHERE collation <> 'your_database_collation_type'
AND collation IS NOT NULL
AND OBJECTPROPERTY([id], N'IsUserTable')=1

try this
ace.EmailAddress COLLATE Latin1_General_CI_AS = u.EmailAddress COLLATE Latin1_General_CI_AS

Because you are doing a union all to the CRM table which has a different collation. You need to explicitly define the collation of your columns going into your union. In your case that will be UnsubscribeID,EmailAddress and SentEmailFK all should be defined with collate database_default
Running the below should work. Let me know in the comments if this works
SELECT TOP 1 ac.AccountID,
co.Email,
ao.AccountOptionID
FROM CRM.acc.Account ac
INNER JOIN CRM.[profile].[Profile] pr
ON pr.ProfileID = ac.ProfileFK
INNER JOIN CRM.[profile].Contact co
ON pr.ProfileID = co.ProfileFK
LEFT JOIN CRM.acc.[AccountOption] ao
ON ao.AccountFK = ac.AccountID
LEFT JOIN (
SELECT OptionID
FROM CRM.acc.[Option]
WHERE [Name] = 'SMS messages') op
ON op.OptionID = ao.OptionFK
WHERE ac.AccountID = #AccountID
UNION ALL
SELECT u.UnsubscribeID COLLATE DATABASE_DEFAULT AS UnsubscribeID,
u.EmailAddress COLLATE DATABASE_DEFAULT AS EmailAddress ,
u.SentEmailFK COLLATE DATABASE_DEFAULT AS SentEmailFK
FROM Email.dbo.Unsubscribe u
INNER JOIN (
SELECT CASE
WHEN AccountTypeFK = 2 THEN OnlineBillingEmail
ELSE EmailBillingEmail
END [EmailAddress]
FROM CRM.acc.Account
WHERE AccountID = #AccountID
) ace
ON ace.EmailAddress COLLATE DATABASE_DEFAULT = u.EmailAddress COLLATE
DATABASE_DEFAULT
WHERE ISNULL(ace.EmailAddress, '') != ''

Related

Update using right function in Where statement

I am trying to update one table from another table using the last 4 digits of a column. Below is what I have that is obviously failing. Using SQL 2012
UPDATE [DB].[dbo].[table1]
SET column1 = (SELECT table2.column1
FROM table2
WHERE (dbo.table2.column2 = dbo.table1.column2)
AND (dbo.table2.column3 = dbo.table1.column3)
AND (dbo.table2.column4 = dbo.table1.column4)
AND (dbo.table2.right(column5,4) = dbo.table1.right(column5,4))
The right way to perform an UPDATE with a JOIN in SQL Server is:
UPDATE t1
SET t1.column1 = t2.column1
FROM [DB].[dbo].[table1] t1
INNER JOIN dbo.table2 t2
ON t2.column2 = t1.column2 COLLATE SQL_Latin1_General_CP1_CI_AS
AND t2.column3 = t1.column3 COLLATE SQL_Latin1_General_CP1_CI_AS
AND t2.column4 = t1.column4 COLLATE SQL_Latin1_General_CP1_CI_AS
AND right(t2.column5,4) = right(t1.column5,4) COLLATE SQL_Latin1_General_CP1_CI_AS;

Issue in procedure (resolve the collation conflict)

I have written a stored procedure like below lines of code
ALTER PROCEDURE [dbo].[pr_AgentLinceseInfo_Fetch]
AS
BEGIN
IF OBJECT_ID('tempdb..#BrList') IS NOT NULL
DROP TABLE #BrList
CREATE TABLE #BrList
(
AgentCode nvarchar(max)
,BrokerName nvarchar(max)
,LicenceID bigint
,LicenceNumber nvarchar(max)
,EffectiveDate datetime
,ExpirationDate datetime
,State nvarchar(max)
);
INSERT INTO #BrList
SELECT
a.AgentCode AS BrokerCode,
sy.BrokerName,
L.Licence, L.LicenceNumber,
DateIssued AS EffectiveDate,
L.ExpirationDate,
J.JurisdictionX AS State
FROM
tbAgent AS a
INNER JOIN
tbLicence L ON L.AgentId = a.Agent
LEFT OUTER JOIN
(SELECT Jurisdiction, JurisdictionX
FROM tbJurisdiction) AS j ON j.Jurisdiction =
(CASE
WHEN ISNULL(L.Jurisdiction, '0') = '0'
THEN a.PhysicalAddressState
ELSE L.Jurisdiction
END)
LEFT OUTER JOIN
(SELECT
SystemUser, (FirstName + ' ' + LastName) AS BrokerName
FROM tbSystemUser) AS sy ON sy.SystemUser = a.SystemUser
SELECT * FROM #BrList
SELECT
t.*, p.ProductX, p.ProductCode
FROM
tbCompanyAgent ca
LEFT OUTER JOIN
(SELECT
AgentCode, BrokerName,
LicenceID, LicenceNumber,
EffectiveDate, ExpirationDate, [State]
FROM #BrList) AS t ON t.LicenceNumber = ca.LicenceNumber
INNER JOIN
tbProduct p ON p.Product = ca.ProductId
DROP TABLE #BrList
END
When we execute this procedure, it throws error message
Cannot resolve the collation conflict
Cannot resolve the collation
conflict between "SQL_Latin1_General_CP1_CI_AS" and
"Latin1_General_CI_AI" in the equal to operation.
The problem is you have two different collations in comparing columns
You should not have two different collation in columns when you want to compare them.
Example:
Consider the following query
SELECT 1
WHERE 'a' COLLATE SQL_Latin1_General_CP1_CI_AS = 'a' COLLATE Latin1_General_CI_AI
Which will throw error stating
Msg 468, Level 16, State 9, Line 1 Cannot resolve the collation
conflict between "Latin1_General_CI_AI" and
"SQL_Latin1_General_CP1_CI_AS" in the equal to operation.
It can resolved by explictly making the collation same in both LHS and RHS
SELECT 1
WHERE 'a' COLLATE SQL_Latin1_General_CP1_CI_AS = 'a' COLLATE SQL_Latin1_General_CP1_CI_AS.
Try updating your query like this
SELECT t.*,
p.ProductX,
p.ProductCode
FROM tbCompanyAgent ca
LEFT OUTER JOIN (SELECT AgentCode,
BrokerName,
LicenceID,
LicenceNumber,
EffectiveDate,
ExpirationDate,
[State]
FROM #BrList) AS t
ON t.LicenceNumber COLLATE SQL_Latin1_General_CP1_CI_AS = ca.LicenceNumber COLLATE SQL_Latin1_General_CP1_CI_AS
INNER JOIN tbProduct p
ON p.Product COLLATE SQL_Latin1_General_CP1_CI_AS = ca.ProductId COLLATE SQL_Latin1_General_CP1_CI_AS
jUST ADD this line -> SQL_Latin1_Generstrong textal_CP1_CI_AS in your joinings. Thats it :)
https://premkt.blogspot.my/2016/12/error-cannot-resolve-collation-conflict.html

Collation issue while join query in MVC5 and SQL Server 2012

When I join two tables I got a collation issue that is
System.Data.SqlClient.SqlException: Cannot resolve the collation conflict between "SQL_Latin1_General_CP1_CI_AS" and "Latin1_General_CI_AI" in the equal to operation.
Then I set the collation in my db using the following code
ALTER DATABASE [CAM] COLLATE SQL_Latin1_General_CP1_CI_AS;
ALTER TABLE CAM_Users
ALTER COLUMN [EmployeeCode] VARCHAR(50)
COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL;
But I still get the same error.
My join query is this:
List<DTOUserManagement> users = (from CAMuser in _unitOfWorkAsync.RepositoryAsync<CAM_Users>().Queryable()
join QRuser in _unitOfWorkAsync.RepositoryAsync<CAM_V_EmployeeMaster>().Queryable() on CAMuser.EmployeeCode equals QRuser.EmployeeCode into t
from t1 in t.DefaultIfEmpty()
join CAMDomain in _unitOfWorkAsync.RepositoryAsync<CAM_Domain>().Queryable() on CAMuser.DomainID equals CAMDomain.DomainID into t2
from t3 in t2.DefaultIfEmpty()
where CAMuser.IsActive
select new DTOUserManagement
{
TransactUserCode = CAMuser.TransactUserCode,
EmployeeCode = CAMuser.EmployeeCode,
EmployeeName = t1.EmployeeName,
Email = t1.EMail,
DomainID = CAMuser.DomainID,
DomainName = t3.DomainName,
IsActive = CAMuser.IsActive,
AssignedRole = CAMuser.AssignedRoles
}).ToList();
How can I solve this?
Please reply anybody
The problem here is that the COLLATION must match on joining columns.
There are two ways to fix this. First you could change the collation on each column. Or second, you can change the collation at execution time. Here's an example of the second approach:
Sample Data
/* T1 and T2 are identical tables in structre and content, except
* for the collation.
*/
DECLARE #T1 TABLE
(
ID VARCHAR(3) COLLATE SQL_Latin1_General_CP1_CI_AS
)
;
DECLARE #T2 TABLE
(
ID VARCHAR(3) COLLATE Latin1_General_CI_AI
)
;
INSERT INTO #T1
(
ID
)
VALUES
('x'),
('y'),
('z')
;
INSERT INTO #T2
(
ID
)
VALUES
('x'),
('y'),
('z')
;
Anti Pattern - Will not Work
/* This query will failed with the error:
* Cannot resolve the collation conflict between "Latin1_General_CI_AI" and "SQL_Latin1_General_CP1_CI_AS" in the equal to operation.
*/
SELECT
*
FROM
#T1 AS t1
INNER JOIN #T2 AS t2 ON t1.ID = t2.ID
;
Corrected - No Error
/* Success.
*/
SELECT
*
FROM
#T1 AS t1
INNER JOIN #T2 AS t2 ON t1.ID = t2.ID COLLATE Latin1_General_CI_AI
;
You need to check your tables that are involved in these JOINs here to verify that all columns involved have the same collation.
You can do this with this SQL query:
SELECT
TableName = t.Name,
ColumnName = c.name,
Collation = c.collation_name
FROM
sys.columns c
INNER JOIN
sys.tables t ON t.object_id = c.object_id
INNER JOIN
sys.types ty ON c.system_type_id = ty.system_type_id
WHERE
t.name IN ('CAM_Users', 'CAM_Domain') -- add any further tables to check
AND ty.name IN ('char', 'nchar', 'nvarchar', 'varchar')
ORDER BY
t.name, c.name
If there are columns that do not match the database default collation, you need to change those to be the same as all other columns. Once all the string columns in those tables are the same collation, then your joins should work.
Update: use this query to find those tables & columns that do not have the current default database collation:
SELECT
TableName = t.Name,
ColumnName = c.name,
Collation = c.collation_name
FROM
sys.columns c
INNER JOIN
sys.tables t ON t.object_id = c.object_id
INNER JOIN
sys.types ty ON c.system_type_id = ty.system_type_id
WHERE
ty.name IN ('char', 'nchar', 'nvarchar', 'varchar')
AND c.collation_name <> 'SQL_Latin1_General_CP1_CI_AS'
ORDER BY
t.name, c.name

Cannot resolve the collation conflict between "SQL_AltDiction_CP850_CI_AS" and "SQL_Latin1_General_CP1_CI_AS" in the equal to operation

I have a piece of code in my stored procedure as below -
update tblexpwitretrocmdocs set sCheckedOut = A.sEditor, idone = 0
from #tblDocs A
JOIN tblexpwitretrocmdocs B ON A.SID = B.SID
where A.iDocumentTypeId in (16,17,13,11)
and A.sid not in (select SID COLLATE SQL_AltDiction_CP850_CI_AS from tblexpwitdocumentgeneral)
I am getting the error "Cannot resolve the collation conflict between "SQL_AltDiction_CP850_CI_AS" and "SQL_Latin1_General_CP1_CI_AS" in the equal to operation." for the first line of code.
The column - sCheckedOut in table - tblexpwitretrocmdocs has collation SQL_AltDiction_CP850_CI_AS. So to make the column - sEditor compatible to it, I defined the temp table as below -
CREATE TABLE #tblDocs(
iId INT IDENTITY (1,1),
SID NVARCHAR(50) COLLATE SQL_AltDiction_CP850_CI_AS,
iDocumentTypeId INT,
sType NVARCHAR(200),
sEditor NVARCHAR(50) COLLATE SQL_AltDiction_CP850_CI_AS
)
Still I am getting the same error. Please help me resolve this.
To resolve the collation conflict add "COLLATE DATABASE_DEFAULT" keywords around “=” operator as shown below:
UPDATE tblexpwitretrocmdocs SET sCheckedOut = A.sEditor, idone = 0
FROM #tblDocs A
JOIN tblexpwitretrocmdocs B ON A.SID = B.SID
WHERE A.iDocumentTypeId in (16,17,13,11) COLLATE DATABASE_DEFAULT
AND A.sid COLLATE DATABASE_DEFAULT NOT IN
(SELECT SID COLLATE DATABASE_DEFAULT FROM tblexpwitdocumentgeneral)
Hope this helps...
Specify DATABASE_DEFAULT for the collation of all temp table string columns to use the current database default collation:
CREATE TABLE #tblDocs(
iId INT IDENTITY (1,1),
SID NVARCHAR(50) COLLATE DATABASE_DEFAULT,
iDocumentTypeId INT,
sType NVARCHAR(200) COLLATE DATABASE_DEFAULT,
sEditor NVARCHAR(50) COLLATE DATABASE_DEFAULT
);
For columns that differ from the database default collation, specify the exact column collation instead of DATABASE_DEFAULT.

How to get list of child tables for a database table?

I have to write a delete script to delete rows form a database table. However the table has a lot of children tables (foreign keys) and those children tables have children tables too.
There are foreign keys for all relationships and I'd like to use this info to get the list of tables where I'll have to deletes, in the correct order (leaf tables first and then up the dependency graph).
How can I get the list of child tables for a given table in the correct order?
try this on your database, this script will only give you the graph for one table at a time. I assume you have an Employee table but you would have to change line 2 to check a specific table of your database:
DECLARE #masterTableName varchar(1000)
SET #masterTableName = 'Employee'
DECLARE #ScannedTables TABLE( Level int, Name varchar(1000) collate Latin1_General_CI_AS )
DECLARE #currentTableCount INT
DECLARE #previousTableCount INT
DECLARE #level INT
SET #currentTableCount = 0
SET #previousTableCount = -1
SET #level = 0
INSERT INTO #ScannedTables VALUES ( #level, #masterTableName )
WHILE #previousTableCount <> #currentTableCount
BEGIN
SET #previousTableCount = #currentTableCount
INSERT INTO #ScannedTables
SELECT DISTINCT
#level + 1, TC.Table_Name COLLATE Latin1_General_CI_AS
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS TC
LEFT JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC ON TC.Constraint_Name = RC.Constraint_Name
LEFT JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS FTC ON RC.Unique_Constraint_Name = FTC.Constraint_Name
WHERE TC.CONSTRAINT_TYPE = 'FOREIGN KEY'
AND FTC.TABLE_NAME COLLATE Latin1_General_CI_AS IN ( SELECT Name FROM #ScannedTables WHERE Level = #level )
AND TC.Table_Name COLLATE Latin1_General_CI_AS NOT IN ( SELECT Name FROM #ScannedTables )
SET #level = #level + 1
SELECT #currentTableCount = COUNT(*) FROM #ScannedTables
END
SELECT * FROM #ScannedTables
There is no simple generic answer for this, since tables can recursively depend on other tables including self relationships, etc. Your result could be more than simple tree.
Your best way should depend on your db model: if you have tree tables connected, then delete your data from third table first, than second, than third.
...or disable constraints, delete data, enable constraints.
...or change foreign keys to DELETE CASCADE.
It depends on your data model.
This article gives a good idea of how to do what you're asking.
EDIT: I've modified the original query given in the link to:
Make the script schema aware
Correct the bug noted in the
comments below
Not sure why the editor is doing such a poor job of formatting the code block.
with Fkeys as (
select distinct
OnTable = onTableSchema.name + '.' + OnTable.name
,AgainstTable = againstTableSchema.name + '.' + AgainstTable.name
from
sysforeignkeys fk
inner join sys.objects onTable
on fk.fkeyid = onTable.object_id
inner join sys.objects againstTable
on fk.rkeyid = againstTable.object_id
inner join sys.schemas onTableSchema
on onTable.schema_id = onTableSchema.schema_id
inner join sys.schemas againstTableSchema
on againstTable.schema_id = againstTableSchema.schema_id
where 1=1
AND AgainstTable.TYPE = 'U'
AND OnTable.TYPE = 'U'
-- ignore self joins; they cause an infinite recursion
and onTableSchema.name + '.' + OnTable.name <> againstTableSchema.name + '.' + AgainstTable.name
)
,MyData as (
select
OnTable = s.name + '.' + o.name
,AgainstTable = FKeys.againstTable
from
sys.objects o
inner join sys.schemas s
on o.schema_id = s.schema_id
left join FKeys
on s.name + '.' + o.name = FKeys.onTable
left join Fkeys fk2
on s.name + '.' + o.name = fk2.AgainstTable
and fk2.OnTable = Fkeys.AgainstTable
where 1=1
and o.type = 'U'
and o.name not like 'sys%'
and fk2.OnTable is null
)
,MyRecursion as (
-- base case
select
TableName = OnTable
,Lvl = 1
from
MyData
where 1=1
and AgainstTable is null
-- recursive case
union all select
TableName = OnTable
,Lvl = r.Lvl + 1
from
MyData d
inner join MyRecursion r
on d.AgainstTable = r.TableName
)
select
Lvl = max(Lvl)
,TableName
,strSql = 'delete from [' + tablename + ']'
from
MyRecursion
group by
TableName
order by
1 desc
,2 desc

Resources