Unicode code point 9619 is a character called "Dark shade": (http://unicode-table.com/en/search/?q=9619).

Using the SQL_Latin1_General_CP1_CI_AS collation and 1252 code page, I would expect that casting / converting that Unicode character to non-Unicode data type would result in a question mark (?) as code page 1252 does not appear to contain this character and this appears to be SQL Server's behavior when conversion can not take place.

So my question is: why does SQL Server convert this character to an ASCII code 166 which is "Pipe, Broken vertical bar": ¦ ?

SELECT NCHAR(9619), CAST(NCHAR(9619) AS CHAR(1)), ASCII(CAST(NCHAR(9619) AS CHAR(1)))
  • 3
    SQL Server uses what this paper calls homoglyphic transformation and often converts characters that can't be represented to near equivalents. Such as losing the accent on a character or changing smart quotes to plain quotes. I agree that doesn't look very close though! I'm not sure if or where these transformations are documented. – Martin Smith Sep 16 '14 at 22:57
  • Wow, had no idea...jeez, just doesn't seem right...it's not the same character. Why not just an "...oops, no such character found in this code page..." and fail the conversion? – Henry Lee Sep 16 '14 at 23:10
  • 1
    Just reading this page and remembered this. Not sure if SQL Server uses exact same "best fit" algorithms. – Martin Smith Sep 29 '14 at 22:42
  • 1
    @MartinSmith regarding not being sure of the "best fit" mappings for SQL Server, please see my answer below as I found those mappings :-). – Solomon Rutzky Nov 30 '15 at 15:31

Why does SQL convert Unicode 9619 to ASCII code 166?

SQL Server is not employing any special custom logic here; it is using standard operating system services to perform the conversion.

Specifically, the SQL Server type and expression service (sqlTsEs) calls into OS routine WideCharToMultiByte in kernel32.dll. SQL Server sets the input parameters to WideCharToMultiByte such that the routine performs a 'quick translation'. This is faster than requesting a specific default character be used when no direct translation exists.

The quick translation relies on the target code page to perform a best-fit mapping for any unmatched characters, as mentioned in the link Martin Smith provided in a comment to the question:

Best-fit strategies vary for different code pages, and they are not documented in detail.

When the input parameters are set for a quick translation, WideCharToMultiByte calls OS service GetMBNoDefault (source). Inspecting the SQL Server call stack when performing the conversion specified in the question confirms this:

SQL Server stack trace

Converting from Unicode data to a particular Code Page employs what is known as the "Best-fit" strategy (as noted in @Paul's answer and in the link that @Martin noted in a comment on the Question). According to that MSDN page for Character Encoding in the .NET Framework:

Best-fit mapping is the default behavior for an Encoding object that encodes Unicode data into code page data...

But what exactly are these mappings? That MSDN page used to state the following:

Best-fit strategies vary for different code pages, and they are not documented in detail.

However, that was not entirely correct. Perhaps the "strategies" for determining the mappings are not exactly documented. Ok. But, the mappings themselves are documented, just not in the easiest of places to find.

So, thanks to Microsoft moving the documentation to GitHub, that page now states the following (because I updated it 😸 ):

Best-fit strategies are not documented in detail. However, several code pages are documented at the Unicode Consortium's website. Please review the readme.txt file in that folder for a description of how to interpret the mapping files.

If you go to the following URL you will see a list of several files, each one named for the Code Page that it maps Unicode characters to:

ftp://ftp.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WindowsBestFit/

Most of the files were last updated (or at least placed there) on 2006-10-04, and one of them was updated on 2012-03-14. The first part of those files maps ASCII codes into an equivalent Unicode Code Point. But the second part of each file maps the Unicode characters into their ASCII "equivalents".

I wrote a test script that uses the Code Page 1252 mappings to check if SQL Server is truly using those mappings. That can be determined by answering these two questions:

  1. For all mapped Code Points, does SQL Server convert them into the specified mappings ?
  2. For all unmapped Code Points, does SQL Server convert any of them into a non-"?" character?

The test script is too long to place here, so I posted it on Pastebin at:

Unicode to Code Page mappings in SQL Server

Running the script will show that the answer to the first question above is "Yes" (meaning that all of the provided mappings are adhered to). It will also show that the answer to the second question is "No" (meaning, none of the unmapped Code Points convert into anything but the character for "unknown"). Hence, that mapping file is very accurate :-).

Your Answer

 
discard

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.