Tidak dapat memperbarui "CO2" ke "CO₂" di baris tabel


19

Diberikan tabel ini:

CREATE TABLE test (
    id INT NOT NULL,
    description NVARCHAR(100) COLLATE Modern_Spanish_CI_AS NOT NULL
);
INSERT INTO test (id, description) VALUES (1, 'CO2');

Saya menyadari bahwa saya tidak dapat memperbaiki masalah tipografi:

SELECT * FROM test WHERE id = 1;
UPDATE test SET description = 'CO₂' WHERE id = 1;
SELECT * FROM test WHERE id = 1;

karena pembaruan cocok tetapi tidak berpengaruh:

id          description
----------- -----------
1           CO2

(1 affected rows)

(1 affected rows)

id          description
----------- -----------
1           CO2

(1 affected rows)

Seolah-olah SQL Server menentukan bahwa, karena jelas hanya 2 kecil , nilai akhir tidak akan berubah sehingga tidak layak untuk mengubahnya.

Bisakah seseorang menjelaskan ini dan mungkin menyarankan solusi (selain memperbarui ke nilai perantara)?


1
Álvaro: jika Anda ingin mempelajari lebih lanjut tentang perilaku ini, untuk lebih memahami mengapa ini terjadi, silakan lihat dua tautan yang baru saja saya tambahkan di bagian bawah jawaban saya.
Solomon Rutzky

Jawaban:


29

Subskrip 2 bukan bagian dari rangkaian karakter varchar (dalam susunan apa pun, bukan hanya Modern_Spanyol). Jadi buatlah konstanta nvarchar:

UPDATE test SET description = N'CO₂' WHERE id = 1;

1
Tidak hanya saya memperbaiki nilainya, saya juga mengerti bagaimana hal itu sampai di sana. Terima kasih!
Álvaro González

2
@ ÁlvaroGonzález dan gbn: Hanya untuk memperjelas, "Subskrip 2" tidak tersedia di Halaman Kode yang ditentukan oleh Collation default dari Database yang dimaksud, yang merupakan Collation yang digunakan untuk string literal dan variabel, bukan Collation kolom (walaupun keduanya Collation (meskipun keduanya bisa menggunakan Halaman Kode yang sama). Namun, "Subskrip 2" tersedia dalam Kode 949 melalui Collations Korea. Itu tidak akan membantu di sini, tetapi hanya FYI. Saya memiliki detail dan contoh dalam jawaban saya .
Solomon Rutzky

21

@ gbn sudah menjelaskan alasan dasar dan memperbaikinya, tetapi alasan spesifik untuk perilaku yang Anda lihat adalah ini:

  1. Anda menggunakan VARCHARliteral (tanpa Nawalan) alih-alih NVARCHARliteral (string dengan Nawalan), maka karakter Unicode akan dikonversi menjadi VARCHAR.
  2. VARCHARadalah pengkodean 8-bit yang, dalam banyak kasus, satu byte per karakter, tetapi juga bisa dua byte per karakter. Di sisi lain, NVARCHARadalah penyandian 16-bit (UTF-16 Little Endian) yang terdiri dari dua byte atau empat byte per karakter.
  3. Karena perbedaan jumlah byte yang tersedia untuk digunakan untuk memetakan karakter, pengkodean 8 bit, pada dasarnya, jauh lebih terbatas dalam jumlah karakter yang dapat dipetakan. VARCHARdata hingga 256 karakter untuk Set Karakter Byte Tunggal (mayoritasnya) dan hingga 65.536 karakter untuk Set Karakter Byte Ganda (hanya beberapa di antaranya). Di sisi lain, NVARCHARdata dapat memetakan lebih dari 1,1 juta karakter Unicode (meskipun hanya di bawah 250 ribu yang saat ini dipetakan).
  4. Karena terbatasnya pemetaan yang dapat dilakukan dengan 8-bit / VARCHARdata, pengelompokan karakter yang berbeda (berdasarkan Bahasa / Budaya) tersebar di beberapa "Halaman Kode" (yaitu set karakter)
  5. Setiap Kolasi menentukan Halaman Kode mana, jika ada, yang akan digunakan untuk VARCHARdata ( NVARCHARsemuanya karakter)
  6. Ketika mengkonversi string literal atau variabel dari NVARCHAR(yaitu Unicode / UTF-16 / semua karakter) ke VARCHAR(set karakter berdasarkan Halaman Kode yang ditentukan dalam kebanyakan Collations), Collation default dari Database digunakan
  7. Jika Halaman Kode Koleksi yang digunakan untuk konversi tidak mengandung karakter yang sama, tetapi berisi pemetaan "paling cocok", maka pemetaan "paling cocok" akan digunakan.
  8. Jika Halaman Kode Koleksi yang digunakan untuk konversi tidak mengandung karakter yang sama atau berisi pemetaan "paling cocok", maka karakter "pengganti" default akan digunakan (paling umum ?).

Jadi, apa yang Anda lihat adalah NVARCHARuntuk VARCHARkonversi karena hilang Nawalan pada literal string yang. Dan, Halaman Kode dari Collation default untuk Database tidak mengandung karakter yang sama persis, tetapi pemetaan "paling cocok" ditemukan, itulah sebabnya Anda mendapatkan 2alih - alih a ?.

Anda dapat melihat efek ini dengan melakukan tes sederhana berikut:

SELECT '₂', N'₂';

Pengembalian:

2    ₂

Agar lebih jelas, JIKA Halaman Kode dari Collation default untuk Database memang mengandung karakter yang sama persis, maka itu akan diterjemahkan ke dalam karakter yang sama di Halaman Kode itu. Dan, kemudian, dalam kasus Anda, karena Anda menyimpan ke dalam NVARCHARkolom, itu akan diterjemahkan lagi, kembali ke karakter Unicode yang asli. Contoh terakhir di bawah ini menunjukkan perilaku ini.

PENTING: Harap perhatikan bahwa konversi terjadi ketika string literal sedang ditafsirkan, yang sebelum disimpan ke dalam kolom. Ini berarti bahwa bahkan jika kolom dapat menampung karakter itu, itu akan telah dikonversi menjadi sesuatu yang lain, berdasarkan Basis Data default Basis Data, semua karena meninggalkan Nawalan pada string literal itu. Dan ini persis seperti apa yang Anda (atau sedang) alami.

Misalnya, jika Collation default dari Database Anda akan menjadi salah satu Collations Korea (salah satu dari empat Set Karakter Double-Byte), maka Anda tidak akan melihat masalah ini karena karakter "Subskrip 2" tersedia dalam karakter itu set (Kode Halaman 949). Coba tes berikut untuk melihat (menggunakan Collation of the kolom daripada Collation default di Database karena lebih mudah ditampilkan):

CREATE TABLE #TestChar
(
    [8bit_Latin1_General-1252] VARCHAR(2) COLLATE Latin1_General_100_CI_AS_SC,
    [8bit_Korean-949] VARCHAR(2) COLLATE Korean_100_CI_AS_SC,
    [UTF16LE_Latin1_General-1252] NVARCHAR(2) COLLATE Latin1_General_100_CI_AS_SC
);

INSERT INTO #TestChar VALUES (N'₂', N'₂', N'₂');

SELECT * FROM #TestChar;

Pengembalian:

8bit_Latin1_General-1252    8bit_Korean-949    UTF16LE_Latin1_General-1252
2                           ₂                  ₂

Seperti yang dapat Anda lihat, Latin1_General Collations, yang menggunakan Code Page 1252 (Halaman Code yang sama yang Modern_Spanishdigunakan Collations) untuk VARCHARdata, tidak memiliki kecocokan persis, tetapi mereka memiliki pemetaan "paling cocok" (yang Anda lihat) ). TAPI, Koleksi Korea, yang menggunakan Kode untuk VARCHARdata, memiliki kecocokan yang tepat untuk karakter "Subskrip 2".


Untuk mengilustrasikan lebih lanjut, kita dapat membuat Database baru dengan Collation default dari salah satu Collations Korea, dan kemudian menjalankan SQL yang ada dalam pertanyaan:

CREATE DATABASE [TestKorean-949] COLLATE Korean_100_CI_AS_KS_WS_SC;
ALTER DATABASE [TestKorean-949] SET RECOVERY SIMPLE;
GO

USE [TestKorean-949];

CREATE TABLE test (
    id INT NOT NULL,
    description NVARCHAR(100) COLLATE Modern_Spanish_CI_AS NOT NULL
);
INSERT INTO test (id, description) VALUES (1, 'CO2');


SELECT * FROM test WHERE id = 1;
UPDATE test SET description = 'CO₂' WHERE id = 1;
SELECT * FROM test WHERE id = 1;

Pengembalian:

id  description
1   CO2


id  description
1   CO₂

MEMPERBARUI

Bagi siapa saja yang tertarik untuk mencari tahu lebih banyak tentang apa sebenarnya yang terjadi di sini (yaitu semua detail berdarah), silakan lihat penyelidikan dua bagian yang baru saja saya posting:

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.