SQL Server 中每个句子的每个单词的第一个字母只大写

Mar*_*das 18 sql-server sql-server-2014

我只想将 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]
Run Code Online (Sandbox Code Playgroud)

在这里,我只在我的专栏中做了大写、小写和大写第一个字母(这里我只放了一个随机单词)。

这是我的结果:

在此处输入图片说明

有没有可能做到这一点?

有没有可能在不使用用户定义的函数的情况下获得结果?

我需要输出 Qwerty Keyboard

Mik*_*son 27

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];
Run Code Online (Sandbox Code Playgroud)

这首先通过用空标记替换所有空格将字符串转换为 XML <X/>。然后它使用nodes(). 为了让行回到一个值,它使用了这个for xml path技巧。

  • @TomTom 它看起来很复杂,但与它生成的查询计划相比,这算不了什么,而且按照任何标准都不会很快。然而,深入了解查询中实际发生的事情以及为什么这样编写它是有教育意义和**有趣的**。该问题可以通过字符串拆分函数(数字表)解决。很难避免用于连接的“for xml path”技巧。除非您选择 CLR,否则如果速度和效率很重要,这将是最佳选择。 (9认同)
  • 这段代码正是我永远不会在 SQL 中这样做的原因。并不是说答案是错误的 - 这是被要求的。但是标准 SQL 非常不适合这种类型的字符串操作。基于 CLR 的函数可以工作,或者只是在表示层上执行。 (8认同)

wBo*_*Bob 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 ) );
Run Code Online (Sandbox Code Playgroud)

你是否应该是一个不同的问题:)


Chr*_*s J 14

也许我很傻,但是检查我针对提供的一些查询编写的以下查询,这似乎更有效(取决于索引)。

代码有点蠢,但不是有句话说,如果它看起来很蠢,但它有效,那么它就不蠢了。

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
Run Code Online (Sandbox Code Playgroud)

  • 这是一个伟大而可怕的答案。我特别喜欢你在开始时加上然后在最后剥离的空间。 (3认同)
  • @BradC 它很可怕,但是当我尝试将它与针对数据集的 XML 方法进行比较时,它似乎只需花费一小部分成本即可运行! (2认同)

Sol*_*zky 9

另一种选择是通过 SQLCLR 处理此问题。.NET 中甚至已经有一种方法可以执行此操作:TextInfo.ToTitleCase(在 中System.Globalization)。此方法将大写每个单词的第一个字母,并小写其余字母。与这里的其他提议不同,它还跳过全部大写的单词,假设它们是首字母缩略词。当然,如果需要这种行为,更新任何 T-SQL 建议也很容易做到这一点。

.NET 方法的一个好处是它可以使用大写字母作为补充字符。例如:DESERET SMALL LETTER OW有一个DESERET CAPITAL LETTER OW 的大写映射(当我将它们粘贴到这里时它们都显示为框),但该UPPER()函数不会将小写版本更改为大写,即使在当前数据库的默认排序规则设置为Latin1_General_100_CI_AS_SC。这似乎与MSDN文档它不会列出一致UPPER,并LOWER使用一个时不同行为的功能在图表中_SC整理:排序规则和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]
Run Code Online (Sandbox Code Playgroud)

返回(放大以便您可以实际看到补充字符):

查询结果显示 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));
    }
}
Run Code Online (Sandbox Code Playgroud)

这是@MikaelEriksson 的建议——稍微修改以处理NVARCHAR数据以及跳过全部大写的单词(以更紧密地匹配 .NET 方法的行为)——以及对该 T-SQL 实现和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));
    }
}
Run Code Online (Sandbox Code Playgroud)

查询结果显示通过 SQLCLR 输出 T-SQL XML 代码和 ToTitleCase

另一个行为差异是这个特定的 T-SQL 实现仅在空格上拆分,而该ToTitleCase()方法将大多数非字母视为单词分隔符(因此处理“one&TWO”部分的差异)。

两种实现都正确处理组合序列。“u?vU?lA”中的每个重音字母都由一个基本字母和一个组合的分音符/变音符号(每个字母上方的两个点)组成,并且在两个测试中它们都正确地转换为另一种情况。

最后,SQLCLR 版本的一个意想不到的缺点是,在进行各种测试时,我在 .NET 代码中发现了一个与其处理带圆圈字母相关的错误(现在已在 Microsoft Connect 上报告— 更新:Connect 已被移至/dev/null- 从字面上看 - 所以如果问题仍然存在,我可能需要重新提交)。.NET 库将带圆圈的字母视为单词分隔符,这就是为什么它没有将“??D”变成“??d”的原因。


供参考

封装了TextInfo.ToTitleCase上述方法的预先完成的 SQLCLR 函数现在在SQL#(我编写的)的免费版本中可用,作为String_ToTitleCaseString_ToTitleCase4k


Ril*_*jor 5

作为Mikael Eriksson's answer的替代方案,您可以考虑在多行选择语句中使用专有的 T-SQL 处理变量设置。

在 SQL Server 中,当变量被设置为 SELECT 语句的一部分时,每一行都将执行设置逻辑的迭代。

人们经常使用这种方法来连接字符串,尽管它不受支持并且有一些官方记录的问题。官方问题与特定的 ORDER BY 特性有关,我们在这里不需要它,所以也许这是一个安全的选择。

在这里,我们遍历字母表中的 26 个字母,如果它们前面有一个空格,则用大写版本替换它们。(我们最初通过将第一个字母大写并将其余字母小写来准备字符串,就像您在问题中所做的那样。)

SQL 有点复杂,因为它需要使用 Tally 表(一个数字表)来生成它正在执行的 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];
Run Code Online (Sandbox Code Playgroud)

(我使用更大的字符串对此进行了测试,对于 XML 解决方案,它大约为 6 毫秒与 14 毫秒。)

此解决方案还有许多其他限制。正如所写的那样,它假定不区分大小写的排序规则,尽管您可以通过指定排序规则或在搜索词上运行 LCASE来消除该问题,但会牺牲一些性能。它也只处理标准的 ASCII 字母并依赖于它们在字符集中的位置,所以它对 ñ 没有任何作用。