SQL Serverの各文の各単語の最初の文字のみを大文字にする


18

SQL列の各文の各単語の最初の文字のみを大文字にします。

たとえば、文が次の場合:

'私は映画が好き'

次に出力が必要です:

'私は映画が好き'

クエリ:

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]

ここでは、自分の列でのみ最初の文字を大文字、小文字、大文字にしました(ここではランダムな単語を入力しています)。

私の結果は次のとおりです。

ここに画像の説明を入力してください

それを行う可能性はありますか?

ユーザー定義関数を使用せずに結果を取得する可能性はありますか?

出力が必要です Qwerty Keyboard


11
なぜあなたはSQLサーバー内でこれをしたいのですか?プレゼンテーション層はそれを効率的に処理する必要があります!
キンシャー

たとえば、SQL Serverにインポートされた不良データをクリーンアップするときなど、プレゼンテーションレイヤーが常にあるとは限らず、そのためのC#プログラムを作成する必要はありません。はい、CLR関数に投資することもできますが、迅速で汚れた機能はどうでしょうか。
ジェフリーラフガーデン

回答:


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];

これは、最初にすべてのスペースを空のタグで置き換えることにより、文字列をXMLに変換します<X/>。次に、XMLを細断して、を使用して行ごとに1つの単語を取得しnodes()ます。行を1つの値に戻すには、for xml pathトリックを使用します。


8
そして、そのコードがまさにSQLでそれをしない理由です。答えが間違っていると言っていない-これが求められた。しかし、標準SQLは、このタイプの文字列操作にはとてつもなく不適切です。CLRベースの機能が機能するか、プレゼンテーション層で機能します。
トムトム

8
@TomTomそれは複雑に見えますが、それはそれが生成するクエリプランと比較して何もありません、そして、それはどんな標準によっても速くありません。しかし、クエリで実際に何が行われているか、なぜそのように記述されているのかを掘り下げることは、教育的で楽しいことです。この問題は、文字列分割関数(数値テーブル)で解決できます。for xml path連結のトリックを避けるのは難しい。速度と効率が重要な場合に最適なCLRを選択しない限り。
ミカエルエリクソン

15

SQL Server 2016では、Rを使用してこれを実行できます。たとえば、

-- 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 ) );

あなたがすべきかどうかは別の質問です:)


ああ、絶対にすべきではありません。時にはそれが最も悪いオプションであるか、OPが述べたように、彼らは迅速で汚れたものを必要とします。
ジョナサンファイト

12

たぶん私は愚かですが、提供されたいくつかに対して書いた以下のクエリをチェックすると、これはもう少し効率的です(インデックス付けに依存します)。

このコードは少し馬鹿げていますが、馬鹿げているように見えても機能する場合は馬鹿ではないということわざはありません。

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

2
これは素晴らしい、そして恐ろしい答えです。私は特にあなたが最初にタックしたスペースが好きで、それから最後にストリップします。
BradC

2
@BradCは恐ろしいですが、データセットに対するXMLメソッドと比較してみたところ、わずかなコストで実行されるようです!
クリスJ

9

別のオプションは、SQLCLRを介してこれを処理することです。これを行う.NETで既に利用可能なメソッドもあります:TextInfo.ToTitleCase(inSystem.Globalization)。この方法は、各単語の最初の文字を大文字にし、残りの文字を小文字にします。ここにある他の提案とは異なり、頭字語であると仮定して、すべて大文字の単語もスキップします。もちろん、この動作が必要な場合は、T-SQLの提案を更新してこれを行うことも簡単です。

.NETメソッドの利点の1つは、補助文字である大文字を使用できることです。例:DESERET SMALL LETTER OWにDESERET CAPITAL LETTER OWの 大文字マッピングがあります(ここに貼り付けると両方ともボックスとして表示されます)が、UPPER()関数は小文字バージョンを大文字に変更しません。現在のデータベースのデフォルトの照合はに設定されLatin1_General_100_CI_AS_SCます。これは、Collat​​ion:Collat​​ionおよびUnicode Support:Supplementary Charactersを使用した場合に異なる動作をする関数のリストUPPERおよびリストにないMSDNドキュメントと一致してLOWERいるようです。_SC

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]

戻り値(実際に補助文字を見ることができるように拡大):

UPPER()が補助文字で機能しないことを示すクエリ結果

Unicode.orgの次の検索機能を使用して、小文字で大文字に変更される(および現在の)文字の完全なリストを表示できます(「DESERET」に到達するまで下にスクロールすると、補助文字を表示できます)セクション、または単にヒットControl-Fしてその単語を検索します):

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

正直に言って、これは大きな利点ではありません。タイトルケースに入れることができる補助文字を誰かが実際に使用していることは疑わしいからです。いずれにしても、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));
    }
}

ここでMikaelErikssonの提案@ -ハンドルをわずかに変更NVARCHARそのT-SQL実装の試験と一緒の-データだけでなく、すべて大文字であるスキップ単語(より密接.NETメソッドの挙動と一致するように) 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];

SQLCLRを介したT-SQL XMLコードとToTitleCaseの出力を示すクエリ結果

動作のもう1つの違いは、この特定のT-SQL実装はスペースのみで分割されるのに対し、ToTitleCase()メソッドはほとんどの非文字を単語区切り文字と見なすことです(したがって、 "one&TWO"部分の処理の違い)。

両方の実装は、シーケンスの結合を正しく処理します。「üvÜlA」の各アクセント付き文字は、ベース文字と分音記号/ウムラウト(各文字の上の2つのドット)で構成され、両方のテストで正しく他のケースに変換されます。

最後に、SQLCLRバージョンの予期せぬ短所の1つは、さまざまなテストを考え出す中で、サークルレターの処理に関連する.NETコードのバグを発見したことです(Microsoft Connectで報告されています— UPDATE:Connectは/dev/null-文字通り-に移動したので、問題がまだ存在する場合はこれを再送信する必要があります)。.NETライブラリは、円で囲まれた文字を単語の区切り文字として扱います。そのため、「ⓐDD」は本来の「Ⓐdd」に変換されません。


ご参考までに

TextInfo.ToTitleCase上記のメソッドをカプセル化する事前に実行されたSQLCLR関数は、String ToTitleCaseおよびString_ToTitleCase4kとして、SQL#の無料版(私が書いた)で利用できるようになりました

😺


5

Mikael Erikssonの回答の代替として、複数行のselectステートメントで変数設定の独自のT-SQL処理を使用することを検討できます。

SQL Serverでは、変数がSELECTステートメントの一部として設定されている場合、各行は設定されたロジックの反復を実行します。

人々はしばしばこのメソッド使用して文字列を連結しますが、サポートされておらず、公式に文書化された問題いくつかあります。公式の問題は、特定のORDER BYの特性に関連しているため、ここでは必要ありません。したがって、おそらく安全なオプションです。

ここでは、アルファベットの26文字を反復処理し、先頭にスペースが付いている場合は大文字に置き換えます。(最初の文字を大文字にして残りを小文字にして、最初に文字列を準備します。これは質問で行ったとおりです。)

SQLは、実行中の置換の26回の反復を生成するために、集計テーブル(数値のテーブル)を使用する必要があるため、少し複雑です。便利なインラインテーブル値ユーザー定義関数(TVF)を作成して、その数値のテーブルを作成したり、物理テーブルを使用することもできます。

このオプションの欠点は、変数を設定する必要があるため、インラインTVFの一部になれないことです。したがって、このメソッドを出力の列に適用する場合は、マルチステートメントTVFまたはスカラーユーザー定義関数にラップする必要があります。

ただし、クエリプランははるかに単純であり、おそらくXMLメソッドよりも大幅に高速です。理解しやすいと主張することもできます(特に、独自の集計テーブルがある場合)。

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];

(はるかに大きな文字列を使用してこれをテストしましたが、XMLソリューションでは約6ミリ秒でしたが、14ミリ秒でした。)

このソリューションには、いくつかの追加の制限があります。書かれているように、大文字と小文字を区別しない照合を想定していますが、パフォーマンスを犠牲にして照合を指定するか、検索語でLCASEを実行することにより、この問題を排除できます。また、標準のASCII文字のみに対応し、文字セットでの配置に依存するため、ñでは何もしません。


3

スペースに続く単語のみを大文字にしたい場合、別の方法があります。

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)

1

防弾ではないかもしれませんが、このスレッドに役立つことを願っています。

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

0

以下は、Firebirdデータベースでこれを行うために使用した手順です。おそらく多くのことを片付けることができますが、それは私のために仕事を成し遂げました。

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 ;~

0

再帰CTEは、この種のことには非常に適しています。

おそらく大規模な操作では特に効率的ではありませんが、純粋なSQL selectステートメントでこの種の操作を許可します。

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

出力:

The Quick Brown Fox Jumps   Over The Lazy Dog

0

私はこのバージョンが好きです。シンプルで、関数を作成するために使用できます。正しいバージョンのSQL Serverが必要です。

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

適切なバージョンはどれですか?
dezso

SQL Server(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 

私が助けてくれることを願っています...


データベース管理者へようこそ!クエリが著者の問題をどのように解決するか説明してください。説明のない回答は、一般的に受け入れられません。
グロルフィンデル

-3

テストデータ

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())

実装

select substring(Upper(@word),1,1) + substring(@word, 2, LEN(@word))

すでに分離されている単語を大文字にするのは簡単です。OPは、文字列内の単語を識別し、各単語を大文字にする方法に関心があると思います。
すべての取引のジョン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.