Fungsi LEN tidak termasuk spasi tambahan di SQL Server

109

Saya memiliki tabel uji berikut di SQL Server 2005:

CREATE TABLE [dbo].[TestTable]
(
 [ID] [int] NOT NULL,
 [TestField] [varchar](100) NOT NULL
) 

Diisi dengan:

INSERT INTO TestTable (ID, TestField) VALUES (1, 'A value');   -- Len = 7
INSERT INTO TestTable (ID, TestField) VALUES (2, 'Another value      '); -- Len = 13 + 6 spaces

Ketika saya mencoba menemukan panjang TestField dengan fungsi SQL Server LEN (), itu tidak menghitung spasi tambahan - misalnya:

-- Note: Also results the grid view of TestField do not show trailing spaces (SQL Server 2005).
SELECT 
 ID, 
 TestField, 
 LEN(TestField) As LenOfTestField, -- Does not include trailing spaces
FROM 
 TestTable

Bagaimana cara menyertakan spasi tambahan dalam hasil panjang?

Jason Snelders
sumber
1
Saya pikir solusi sebenarnya di sini mungkin bagi Microsoft untuk memperbaiki perangkat lunak mereka yang rusak. Pilih di sini: feedback.azure.com/forums/908035-sql-server/suggestions/…
QA Collective

Jawaban:

125

Ini dengan jelas didokumentasikan oleh Microsoft di MSDN di http://msdn.microsoft.com/en-us/library/ms190329(SQL.90).aspx , yang menyatakan LEN "mengembalikan jumlah karakter dari ekspresi string yang ditentukan, tidak termasuk membuntuti kosong ". Namun demikian, detail yang mudah dilewatkan jika Anda tidak waspada.

Anda harus menggunakan fungsi DATALENGTH - lihat http://msdn.microsoft.com/en-us/library/ms173486(SQL.90).aspx - yang "mengembalikan jumlah byte yang digunakan untuk mewakili ekspresi apa pun".

Contoh:

SELECT 
    ID, 
    TestField, 
    LEN(TestField) As LenOfTestField,           -- Does not include trailing spaces
    DATALENGTH(TestField) As DataLengthOfTestField      -- Shows the true length of data, including trailing spaces.
FROM 
    TestTable
Jason Snelders
sumber
52
CATATAN: Untuk DATALENGTHAnda juga perlu membagi hasil dengan 2 jika ekspresi yang diuji adalah tipe karakter lebar (Unicode; nchar, nvarchar atau ntext), karena hasilnya dalam byte , bukan karakter .
devstuff
7
Juga untuk varchardll. Ini dapat bergantung pada pemeriksaan dan bahkan pembagian lurus ke depan dengan 2 tidak dapat diandalkan. Lihat contoh di sini
Martin Smith
18
Saya akan menggunakan LEN(REPLACE(expr, ' ', '_')). Ini harus bekerja dengan varchardan nvarchardan string yang berisi karakter kontrol unicode khusus.
Olivier Jacot-Descombes
6
-1, DATALENGTH()tidak boleh dianggap sebagai cara alternatif untuk menghitung karakter karena ini menghitung byte, bukan karakter, dan ini penting saat mewakili string yang sama di VARCHAR/ NVARCHAR.
binki
5
Mulai dari SQL server 2012, kolom unicode dengan pemeriksaan versi 100 sekarang mendukung pasangan pengganti. Ini berarti satu karakter dapat menggunakan hingga 4 byte, menyebabkan trik bagi dengan dua gagal. Lihat msdn .
Frédéric
85

Anda bisa menggunakan trik ini:

LEN (Str + 'x') - 1

Serge
sumber
15
Bisakah Anda mencerahkan kami dengan alternatif yang lebih baik? Datalength pasti tidak.
Serge
15
Saya sangat tidak setuju bahwa menggunakan metode yang tidak konsisten (dalam beberapa kasus Anda membagi hasilnya dengan 2 dan terkadang tidak) adalah pilihan yang lebih baik. Mungkin apakah ada kinerja yang mendekati nol dengan metode saya.
Serge
5
Metode @usr Serge adalah yang terbaik, IMHO. Sederhana dan elegan. DATALENGTH rumit: tergantung tipe byte tunggal / ganda, pemeriksaan / bahasa tergantung, dll.
Tn. TA
10
Ini adalah solusi terbaik dan elegan sejauh ini. Saya tidak terlalu peduli apakah RASA seperti hack atau tidak (coding bukan tentang perasaan), saya sangat peduli dengan fakta bahwa solusi ini tidak memiliki efek samping. Saya dapat mengubah tipe data varchar / nvarchar dan masih berfungsi. Kerja bagus.
Mike Keskinov
5
Ada peringatan karena efek samping ini. Jika Anda bekerja dengan variabel berjenis nvarchar (4000), dan variabel Anda berisi 4000 karakter string, karakter yang ditambahkan akan diabaikan, dan Anda akan mendapatkan hasil yang salah (len SQL yang mengabaikan spasi tambahan, dikurangi 1 Anda mengurangi).
kapak - dilakukan dengan SOverflow
17

Saya menggunakan metode ini:

LEN(REPLACE(TestField, ' ', '.'))

Saya lebih suka ini daripada DATALENGTH karena ini berfungsi dengan tipe data yang berbeda, dan saya lebih suka menambahkan karakter di akhir karena Anda tidak perlu khawatir tentang kasus tepi di mana string Anda sudah berada pada panjang maksimal.

Catatan: Saya akan menguji performanya sebelum menggunakannya terhadap kumpulan data yang sangat besar; meskipun saya baru saja mengujinya terhadap 2 juta baris dan itu tidak lebih lambat dari LEN tanpa REPLACE ...

TTT
sumber
14

"Bagaimana cara memasukkan spasi tambahan dalam hasil panjang?"

Anda meminta seseorang untuk mengajukan permintaan peningkatan SQL Server / laporan bug karena hampir semua solusi yang terdaftar untuk masalah yang sangat sederhana ini memiliki beberapa kekurangan atau tidak efisien. Ini tampaknya masih berlaku di SQL Server 2012. Fitur pemangkasan otomatis mungkin berasal dari ANSI / ISO SQL-92 tetapi tampaknya ada beberapa lubang (atau kurangnya menghitungnya).

Harap beri suara "Tambahkan setelan sehingga LEN menghitung spasi kosong" di sini:

https://feedback.azure.com/forums/908035-sql-server/suggestions/34673914-add-setting-so-len-counts-trailing-whitespace

Tautan koneksi yang dihentikan: https://connect.microsoft.com/SQLServer/feedback/details/801381

crokusek.dll
sumber
2
The datalengthsolusi bahkan lebih buruk mulai dari SQL Server 2012, karena itu sekarang mendukung pasangan pengganti di UTF-16, yang berarti karakter dapat menggunakan hingga 4 bytes. Sudah saatnya mereka memperbaiki lenfungsi agar sesuai dengan ANSI, atau setidaknya menyediakan fungsi khusus untuk menghitung karakter termasuk spasi tambahan.
Frédéric
1
Tautan umpan balik perlu digunakan lebih banyak untuk ini. Sungguh mengherankan bahwa masalah ini hanya dapat dicari melalui internet. Saya menghabiskan hampir 2 jam mencoba mencari tahu di mana saya telah membuat kesalahan dalam kode saya sendiri bahkan sebelum mempertimbangkan bahwa fungsi LEN () adalah penyebab pemutusan sambungan saya.
Takophiliac
Saya setuju dengan ini tetapi harus mengizinkan parameter untuk memangkas spasi putih .. karena membuat perbandingan string dengan EF jauh lebih mudah, tidak harus memeriksa apakah ada spasi kosong yang disertakan saat ekspresi iquerizable dibangun.
ganjeii
9

Ada masalah dengan dua jawaban pilihan teratas. Jawaban yang direkomendasikan DATALENGTHrentan terhadap kesalahan programmer. Hasil dari DATALENGTHharus dibagi 2 untuk NVARCHARtipe, tetapi tidak untuk VARCHARtipe. Ini membutuhkan pengetahuan tentang jenis yang Anda dapatkan panjangnya, dan jika jenis itu berubah, Anda harus rajin mengubah tempat yang Anda gunakan DATALENGTH.

Ada juga masalah dengan jawaban yang paling banyak dipilih (yang saya akui adalah cara yang saya pilih untuk melakukannya sampai masalah ini menggigit saya). Jika hal yang Anda dapatkan panjangnya adalah tipe NVARCHAR(4000), dan itu benar-benar berisi string 4000 karakter, SQL akan mengabaikan karakter yang ditambahkan daripada secara implisit menampilkan hasilnya NVARCHAR(MAX). Hasil akhirnya adalah panjang yang salah. Hal yang sama akan terjadi dengan VARCHAR (8000).

Apa yang saya temukan berfungsi, hampir secepat lama biasa LEN, lebih cepat daripada LEN(@s + 'x') - 1string besar, dan tidak mengasumsikan lebar karakter yang mendasarinya adalah sebagai berikut:

DATALENGTH(@s) / DATALENGTH(LEFT(LEFT(@s, 1) + 'x', 1))

Ini mendapatkan datalength, dan kemudian membaginya dengan datalength karakter tunggal dari string. Penambahan 'x' mencakup kasus di mana string kosong (yang akan memberikan pembagian dengan nol dalam kasus itu). Ini berfungsi baik @situ VARCHARatau NVARCHAR. Melakukan LEFTof 1 karakter sebelum append memotong beberapa waktu ketika stringnya besar. Masalahnya dengan ini, adalah bahwa itu tidak bekerja dengan benar dengan string yang mengandung pasangan pengganti.

Ada cara lain yang disebutkan dalam komentar untuk jawaban yang diterima, menggunakan REPLACE(@s,' ','x'). Teknik itu memberikan jawaban yang benar, tetapi beberapa kali lipat lebih lambat daripada teknik lain ketika stringnya besar.

Mengingat masalah yang diperkenalkan oleh pasangan pengganti pada teknik apa pun yang digunakan DATALENGTH, menurut saya metode teraman yang memberikan jawaban benar yang saya ketahui adalah sebagai berikut:

LEN(CONVERT(NVARCHAR(MAX), @s) + 'x') - 1

Ini lebih cepat daripada REPLACEtekniknya, dan jauh lebih cepat dengan senar yang lebih panjang. Pada dasarnya teknik ini adalah LEN(@s + 'x') - 1teknik, tetapi dengan perlindungan untuk kasus tepi di mana string memiliki panjang 4000 (untuk nvarchar) atau 8000 (untuk varchar), sehingga jawaban yang benar diberikan bahkan untuk itu. Ini juga harus menangani string dengan pasangan pengganti dengan benar.

kapak - dilakukan dengan SOverflow
sumber
1
Sayangnya, jawaban ini tidak lagi berfungsi untuk string yang berisi pasangan pengganti di SQL Server 2012. Menjalankan operasi Anda pada N'x𤭢x' COLLATE Latin1_General_100_CI_AS_SCmemberikan 4, sementara LENmemberikan 3.
Douglas
9
@ Douglas - Itu informasi yang berguna. Andai saja Microsoft memberi kami versi LEN yang tidak mengabaikan spasi tambahan.
kapak - dilakukan dengan SOverflow
5

Anda juga perlu memastikan bahwa data Anda benar-benar disimpan dengan trailing blank. Saat ANSI PADDING MATI (non-default):

Trailing blank dalam nilai karakter yang dimasukkan ke dalam kolom varchar akan dipangkas.

Remus Rusanu
sumber
3
Saya rasa Anda sebaiknya tidak mematikan ANSI PADDING karena pengaturan ini sudah usang. Memiliki nilai yang tidak standar menyebabkan banyak masalah kecil.
usr
4

LEN memotong spasi di belakang secara default, jadi menurut saya ini berfungsi saat Anda memindahkannya ke depan

(LEN (MUNDUR (TestField))

Jadi kalau mau, bisa dibilang

SELECT
t.TestField,
LEN(REVERSE(t.TestField)) AS [Reverse],
LEN(t.TestField) AS [Count]
FROM TestTable t
WHERE LEN(REVERSE(t.TestField)) <> LEN(t.TestField)

Jangan gunakan ini untuk spasi utama tentunya.

Joey
sumber
9
Sekarang trims terkemuka spasi, bukan spasi. Hari yang sama, masalah yang berbeda :)
Reversed Engineer
@DaveBoltman Saran saya mungkin masih lebih berbelit-belit, tetapi Anda juga dapat membandingkan dengan panjang TRIM'ed.
Brian J
Ini membalikkan bug di mana spasi di depan tidak dihitung, bukan spasi di belakangnya. Lihat kode berikut: declare @TestField varchar(10); SET @TestField = ' abc '; -- Length with spaces is 5. select LEN(REVERSE(@TestField)) -- Returns 4 select LEN(@TestField) -- Returns 4
Metalogic
1

Anda harus menentukan fungsi CLR yang mengembalikan bidang Panjang String, jika Anda tidak menyukai penggabungan string. Saya gunakan LEN('x' + @string + 'x') - 2dalam kasus penggunaan produksi saya.

obratim
sumber
0

Jika Anda tidak menyukai DATALENGTHkarena kekhawatiran n / varchar, bagaimana dengan:

select DATALENGTH(@var)/isnull(nullif(DATALENGTH(left(@var,1)),0),1)

yang adil

select DATALENGTH(@var)/DATALENGTH(left(@var,1))

dibungkus dengan perlindungan bagi-dengan-nol.

Dengan membaginya dengan DATALENGTH karakter tunggal, kita mendapatkan panjangnya dinormalisasi.

(Tentu saja, masih masalah dengan pasangan pengganti jika itu menjadi perhatian.)

dsz
sumber
-4

gunakan SELECT DATALENGTH ('string')

aman6496
sumber
2
Anda baru saja menyatakan kembali jawaban orang lain dari 7 tahun sebelumnya dan tidak memberikan sesuatu yang baru atau bahkan menjelaskan apa yang Anda jawab atau bagaimana jawabannya menjawab pertanyaan itu.
Jpsh