【问题标题】:SQL- Extracting Text between charactersSQL-提取字符之间的文本
【发布时间】:2018-07-17 19:55:01
【问题描述】:

这就是我的数据的样子。 (我正在尝试准确的电子邮件地址,以便我可以为 TO 和 CC 人发送电子邮件。)

    EmailTO:[url=mailto:Test_Email_1@Yahoo.com] Test_Email_1@Yahoo.com[/url]             
    EmailCC:[url=mailto:Test_Email_2@Yahoo.com] Test_Email_2@Yahoo.com[/url]           

    Hello, This is the rest of the email message....

当我运行第一个 SQL 时,我得到了我想要的结果。

    Select
    Body,
    SUBSTRING(Body, CHARINDEX('EmailTO', Body) + 20,CHARINDEX(']',Body)-CHARINDEX('EmailTO',Body)-20) ToEmail

返回

    ToEmaiL = Test_Email_1@Yahoo.com

但是当我尝试像这样进行第二次 SUBSTRING 时

    Select
    Body,
    SUBSTRING(Body, CHARINDEX('EmailTO', Body) + 20,CHARINDEX(']',Body)-CHARINDEX('EmailTO',Body)-20) ToEmail,
    SUBSTRING(Body, CHARINDEX('EmailCC', Body) + 20,CHARINDEX(']',Body)-CHARINDEX('EmailCC',Body)-20) CCEmail --(Simply replacing the EmailTo from the previous line to EmailCC)
    From hdIssues   

我收到此错误

    "Msg 537, Level 16, State 5, Line 1 Invalid length parameter passed to the LEFT or SUBSTRING function."

感谢任何帮助。

附注在我的数据集中,电子邮件地址可以有多个收件人,用分号分隔,如下所示:

[url=mailto:Test_Email_1@Yahoo.com] Test_Email_1@Yahoo.com[/url]; [url=mailto:Test_Email_5@Yahoo.com] Test_Email_5@Yahoo.com[/url]; [url=mailto:Test_Email_8@Yahoo.com] Test_Email_8@Yahoo.com[/url]

【问题讨论】:

  • SQL 确实是不适合这项工作的工具,尤其是使用那个 ps。
  • 什么数据库? MYSQL?甲骨文?为您的问题添加适当的标签;正如 HoneyBadger 所指出的,SQL 可能不是最佳选择;我认为一些 Unix 命令可以快速解决这个问题。
  • MS SQL Server 2008 R2

标签: sql sql-server tsql substring charindex


【解决方案1】:

如果对 TVF 开放

示例

Select A.ID
      ,B.*
 From  YourTable A
 Cross Apply [dbo].[tvf-Str-Extract](A.Body,'[url=mailto:',']') B

返回

ID  RetSeq  RetPos  RetVal
1   1       23      Test_Email_1@Yahoo.com
1   2       89      Test_Email_5@Yahoo.com
1   3       155     Test_Email_8@Yahoo.com
1   4       229     Test_Email_2@Yahoo.com

TVF(如果有兴趣)

CREATE FUNCTION [dbo].[tvf-Str-Extract] (@String varchar(max),@Delimiter1 varchar(100),@Delimiter2 varchar(100))
Returns Table 
As
Return (  

with   cte1(N)   As (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N)),
       cte2(N)   As (Select Top (IsNull(DataLength(@String),0)) Row_Number() over (Order By (Select NULL)) From (Select N=1 From cte1 N1,cte1 N2,cte1 N3,cte1 N4,cte1 N5,cte1 N6) A ),
       cte3(N)   As (Select 1 Union All Select t.N+DataLength(@Delimiter1) From cte2 t Where Substring(@String,t.N,DataLength(@Delimiter1)) = @Delimiter1),
       cte4(N,L) As (Select S.N,IsNull(NullIf(CharIndex(@Delimiter1,@String,s.N),0)-S.N,8000) From cte3 S)

Select RetSeq = Row_Number() over (Order By N)
      ,RetPos = N
      ,RetVal = left(RetVal,charindex(@Delimiter2,RetVal)-1) 
 From  (
        Select *,RetVal = Substring(@String, N, L) 
         From  cte4
       ) A
 Where charindex(@Delimiter2,RetVal)>1

)
/*
Max Length of String 1MM characters

Declare @String varchar(max) = 'Dear [[FirstName]] [[LastName]], ...'
Select * From [dbo].[tvf-Str-Extract] (@String,'[[',']]')
*/

编辑 - 正文

两个分隔符是 '[/url]''|||' 。我们通过添加一个唯一的字符串来强制结束分隔符。在这种情况下,我选择了|||

如果您不想要多重记录。删除CROSS APPLY B

示例

Select A.ID
      ,B.*
      ,Body = ltrim(rtrim(C.RetVal))
 From  @YourTable A
 Cross Apply [dbo].[tvf-Str-Extract](A.Body,'[url=mailto:',']') B
 Cross Apply [dbo].[tvf-Str-Extract](A.Body+'|||','[/url]','|||') C  --- Notice A.Body+'|||'.... this is to force an ending delimiter

退货

【讨论】:

  • John,这太棒了,我绝对可以在我正在从事的其他项目中使用它。不幸的是,对于这个特定的项目,它并不完全有效。重新阅读我最初的问题后,我忘了提到这些电子邮件地址不是静态的,为什么会一直更改。因此,通过对上面代码中的电子邮件地址进行硬编码,只有在使用这些电子邮件地址时才会给出结果。
  • @StephenMorrell 什么是硬编码? (at)YourTable 只是一个演示表变量。
  • 正文列实际上是电子邮件的正文。在这些消息中,EmailTO 和 EMAILCC 总是会改变,它上面的 Declare 语句显示为: (1, ' EmailTO:[url=mailto:Test_Email_1@Yahoo.com] Test_Email_1@Yahoo.com[/url]; [url= mailto:Test_Email_5@Yahoo.com] Test_Email_5@Yahoo.com[/url]; [url=mailto:Test_Email_8@Yahoo.com] Test_Email_8@Yahoo.com[/url] EmailCC:[url=mailto:Test_Email_2@Yahoo.com ] Test_Email_2@Yahoo.com[/url] 你好,这是电子邮件的其余部分......')我如何让它使用名为“Body”的列?
  • 完美!!!!太感谢了!!!!!我如何将其标记为已回答并给予您信任?
  • @StephenMorrell 总是乐于提供帮助:)
【解决方案2】:

我会使用 regexp_substr

with t1(col) as(
   select 'EmailTO:[url=mailto:Test_Email_1@Yahoo.com] Test_Email_1@Yahoo.com[/url]' from dual
)

select regexp_substr(col, '[[:alnum:]._%-]+@[[:alnum:]._%-]+\.com') as res
  from t1;

这将提取两个电子邮件地址,因为您在 P.S. 中说过,所以我离开了。可能存在多个电子邮件地址。您可以修改正则表达式以仅提取每封电子邮件的一个副本。

【讨论】:

  • 我得到这个错误:Msg 195, Level 15, State 10, Line 6 'regexp_substr' is not an known built-in function name.
  • 对不起,我以为这上面有 Oracle 标记。 regexp_substr 是一个 Oracle 函数。试试这个? stackoverflow.com/questions/36189734/…
【解决方案3】:

要解决您的查询问题,您需要在第一个 ']' 字符之后开始搜索 EmailCC。否则,您会选择在“EmailCC”之前第一次出现的“]”字符,因此会出现错误。您可以通过为 CHARINDEX() 添加一个“start_location”来做到这一点。

因此,将您的查询更改为以下内容:

    Select
    Body,
    SUBSTRING(Body, CHARINDEX('EmailTO', Body) + 20,CHARINDEX(']',Body)-CHARINDEX('EmailTO',Body)-20) ToEmail,
    SUBSTRING(Body, CHARINDEX('EmailCC', Body) + 20,CHARINDEX(']',Body, CHARINDEX('EmailCC', Body))-CHARINDEX('EmailCC',Body)-20) CCEmail
    From hdIssues

在此处查看文档:https://docs.microsoft.com/en-us/sql/t-sql/functions/charindex-transact-sql

【讨论】:

    猜你喜欢
    • 2019-07-21
    • 2018-10-04
    • 2014-03-26
    • 1970-01-01
    • 1970-01-01
    • 2018-02-07
    • 2018-11-02
    • 2021-08-30
    • 1970-01-01
    相关资源
    最近更新 更多