SQL Server 2008 Converting VARCHAR(50) to Uniqueidentifier
-
22-08-2019 - |
Question
I'm trying to convert a varchar(50) column to a uniqueidentifier, but this error keeps popping up all the time, and i don't know why:
"Msg 8169, Level 16, State 2, Line 1 Conversion failed when converting from a character string to uniqueidentifier."
The data in the column is currently a valid uniqueidentifier.
What's the proper way of doing what I want?
Thanks
Solution
Do you have any columns containing empty strings? I.e. NOT NULL, string length = 0.
Or do you have any GUIDs with non-standard characters? I.e. not 0-9, A-F?
We have some non-standard GUIDs in my application that were created before I inherited it...
EDIT:
For future help, this script can help you find any rows that aren't valid:
SELECT REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(GUID, '1', '0'), '2', '0'), '3', '0'), '4', '0'), '5', '0'), '6', '0'), '7', '0'), '8', '0'), '9', '0'), 'A', '0'), 'B', '0'), 'C', '0'), 'D', '0'), 'E', '0'), 'F', '0'), COUNT(*)
FROM TABLE
GROUP BY REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(GUID, '1', '0'), '2', '0'), '3', '0'), '4', '0'), '5', '0'), '6', '0'), '7', '0'), '8', '0'), '9', '0'), 'A', '0'), 'B', '0'), 'C', '0'), 'D', '0'), 'E', '0'), 'F', '0')
Any rows that have invalid Guids will show up, and can be found by:
SELECT *, REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(GUID, '1', '0'), '2', '0'), '3', '0'), '4', '0'), '5', '0'), '6', '0'), '7', '0'), '8', '0'), '9', '0'), 'A', '0'), 'B', '0'), 'C', '0'), 'D', '0'), 'E', '0'), 'F', '0')
FROM TABLE
WHERE REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(GUID, '1', '0'), '2', '0'), '3', '0'), '4', '0'), '5', '0'), '6', '0'), '7', '0'), '8', '0'), '9', '0'), 'A', '0'), 'B', '0'), 'C', '0'), 'D', '0'), 'E', '0'), 'F', '0') != '00000000-0000-0000-0000-0000000000'
OTHER TIPS
I know this has already been answered but there is a slightly more elegant approach. You can use the simplistic, exactly-one-character wildcard syntax (i.e. []
) allowed in a LIKE
clause to test for valid hex digits (i.e. 0 - 9 and A - F). And, by using a LIKE
clause with the single-character search you can also enforce the format of a valid GUID / UNIQUEIDENTIFIER
in a more readable way then the REPLACE
method which has to first normalize all hex digits to 0's before comparing to the valid format.
SETUP
SET NOCOUNT ON;
IF (OBJECT_ID('tempdb.dbo.#GUIDs') IS NOT NULL)
BEGIN
DROP TABLE #GUIDs;
END;
CREATE TABLE #GUIDs (ID INT NOT NULL, TheGUID VARCHAR(50) NULL);
INSERT INTO #GUIDs (ID, TheGUID)
SELECT tmp.ID, tmp.TheGUID
FROM (
SELECT 1, 'E1A21B62-ACC4-4ACB-B284-0F0233F19EDA' -- valid
UNION ALL
SELECT 2, '50178543-11E6-40D2-87F1-9C4676DCF542' -- valid
UNION ALL
SELECT 3, '' -- invalid: empty string
UNION ALL
SELECT 4, '4EB30267-0EB4-413A-9B05-6EDDB943C7D8' -- valid
UNION ALL
SELECT 5, '4EB30267-0EB4-413A-9Z05-6EDDB943C7D8' -- invalid: has a "Z"
UNION ALL
SELECT 6, NULL -- invalid: is NULL
UNION ALL
SELECT 7, '18EAE6C5-7256-4598-AA0A-837718145001' -- valid
UNION ALL
SELECT 8, '18eae6c5-7256-4598-aa0a-837718145001' -- valid (lowercase version of #7)
UNION ALL
SELECT 9, '18EAE6C5-7²56-4598-AA0A-837718145001' -- invalid: has superscript "2"
) tmp (ID, TheGUID);
TESTS
The example below shows using 32 sets of [0-9A-F]
for each hex-digit position of the GUID and has the dashes (-
) in the appropriate places. Please note:
- the backslash (
\
) at the end of each line in theLIKE
pattern is the T-SQL Line Continuation character, and - you should use a binary collation to ensure that ranges of "0-9" and "A-F" does not match any characters that have values in that range but are not specifically decimal digits. For example, the superscript "2" character (
²
) is not a decimal 2, but it does have a value of 2 when used in a range wildcard and using Unicode comparison rules (which are the rules used for allNVARCHAR
data and evenVARCHAR
IF the collation is a Windows collation -- collation name not starting withSQL_
). Test row #9 verifies this case. However, doing a binary comparison requires either making the range pattern[0-9A-Fa-f]
or wrapping the column in anUPPER()
function. UseLatin1_General_100_BIN2
unless you are using SQL Server 2000 or 2005, in which case you should useLatin1_General_BIN
.
SELECT ID, TheGUID, CONVERT(UNIQUEIDENTIFIER, TheGUID) AS [Converted]
FROM #GUIDs
WHERE UPPER(TheGUID) COLLATE Latin1_General_100_BIN2 LIKE
'[0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F]-\
[0-9A-F][0-9A-F][0-9A-F][0-9A-F]-\
[0-9A-F][0-9A-F][0-9A-F][0-9A-F]-\
[0-9A-F][0-9A-F][0-9A-F][0-9A-F]-\
[0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F]\
[0-9A-F]';
SELECT ID, TheGUID AS [BAD]
FROM #GUIDs
WHERE UPPER(TheGUID) COLLATE Latin1_General_100_BIN2 NOT LIKE
'[0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F]-\
[0-9A-F][0-9A-F][0-9A-F][0-9A-F]-\
[0-9A-F][0-9A-F][0-9A-F][0-9A-F]-\
[0-9A-F][0-9A-F][0-9A-F][0-9A-F]-\
[0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F]\
[0-9A-F]'
OR TheGUID IS NULL;
And, while this question was asked in the context of SQL Server 2008, for anyone using SQL Server 2012 or newer, the TRY_CONVERT function makes this even easier:
SELECT tmp.ID, tmp.TheGUID AS [BAD]
FROM #GUIDs tmp
WHERE TRY_CONVERT(UNIQUEIDENTIFIER, tmp.[TheGUID]) IS NULL;
/*
ID BAD
3
5 4EB30267-0EB4-413A-9Z05-6EDDB943C7D8
6 (NULL)
9 18EAE6C5-7²56-4598-AA0A-837718145001
*/
Out of 5000 rows I had one containing a non hex-valid character.