【问题标题】:SQL Server: Check Upper Case or Lower Case after certain CharacterSQL Server:在某些字符后检查大写或小写
【发布时间】:2017-11-05 07:05:09
【问题描述】:

我有像'John is my name; Ram is my name; Adam is my name' 这样的数据。

我的规则是; 之后的每个第一个字母都应该是大写字母。

如何选择所有满足规则的值?

【问题讨论】:

  • SQL Server 的哪个版本?
  • @Shnugo Microsoft SQL Server 2012 - 11.0.5058.0 (X64)
  • 这将是一个丑陋的问题,尤其是在分号分隔的术语数量未知的情况下。更好的解决方案是规范化您的数据并将每个名称/句子放在单独的记录中。
  • @TimBiegeleisen 不能有正则表达式解决方案吗?
  • 遗憾的是,SQL Server 以对本机正则表达式支持不佳而闻名。但这使用 C# 或 Java 之类的东西很容易处理。您是否考虑过这样的替代方案?

标签: sql sql-server tsql split sql-server-2012


【解决方案1】:

其他答案展示了如何将行转换为与您的模式匹配的内容。

如果您只想select 与您描述的模式匹配的行,您可以使用patindex()like 与区分大小写的排序规则(或使用collate 来应用)。

这假设除了分号后面的每个字母都必须是大写字母的规则之外,第一个字母也应该是大写字母。如果不是这样,只需删除where 中的第一个子句。

select *
from t
where patindex('[ABCDEFGHIJKLMNOPQRSTUVWXYZ]%', val collate latin1_general_cs_as) = 1
  and patindex('%; [^ABCDEFGHIJKLMNOPQRSTUVWXYZ]%', val collate latin1_general_cs_as) = 0

select *
from t
where val collate latin1_general_cs_as like '[ABCDEFGHIJKLMNOPQRSTUVWXYZ]%'
  and val collate latin1_general_cs_as not like '%; [^ABCDEFGHIJKLMNOPQRSTUVWXYZ]%'

测试设置:

create table t (id int not null identity(1,1),val varchar(256))
insert into t values 
 ('John is my name; Ram is my name; Adam is my name')
,('john is my name; ram is my name; adam is my name')

rextester 演示:http://rextester.com/DBGIS10645

以上都返回:

+----+--------------------------------------------------+
| id |                       val                        |
+----+--------------------------------------------------+
|  1 | John is my name; Ram is my name; Adam is my name |
+----+--------------------------------------------------+

【讨论】:

    【解决方案2】:

    你可以用这样的 XML 技巧来拆分它

    DECLARE @YourString VARCHAR(100)='John is my name; Ram is my name; Adam is my name';
    WITH Splitted AS
    (
        SELECT CAST('<x>' + REPLACE((SELECT REPLACE(@YourString,'; ','$$SplitHere$$') AS [*] FOR XML PATH('')),'$$SplitHere$$','</x><x>')+ '</x>' AS XML) AS Casted
    )
    ,DerivedTable AS
    (
        SELECT  ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS PartNr
               ,x.value(N'text()[1]',N'nvarchar(max)') AS Part
        FROM Splitted
        CROSS APPLY Casted.nodes(N'/x') AS X(x)
    )
    SELECT PartNr
          ,Part
          ,CASE WHEN ASCII(LEFT(Part,1)) BETWEEN ASCII('A') AND ASCII('Z') THEN 1 ELSE 0 END AS FirstIsCapital
    FROM DerivedTable;
    

    结果

    Nr  Part            FirstLetterIsCaptial
    ---------------------------------------- 
    1   John is my name     1
    2   Ram is my name      1
    3   Adam is my name     1
    

    我不知道你的最终目标是什么...查找零件,第一个字母不是大写的?确保您的规则得到满足?

    但是:
    最好的办法是使用它来纠正您的设计并将这些部件放置在与1:n 相关的边桌中

    【讨论】:

    • 使用以下源字符串进行测试:'John is my name; Ram is my name; Adam is my name'
    • @BogdanSahlean ,好吧?我会使用L/RTRIM() ...错误是存储格式...任何解决此问题的代码都将是一个黑客...
    • TRIM 是在 SQL Server 2017 中引入的。如果源字符串是 '!John is my name;!Ram is my name;!Adam is my name;¡Hola!' 怎么办?
    【解决方案3】:

    有点丑陋的解决方案,但你可以试一试......

    Declare @str nvarchar(max) = 'John is my name; Ram is my name; Adam is my name'
    
    Declare @xml as xml
    Set @xml = cast(('<X>'+replace(@str,';' ,'</X><X>')+'</X>') as xml)
    Select * from (
        Select RowN = Row_Number() over (order by (SELECT NULL)), LTrim(RTrim(N.value('.', 'nvarchar(MAX)'))) as value FROM @xml.nodes('X') as T(N) -- this is to split if you are using sql server 2016 you can use string_Split
    ) a
    Where unicode(substring(a.[value],1,1)) = unicode(upper(substring(a.[value],1,1)))
    

    想法是拆分字符串并用unicode值检查它是否在上面

    【讨论】:

    • 这个解决方案有一个很大的缺陷:如果你的@str 包含禁止字符,它将破坏......
    • 试试'John is my name; Jim &amp; Tom are my friends'
    • 得到你...很好的学习,因为我之前没有遇到过这个...谢谢Shnugo...
    【解决方案4】:

    你可以创建一个这样的函数。

    Create FUNCTION SPLITTER (    
        @textData NVARCHAR(MAX),
        @Delimeter NVARCHAR(MAX) ) RETURNS @RtnValue TABLE  (
        Data NVARCHAR(MAX) )  AS BEGIN 
        DECLARE @index INT  DECLARE @data nvarchar(1000)    DECLARE @firstCharacter char
        SET @index = CHARINDEX(@Delimeter,@textData)
    
        WHILE (@index>0)
        BEGIN
                    set @data = LTRIM(RTRIM(SUBSTRING(@textData, 1, @index - 1)))       set @firstCharacter = SUBSTRING(@data,1,1);
                    if UNICODE(@firstCharacter) = UNICODE(upper(@firstCharacter))       begin           INSERT INTO @RtnValue (data) SELECT @data       end;
    
            SET @textData = SUBSTRING(@textData, @index + DATALENGTH(@Delimeter) / 2, LEN(@textData))
    
            SET @index = CHARINDEX(@Delimeter, @textData)
        END
            set @data = @textData   set @firstCharacter = SUBSTRING(@data,1,1);
                if UNICODE(@firstCharacter) = UNICODE(upper(@firstCharacter))   begin       INSERT INTO @RtnValue (data) SELECT @data   end;
    
        RETURN END
    

    这样使用

    select * from SPLITTER ('John 是我的名字; Ram 是我的名字; Adam 是我的名字', ';')

    【讨论】:

      【解决方案5】:

      您可以获取NGrams8K 的副本并执行以下操作:

      -- note that I made the 3rd item start with lower-case
      DECLARE @YourString VARCHAR(100)='John is my name; Ram is my name; adam is my name';
      
      WITH D(n) AS
      (
        SELECT 0 UNION ALL SELECT position 
        FROM dbo.NGrams8k(@yourstring,1) WHERE token = ';'
      ),
      TOKEN(token) AS
      (
        SELECT LTRIM(SUBSTRING(@YourString, N+1, 
                 ISNULL(NULLIF(CHARINDEX(';', @YourString, N+1),0), 101)-(N+1)))
        FROM D
      )
      SELECT token,
             FirstLetterIsCaptial = IIF(ASCII(SUBSTRING(token,1,1)) BETWEEN 65 AND 90, 1, 0)
      FROM TOKEN;
      

      结果

      token              FirstLetterIsCaptial
      ------------------ --------------------
      John is my name    1
      Ram is my name     1
      adam is my name    0
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-04-14
        • 2011-11-30
        • 2010-11-27
        • 1970-01-01
        • 2020-10-21
        • 1970-01-01
        • 2017-06-11
        • 1970-01-01
        相关资源
        最近更新 更多