web-dev-qa-db-ja.com

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

SQL列の各文の各Wordの最初の文字のみを大文字にしたい。

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

'私は映画が好き'

次に、出力が必要です:

'私は映画が好き'

クエリ:

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]

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

これが私の結果です:

enter image description here

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

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

出力が必要ですQwerty Keyboard

18
Marin Mohanadas
_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];
_

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

26
Mikael Eriksson

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

すべきかどうかは別の問題です:)

15
wBob

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

コードは少し愚かですが、それが愚かに見えても動作する場合、それは愚かではないということはありません。

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
13
Chris J

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

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

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

戻り値(実際に補助文字が見えるように拡大されています):

Query result showing UPPER() not working with Supplementary Character

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データを処理するように少し変更され、すべて大文字の単語をスキップします( .NETメソッド)-そのT-SQL実装と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];
_

Query result showing output of T-SQL XML code and ToTitleCase via SQLCLR

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

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

最後に、SQLCLRバージョンの予想外の欠点の1つは、さまざまなテストを考えたときに、.NETコードに丸付き文字の処理に関連するバグが見つかったことです(これは で報告されています on Microsoft Connect —更新:Connectは_/dev/null_-文字通り-に移動されました。問題がまだ存在する場合は再送信する必要があるかもしれません)。 .NETライブラリは円で囲まれた文字を単語の区切り文字として扱います。そのため、 "ⓐDD"が "Ⓐdd"に変換されません。


ご参考までに

上記の_TextInfo.ToTitleCase_メソッドをカプセル化する事前に完了したSQLCLR関数が、 SQL# の無料バージョンでString_ToTitleCaseおよびString_ToTitleCase4k.

????

9
Solomon Rutzky

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ソリューションでは約6ms対14msでした。)

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

5
Riley Major

スペースの後に続く単語を大文字にすることだけを考えていると仮定すると、これは別の方法です。

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)
3
TLaV

防弾ではないかもしれませんが、このスレッドへの貢献を願っています。

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
1
Simon Jones

@ChrisJソリューションに基づいて、パフォーマンスを改善する単純なインラインテーブル値関数を次に示します。必要に応じて、REPLACEを追加して、特殊文字(句読点や制御文字)を処理できます。これは(確かにクールな)CTEソリューションよりはるかに優れています(PlanExplorerごとに、上の行はCTE、下の行はREPLACEです)。

enter image description here

CREATE FUNCTION fSelectInitialCaps(@InputString VARCHAR(MAX))
RETURNS TABLE
AS
RETURN
/*  Example usage:

    SELECT TOP(100)
        c.Company
        , company.OutputString
    FROM dbo.Contacts c
        CROSS APPLY dbo.fSelectInitialCaps(c.Company) company
    WHERE CHARINDEX(' ', c.Company) > 0;
*/
SELECT [OutputString] = 
    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(' ' + @InputString) ,
        ' a', Upper (' A')) ,
        ' b', Upper (' B')) ,
        ' c', Upper (' C')) ,
        ' d', Upper (' D')) ,
        ' e', Upper (' E')) ,
        ' f', Upper (' F')) ,
        ' g', Upper (' G')) ,
        ' h', Upper (' H')) ,
        ' i', Upper (' I')) ,
        ' j' , Upper(' J')) ,
        ' k' , Upper(' K')) ,
        ' l' , Upper(' L')) ,
        ' m' , Upper(' M')) ,
        ' n' , Upper(' N')) ,
        ' o' , Upper(' O')) ,
        ' p' , Upper(' P')) ,
        ' q' , Upper(' Q')) ,
        ' r' , Upper(' R')) ,
        ' s' , Upper(' S')) ,
        ' t' , Upper(' T')) ,
        ' u' , Upper(' U')) ,
        ' v' , Upper(' V')) , 
        ' w' , Upper(' W')) , 
        ' x' , Upper(' X')) ,
        ' y' , Upper(' Y')) , 
        ' z' , Upper(' Z')))
;
GO
0
Russell Fox

以下は、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
Jeffrey Elkins

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

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

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
Jerb

このバージョンが好きです。これは単純で、関数の作成に使用できます。SQLServerの適切なバージョンが必要です。

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
0
Cristi