Gunakan huruf kapital hanya untuk huruf pertama dari setiap kata dari setiap kalimat dalam SQL Server

18

Saya ingin menggunakan huruf kapital hanya huruf pertama dari setiap kata dari setiap kalimat dalam kolom SQL.

Misalnya, jika kalimatnya adalah:

'Aku suka film'

maka saya perlu output:

'Aku suka film'

Pertanyaan:

declare @a varchar(15) 

set @a = 'qWeRtY kEyBoArD'

select @a as [Normal text],
upper(@a) as [Uppercase text],
lower(@a) as [Lowercase text],
upper(left(@a,1)) + lower(substring(@a,2,len(@a))) as [Capitalize first letter only]

Di sini saya melakukan huruf besar, kecil, dan huruf besar hanya di kolom saya (di sini saya hanya menempatkan kata acak).

Inilah hasil saya:

masukkan deskripsi gambar di sini

Apakah ada kemungkinan untuk melakukan itu?

Adakah kemungkinan untuk mendapatkan hasil tanpa menggunakan fungsi yang ditentukan pengguna?

Saya butuh hasilnya Qwerty Keyboard

Marin Mohanadas
sumber
11
Mengapa Anda ingin melakukan ini di dalam sql server? Lapisan presentasi Anda harus mengatasinya dengan efisien!
Kin Shah
Anda tidak selalu memiliki lapisan presentasi, misalnya ketika membersihkan data buruk yang diimpor ke SQL Server, dan Anda tidak ingin menulis program C # untuk melakukannya. Ya, Anda bisa berinvestasi dalam fungsi CLR, tetapi bagaimana dengan sesuatu yang cepat dan kotor yang berfungsi.
Jeffrey Roughgarden

Jawaban:

26
declare @a varchar(30); 

set @a = 'qWeRtY kEyBoArD TEST<>&''"X';

select stuff((
       select ' '+upper(left(T3.V, 1))+lower(stuff(T3.V, 1, 1, ''))
       from (select cast(replace((select @a as '*' for xml path('')), ' ', '<X/>') as xml).query('.')) as T1(X)
         cross apply T1.X.nodes('text()') as T2(X)
         cross apply (select T2.X.value('.', 'varchar(30)')) as T3(V)
       for xml path(''), type
       ).value('text()[1]', 'varchar(30)'), 1, 1, '') as [Capitalize first letter only];

Ini pertama-tama mengubah string menjadi XML dengan mengganti semua spasi dengan tag kosong <X/>. Kemudian itu merobek XML untuk mendapatkan satu kata per baris menggunakan nodes(). Untuk mendapatkan kembali baris ke satu nilai itu menggunakan for xml pathtrik.

Mikael Eriksson
sumber
8
Dan kode itu persis mengapa saya tidak akan pernah melakukannya di SQL. Tidak mengatakan jawabannya salah - ini diminta. Tapi SQL standar sangat tidak cocok untuk jenis manipulasi string. Fungsi berbasis CLR akan berfungsi, atau hanya melakukannya pada lapisan presentasi.
TomTom
8
@ TomTom Terlihat rumit tapi itu tidak seberapa dibandingkan dengan rencana kueri yang dihasilkannya dan itu tidak akan cepat dengan standar apa pun. Namun itu mendidik dan menyenangkan untuk menggali apa yang sebenarnya terjadi dalam permintaan dan mengapa itu ditulis seperti itu. Masalahnya bisa diselesaikan dengan fungsi string split (tabel angka). Sulit untuk menghindari for xml pathtrik untuk penggabungan. Kecuali Anda menggunakan CLR yang akan menjadi pilihan terbaik jika kecepatan dan efisiensi penting.
Mikael Eriksson
15

Di SQL Server 2016 Anda bisa melakukan ini dengan R, mis

-- R capitalisation code stolen from here:
-- http://stackoverflow.com/questions/6364783/capitalize-the-first-letter-of-both-words-in-a-two-word-string

EXEC sp_execute_external_script
    @language = N'R',
    @script = N'
simpleCap <- function(x) {
  s <- strsplit(x, " ")[[1]]
  paste(toupper(substring(s, 1,1)), substring(s, 2),
        sep="", collapse=" ")
}             

OutputDataSet <- as.data.frame((sapply(as.vector(InputDataSet$xtext), simpleCap)))',
    @input_data_1 = N'SELECT LOWER(testString) xtext FROM dbo.testStrings'
WITH RESULT SETS ( ( properCase VARCHAR(50) NOT NULL ) );

Apakah Anda harus atau tidak adalah pertanyaan yang berbeda:)

wBob
sumber
oh, kamu pasti tidak seharusnya. Terkadang itu opsi yang paling tidak buruk, atau seperti yang disebutkan OP, mereka membutuhkan yang cepat dan kotor.
Jonathan Fite
12

Mungkin saya bodoh tetapi memeriksa kueri di bawah ini yang saya tulis terhadap beberapa yang disediakan, ini tampaknya sedikit lebih efisien (tergantung pada pengindeksan).

Kode ini agak bodoh, tetapi tidak ada pepatah yang mengatakan bahwa jika terlihat bodoh tetapi berfungsi maka itu tidak bodoh.

Begin

    Declare @text Varchar(30);

    Set @text = 'qWeRtY kEyBoArD TEST<>&''"X';

    Declare @1 Varchar(2)= ' a'
      , @2 Varchar(2)= ' b'
      , @3 Varchar(2)= ' c'
      , @4 Varchar(2)= ' d'
      , @5 Varchar(2)= ' e'
      , @6 Varchar(2)= ' f'
      , @7 Varchar(2)= ' g'
      , @8 Varchar(2)= ' h'
      , @9 Varchar(2)= ' i'
      , @10 Varchar(2)= ' j'
      , @11 Varchar(2)= ' k'
      , @12 Varchar(2)= ' l'
      , @13 Varchar(2)= ' m'
      , @14 Varchar(2)= ' n'
      , @15 Varchar(2)= ' o'
      , @16 Varchar(2)= ' p'
      , @17 Varchar(2)= ' q'
      , @18 Varchar(2)= ' r'
      , @19 Varchar(2)= ' s'
      , @20 Varchar(2)= ' t'
      , @21 Varchar(2)= ' u'
      , @22 Varchar(2)= ' v'
      , @23 Varchar(2)= ' w'
      , @24 Varchar(2)= ' x'
      , @25 Varchar(2)= ' y'
      , @26 Varchar(2)= ' z';

Set @text=' '+@text

    Select  LTrim(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Lower(@text) ,
                                                              @1 , Upper(@1)) ,
                                                              @2 , Upper(@2)) ,
                                                              @3 , Upper(@3)) ,
                                                              @4 , Upper(@4)) ,
                                                              @5 , Upper(@5)) ,
                                                              @6 , Upper(@6)) ,
                                                              @7 , Upper(@7)) ,
                                                              @8 , Upper(@8)) ,
                                                              @9 , Upper(@9)) ,
                                                              @10 , Upper(@10)) ,
                                                              @11 , Upper(@11)) ,
                                                              @12 , Upper(@12)) ,
                                                              @13 , Upper(@13)) ,
                                                              @14 , Upper(@14)) ,
                                                              @15 , Upper(@15)) ,
                                                              @16 , Upper(@16)) ,
                                                              @17 , Upper(@17)) ,
                                                              @18 , Upper(@18)) ,
                                                              @19 , Upper(@19)) ,
                                                              @20 , Upper(@20)) ,
                                                            @21 , Upper(@21)) ,
                                                    @22 , Upper(@22)) , @23 ,
                                            Upper(@23)) , @24 , Upper(@24)) ,
                            @25 , Upper(@25)) , @26 , Upper(@26)));


end
Chris J
sumber
2
Ini jawaban yang hebat, dan mengerikan. Saya terutama menyukai ruang yang Anda tempel di awal dan kemudian menanggalkannya.
BradC
2
@BradC itu mengerikan, tetapi ketika saya sudah mencobanya dibandingkan dengan metode XML terhadap set data tampaknya berjalan di sebagian kecil dari biaya!
Chris J
9

Pilihan lain adalah menangani ini melalui SQLCLR. Bahkan ada metode yang sudah tersedia di .NET yang melakukan ini: TextInfo.ToTitleCase (dalam System.Globalization). Metode ini akan Huruf Besar huruf pertama dari setiap kata, dan Huruf Kecil huruf yang tersisa. Tidak seperti proposal lain di sini, ia juga melompati kata-kata dalam huruf besar semua, dengan asumsi mereka akronim. Tentu saja, jika perilaku ini diinginkan, akan cukup mudah untuk memperbarui saran T-SQL untuk melakukan ini juga.

Salah satu manfaat dari metode .NET adalah dapat menggunakan huruf besar yang merupakan karakter tambahan. Sebagai contoh: SURAT DESERET KECIL OW memiliki pemetaan huruf besar dari DESERET MODAL SURAT OW (keduanya muncul sebagai kotak ketika saya tempelkan ke sini) , tetapi UPPER()fungsinya tidak mengubah versi huruf kecil ke huruf besar, bahkan ketika Collation default untuk Database saat ini diatur ke Latin1_General_100_CI_AS_SC. Ini tampaknya konsisten dengan dokumentasi MSDN yang tidak mencantumkan UPPERdan LOWERdi bagan fungsi yang berperilaku berbeda saat menggunakan _SCCollation: Collation and Unicode Support: Character Tambahan .

SELECT N'DESERET SMALL LETTER OW' AS [Label], NCHAR(0xD801)+NCHAR(0xDC35) AS [Thing]
UNION ALL
SELECT N'DESERET CAPITAL LETTER OW' AS [Label], NCHAR(0xD801)+NCHAR(0xDC0D) AS [Thing]
UNION ALL
SELECT N'SmallButShouldBeCapital' AS [Label], UPPER(NCHAR(0xD801)+NCHAR(0xDC35)) AS [Thing]

Pengembalian (diperbesar sehingga Anda benar-benar dapat melihat Karakter Tambahan):

Hasil kueri yang menunjukkan UPPER () tidak bekerja dengan Karakter Tambahan

Anda dapat melihat daftar lengkap (dan saat ini) karakter yang huruf kecil dan berubah menjadi huruf besar menggunakan fitur pencarian berikut di Unicode.org (Anda dapat melihat Karakter Tambahan dengan menggulir ke bawah sampai Anda sampai ke "DESERET" bagian, atau cukup tekan Control-Fdan cari kata itu):

http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3AChanges_When_Titlecased%3DYes%3A%5D

Meskipun sejujurnya, ini bukan manfaat besar karena diragukan bahwa ada orang yang benar-benar menggunakan Karakter Tambahan yang bisa dijadikan judul. Either way, di sini adalah kode SQLCLR:

using System.Data.SqlTypes;
using System.Globalization;
using Microsoft.SqlServer.Server;

public class TitleCasing
{
    [return: SqlFacet(MaxSize = 4000)]
    [Microsoft.SqlServer.Server.SqlFunction(IsDeterministic = true, IsPrecise = true)]
    public static SqlString TitleCase([SqlFacet(MaxSize = 4000)] SqlString InputString)
    {
        TextInfo _TxtInf = new CultureInfo(InputString.LCID).TextInfo;
        return new SqlString (_TxtInf.ToTitleCase(InputString.Value));
    }
}

Berikut ini adalah saran dari @ MikaelEriksson - dimodifikasi sedikit untuk menangani NVARCHARdata serta melewatkan kata-kata yang semuanya huruf besar (untuk lebih mencocokkan perilaku metode .NET) - bersama dengan uji implementasi T-SQL dan implementasi SQLCLR:

SET NOCOUNT ON;
DECLARE @a NVARCHAR(50);

SET @a = N'qWeRtY kEyBoArD TEST<>&''"X one&TWO '
         + NCHAR(0xD801)+NCHAR(0xDC28)
         + N'pPLe '
         + NCHAR(0x24D0) -- ⓐ  Circled "a"
         + NCHAR(0xFF24) -- D  Full-width "D"
         + N'D u'
         + NCHAR(0x0308) -- ̈  (combining diaeresis / umlaut)
         + N'vU'
         + NCHAR(0x0308) -- ̈  (combining diaeresis / umlaut)
         + N'lA';
SELECT @a AS [Original];

SELECT STUFF((
       SELECT N' '
              + IIF(UPPER(T3.V) <> T3.V COLLATE Latin1_General_100_BIN2, 
                    UPPER(LEFT(T3.V COLLATE Latin1_General_100_CI_AS_SC, 1))
                    + LOWER(STUFF(T3.V COLLATE Latin1_General_100_CI_AS_SC, 1, 1, N'')),
                    T3.V)
       FROM (SELECT CAST(REPLACE((SELECT @a AS N'*' FOR XML PATH('')), N' ', N'<X/>')
                    AS XML).query('.')) AS T1(X)
       CROSS APPLY T1.X.nodes('text()') AS T2(X)
       CROSS APPLY (SELECT T2.X.value('.', 'NVARCHAR(70)')) AS T3(V)
       FOR XML PATH(''), TYPE
       ).value('text()[1]', 'NVARCHAR(70)') COLLATE Latin1_General_100_CI_AS_SC, 1, 1, N'')
                AS [Capitalize first letter only];

SELECT dbo.TitleCase(@a) AS [ToTitleCase];

Hasil kueri menunjukkan output kode T-SQL XML dan ToTitleCase melalui SQLCLR

Perbedaan lain dalam perilaku adalah bahwa implementasi T-SQL khusus ini terbagi pada ruang-ruang saja, sedangkan ToTitleCase()metode ini menganggap sebagian besar non-huruf sebagai pemisah kata (maka perbedaan dalam penanganan bagian "satu & DUA").

Kedua implementasi menangani menggabungkan urutan dengan benar. Masing-masing huruf beraksen dalam "ÜvÜlA" terdiri dari huruf dasar dan kombinasi diaeresis / umlaut (dua titik di atas setiap huruf), dan keduanya dikonversi dengan benar ke kasus lain di kedua pengujian.

Akhirnya, satu kerugian yang tidak terduga dari versi SQLCLR adalah bahwa dengan melakukan berbagai tes, saya menemukan bug dalam kode .NET terkait dengan penanganannya pada Circled Letters (yang sekarang telah dilaporkan di Microsoft Connect - UPDATE: Connect telah pindah ke /dev/null- secara harfiah - jadi saya mungkin perlu mengirim ulang ini jika masalahnya masih ada). Pustaka .NET memperlakukan Circled Letters sebagai pemisah kata, itulah sebabnya ia tidak mengubah "ⓐDD" menjadi "Ⓐdd" sebagaimana mestinya.


FYI

Fungsi SQLCLR pra-selesai mengenkapsulasi TextInfo.ToTitleCasemetode yang disebutkan di atas sekarang tersedia dalam versi Gratis SQL # (yang saya tulis) sebagai String_ToTitleCase dan String_ToTitleCase4k .

😺

Solomon Rutzky
sumber
5

Sebagai alternatif untuk jawaban Mikael Eriksson , Anda dapat mempertimbangkan menggunakan penanganan T-SQL eksklusif dari pengaturan variabel dalam pernyataan pilih multi-baris.

Dalam SQL Server, ketika variabel sedang diset sebagai bagian dari pernyataan SELECT, setiap baris akan menjalankan iterasi dari logika yang ditetapkan.

Orang-orang sering menggunakan metode ini untuk merangkai string, meskipun itu tidak didukung dan ada beberapa masalah yang secara resmi didokumentasikan dengannya . Masalah resmi berkaitan dengan karakteristik ORDER BY tertentu, dan kami tidak memerlukannya di sini, jadi mungkin itu pilihan yang aman.

Di sini, kami mengulangi 26 huruf alfabet dan menggantinya dengan versi huruf besar jika didahului oleh spasi. (Kami menyiapkan string awalnya dengan huruf besar huruf pertama dan membuat huruf kecil sisanya, seperti yang Anda lakukan dalam pertanyaan Anda.)

SQL sedikit rumit karena membutuhkan penggunaan Tally Table - tabel angka - untuk menghasilkan 26 iterasi untuk menggantikan yang dilakukannya. Anda bisa membuat fungsi yang ditentukan pengguna (TVF) bernilai tabel inline berguna untuk menghasilkan tabel angka atau Anda bahkan bisa menggunakan tabel fisik.

Kelemahan dari opsi ini adalah tidak bisa menjadi bagian dari TVF inline karena perlu melibatkan pengaturan variabel. Jadi jika Anda ingin menerapkan metode ini ke kolom output Anda, Anda perlu membungkusnya menjadi TVF multi-pernyataan atau fungsi yang ditentukan pengguna skalar.

Namun, rencana kuerinya jauh lebih sederhana dan mungkin jauh lebih cepat daripada metode XML. Anda bisa berdebat juga lebih mudah untuk dipahami (terutama jika Anda memiliki tabel penghitungan sendiri).

DECLARE
    @a VARCHAR(15) = 'qWeRtY kEyBoArD';

SELECT
    @a = UPPER(LEFT(@a,1)) + LOWER(SUBSTRING(@a,2,LEN(@a)));

WITH TallyTableBase AS
(
    SELECT
        0 AS n
    FROM    (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) AS t(n)
)
SELECT
    @a = REPLACE(@a, ' ' + CHAR(n.n), ' ' + CHAR(n.n))
FROM        (
                SELECT      TOP 26 ROW_NUMBER() OVER (ORDER BY (SELECT 1)) + 64 AS n
                FROM        TallyTableBase a
                CROSS JOIN  TallyTableBase b
            ) AS n;

SELECT
    @a AS [NewValue];

(Saya menguji ini menggunakan string yang jauh lebih besar dan sekitar 6ms vs 14ms untuk solusi XML.)

Ada sejumlah batasan tambahan dengan solusi ini. Seperti yang ditulis, ini mengasumsikan susunan case-case yang tidak sensitif, meskipun Anda bisa menghilangkan masalah itu dengan menentukan collation atau menjalankan LCASE pada istilah pencarian, dengan mengorbankan beberapa kinerja. Itu juga hanya membahas surat-surat ASCII standar dan bergantung pada penempatan mereka di set karakter , sehingga tidak akan melakukan apa pun dengan ñ.

Riley Major
sumber
3

Dengan asumsi Anda hanya ingin menggunakan huruf besar untuk kata-kata setelah spasi, berikut ini adalah cara lain untuk melakukannya.

DECLARE @String VARCHAR(1000)
SET @String = 'qWeRtY kEyBoArD tEst'

/*
Set the string to all lower case and
add a space at the beginning to ensure
the first letter gets capitalized
in the CTE
*/
SET @String = LOWER(' ' + @String)  

/*
Use a Tally "Table" as a means of
replacing the letter after the space
with the capitalize version of the
letter
*/
;WITH TallyTable
AS
(
    SELECT TOP 1000 ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) as N
    FROM master.sys.all_columns a CROSS JOIN master.sys.all_columns b

)
SELECT @String = REPLACE(@String,SUBSTRING(@String,CHARINDEX(' ',@String,N), 2),UPPER(SUBSTRING(@String,CHARINDEX(' ',@String,N), 2)))
FROM TallyTable
WHERE CHARINDEX(' ',@String,N) <> 0

--Remove the space added to the beginning of the string earlier
SET @String = RIGHT(@String,LEN(@String) - 1)
TLaV
sumber
1

Mungkin bukan anti peluru tapi saya harap ini merupakan kontribusi yang bermanfaat untuk utas ini.

DECLARE @t VARCHAR(50) = 'the quick brown fox jumps over the lazy dog', @i INT = 0

DECLARE @chk VARCHAR(1)

WHILE @i <= LEN(@t)
BEGIN
    SELECT @chk=SUBSTRING(@t,@i,1)
        IF @chk = CHAR(32)
        BEGIN
            SET @t = STUFF(@t,@i+1,1,UPPER(SUBSTRING(@t,@i+1,1)))
        END
    SET @i=@i+1
END
PRINT @t
Simon Jones
sumber
0

Di bawah ini adalah prosedur yang saya gunakan dalam basis data Firebird untuk melakukan ini. Mungkin bisa dibersihkan banyak tetapi itu menyelesaikan pekerjaan untuk saya.

set term ~;

Create Procedure EachWordCap

As

Declare Variable lcaption varchar(33);
Declare Variable lcurrentpos integer;
Declare Variable lstringlen integer;
begin
    for select ' ' || trim(lower(imagedata.imagename)) from imagedata
    where imagedata.imagename is not null and imagedata.imagename != ''
    into :lcaption
    do 
    begin
        lcurrentpos = 0;
        lstringlen = char_length(lcaption);
        while (lcurrentpos != 1) do
        begin
            lcurrentpos = position(' ', lcaption, iif(lcurrentpos = 0, 1,lcurrentpos)) + 1 ;
            lcaption = left(lcaption,lcurrentpos - 1) || upper(substring(lcaption from lcurrentpos for 1)) || right(lcaption,lstringlen - lcurrentpos);
        end
        --Put what you want to do with the text in here
    end
end~
set term ;~
Jeffrey Elkins
sumber
0

CTE rekursif cukup baik untuk hal semacam ini.

Mungkin tidak terlalu efisien untuk operasi besar, tetapi memungkinkan untuk jenis operasi ini dalam pernyataan pilih SQL murni:

declare @a varchar(100) 

set @a = 'tHe qUiCk bRoWn FOX jumps   OvEr The lAZy dOG';

WITH [CTE] AS (
  SELECT CAST(upper(Left(@a,1)) + lower(substring(@a,2,len(@a))) AS VARCHAR(100)) AS TEXT,
         CHARINDEX(' ',@a) AS NEXT_SPACE
  UNION ALL
  SELECT CAST(Left(TEXT,NEXT_SPACE) + upper(SubString(TEXT,NEXT_SPACE+1,1)) + SubString(TEXT,NEXT_SPACE+2,1000) AS VARCHAR(100)),
         CHARINDEX(' ',TEXT, NEXT_SPACE+1)
  FROM [CTE]
  WHERE NEXT_SPACE <> 0
)

SELECT TEXT
FROM [CTE]
WHERE NEXT_SPACE = 0

Keluaran:

The Quick Brown Fox Jumps   Over The Lazy Dog
Jerb
sumber
0

Saya suka versi ini. Ini sederhana, dan dapat digunakan untuk membuat fungsi, Anda hanya perlu memiliki versi SQL Server yang tepat:

WITH words
AS (
    SELECT upper(left(Value, 1)) + lower(substring(Value, 2, len(Value))) AS word
    FROM STRING_SPLIT('Lorem ipsum dolor sit amet.', ' ')
    )
SELECT STRING_AGG(words.word, ' ')
FROM words
Cristi
sumber
Versi mana yang benar?
dezso
SQL Server (dimulai dengan 2016)
Cristi
-2
DECLARE @someString NVARCHAR(MAX) = 'In this WHILE LOOP example' 

DECLARE @result NVARCHAR(MAX) =Upper(SUBSTRING(@someString, 1, 1))

DECLARE @index INT =2 

WHILE LEN(@someString)>@index

BEGIN

SET @result= @result+CASE WHEN CHARINDEX(' ',@someString,@index)<>0 THEN LOWER(SUBSTRING(@someString, @index, CHARINDEX(' ',@someString,@index)-@index+1)) +Upper(SUBSTRING(@someString, CHARINDEX(' ',@someString,@index)+1, 1)) ELSE  LOWER(SUBSTRING(@someString,@index, LEN(@someString) )) END

SET @index=CASE WHEN CHARINDEX(' ',@someString,@index)<>0 THEN CHARINDEX(' ',@someString,@index)+2 ELSE  LEN(@someString)+1  END

 END

SELECT  @result 

Saya harap akan membantu ...

Alpha k A
sumber
Selamat datang di Administrator Database! Tolong jelaskan bagaimana pertanyaan Anda memecahkan masalah penulis; jawaban tanpa penjelasan umumnya tidak diterima dengan baik.
Glorfindel
-3

Data Uji

declare @word varchar(100)
with good as (select 'good' as a union select 'nice' union select 'fine')
select @word = (SELECT TOP 1 a FROM good ORDER BY NEWID())

Penerapan

select substring(Upper(@word),1,1) + substring(@word, 2, LEN(@word))
Romiko Derbynew
sumber
Mengapitalisasi kata-kata yang sudah terpisah itu mudah. Saya percaya OP tertarik pada bagaimana mengidentifikasi kata-kata dalam sebuah string, dan memanfaatkannya masing-masing.
Jon of All Trades