【问题标题】:Regex replace or LISTAGG on SQL serverSQL 服务器上的正则表达式替换或 LISTAGG
【发布时间】:2021-08-31 23:30:17
【问题描述】:

我需要将此语句“翻译”到 SQL 服务器

regexp_replace(Main.LOCK, '\/\/.*', '') TARGET

我需要摆脱这个标志,因为之前(或之后,取决于你的样子)我使用这个

LISTAGG(stock.LOCATION_NO, '//') WITHIN GROUP (ORDER BY isnull(QTY_OH,0)+isnull(QTY_TR,0) - isnull(QTY_RS, 0) desc) LOCK

Regex 和 Listagg 都不能在 SQL server 中使用

您所看到的,我正在尝试做的(它在 Oracle 中运行良好)是获取包含 Main.LOCK 且 MAXIMUM 值为的 TARGET 值

isnull(QTY_OH,0)+isnull(QTY_TR,0) - isnull(QTY_RS, 0)

现在我无法将其正确翻译到 SQL 服务器

另外,我得到的错误是:

消息 195,级别 15,状态 10,第 12 行 'regexp_replace' 不是可识别的内置函数名称。 消息 10757,第 15 层,状态 1,第 49 行 函数“LISTAGG”可能没有 WITHIN GROUP 子句。

有人可以帮忙吗?

SQL Server 18.8 版

仓库版本 13.0

Microsoft SQL Server 2016 (SP2-GDR) (KB4583460) - 13.0.5103.6 (X64) 2020 年 11 月 1 日 00:13:28 版权所有 (c) 微软公司 Windows Server 2016 Standard 10.0(内部版本 14393:)(管理程序)上的标准版(64 位)

【问题讨论】:

  • SQL Server 18.8 版 ...这可能是 SQL Server Management Studio 版本。 SQL Server 的实际版本是什么?尝试执行select @@version 以找出并Edit 您的问题以包含结果。
  • 请提供样本数据和期望的结果。
  • 在提出问题时,您需要提供一个可重现的最小示例:(1) DDL 和样本数据填充,即 CREATE 表加上 INSERT、T-SQL 语句。 (2) 你需要做什么,即逻辑,以及你在 T-SQL 中尝试实现它。 (3) 基于上述#1 中的样本数据的期望输出。 (4) 你的 SQL Server 版本 (SELECT @@version;)

标签: sql sql-server regex listagg


【解决方案1】:

regexp_replace() 正在做一些非常简单的事情。如果有的话,它会占用'//' 之前的字符串部分。

在较新版本的 SQL Server 中,您可以使用string_agg()left()

string_agg(left(main.lock,
                charindex('//', main.lock + '//')- 1
               ), '//'
          ) within group (order by coalesce(qty_oh, 0) + coalesce(qty_tr, 0) - coalesce(qty_rs, 0) desc)

【讨论】:

    【解决方案2】:

    SQL Server 没有内置的 Regex 函数,但它们可以通过 CLR 获得。好消息是 SQL Server 中不需要 Regex。我以前用 RegEx 做的所有事情现在都用NGrams8k 处理。这很容易并且性能更好。我已经使用 NGrams8K 构建了一些对这个问题和许多其他问题有帮助的函数。首先我们有PatReplace8K,第二个是Translate8K(更新了下面的代码。)第三个选项是PatExtract8K(点击代码链接)。

    每个执行文本转换的示例。对于每个函数,我只是从“SomeString”中删除 Alpha 字符和 0-5 中的数字:

    --==== Sample Data
    DECLARE @table TABLE (SomeId INT IDENTITY, SomeString VARCHAR(40));
    INSERT @table(SomeString) VALUES(NEWID()),(NEWID()),(NEWID()),(NEWID()),(NEWID());
    
    --==== Using Patreplace8k
    SELECT      t.SomeString, f.NewString
    FROM        @table                                         AS t
    CROSS APPLY samd.patReplace8K(t.SomeString,'[0-5A-F-]','') AS f
    
    --==== Using Translate8K
    SELECT      t.SomeString, f.NewString
    FROM        @table                                         AS t
    CROSS APPLY samd.Translate8K(t.SomeString,'[012345ABCDEF-]','') AS f
    
    --==== samd.patExtract8K
    SELECT t.SomeString,
      NewString = STRING_AGG(f.item,'') WITHIN GROUP (ORDER BY f.ItemNumber)
    FROM        @table                                      AS t
    CROSS APPLY samd.patExtract8K(t.SomeString,'[0-5A-F-]') AS f
    GROUP BY    t.SomeString;
    

    每次返回:

    SomeString                               NewString
    ---------------------------------------- -----------------
    0818BEF3-E0B3-4B3B-AA97-649E43EB16AF     8897696
    3077EE8B-9E92-4337-9E2F-97DABE2E4623     7789979976
    6BCD8194-F993-42DB-AF4A-D8289F8F8DA3     6899988988
    C1F152DF-8B6F-4C14-AF6F-AC8869099FDB     866886999
    F877D888-245E-4CEB-84B7-1CFF6E03B974     87788887697
    

    要执行字符串聚合,您可以使用 XML PATH() 或 STRING_AGG。下面是一个同时使用技术和 PatReplace8k 的示例:

    SELECT NewString = STUFF((
      SELECT      '//'+NewString
      FROM        @table                                         AS t
      CROSS APPLY samd.patReplace8K(t.SomeString,'[0-5A-F-]','') AS f
      ORDER BY f.NewString
      FOR XML PATH('')),1,2,'');
    
    SELECT STRING_AGG(f.NewString,'//') WITHIN GROUP (ORDER BY f.NewString)
    FROM        @table                                         AS t
    CROSS APPLY samd.patReplace8K(t.SomeString,'[0-5A-F-]','') AS f;
    

    在每种情况下,我都会得到我想要的:

    NewString
    ----------------------------------------------------
    6967899797//777689886//868796//8887789//88989
    

    翻译功能:

    CREATE OR ALTER FUNCTION samd.Translate8K
    (
      @string  VARCHAR(8000), -- Input
      @pattern VARCHAR(100),  -- characters to replace
      @key     VARCHAR(100)   -- replacement characters
    )
    /*
    Purpose:
      Standard Translate function - the fastest UDF version in the game. Enjoy.
      For more about TRANSLATE see: https://www.w3schools.com/sql/func_sqlserver_translate.asp
    
    Requires:
      NGrams8K; get you some here: 
        https://www.sqlservercentral.com/articles/nasty-fast-n-grams-part-1-character-level-unigrams
    
    Designed By Alan Burstein; May, 2021
    */
    RETURNS TABLE WITH SCHEMABINDING AS RETURN
    SELECT NewString = ISNULL(REPLACE(tx.NewString,CHAR(0),''),@string)
    FROM
    (
      SELECT STRING_AGG(CAST(t.tKey+ng.Token AS CHAR(1)),'')
               WITHIN GROUP (ORDER BY ng.Position)
      FROM        samd.ngrams8k(@string,1)               AS ng
      CROSS APPLY (VALUES(@key+REPLICATE(CHAR(0),100)))  AS tx(NewKey)
      CROSS APPLY (VALUES(CHARINDEX(ng.Token,@pattern))) AS pos(N)
      CROSS APPLY (VALUES(SUBSTRING(tx.NewKey,pos.N,1))) AS t(tKey)
    ) AS tx(NewString);
    

    Patreplace8k:

    CREATE OR ALTER FUNCTION [samd].[patReplace8K]
    (
      @string  VARCHAR(8000),
      @pattern VARCHAR(50),
      @replace VARCHAR(20)
    )
    /*****************************************************************************************
    [Purpose]:
     Given a string (@string), a pattern (@pattern), and a replacement character (@replace)
     patReplace8K will replace any character in @string that matches the @Pattern parameter 
     with the character, @replace.
    
    [Author]:
     Alan Burstein
    
    [Compatibility]:
      SQL Server 2008+
    
    [Syntax]:
    --===== Basic Syntax Example
     SELECT pr.NewString
     FROM   samd.patReplace8K(@String,@Pattern,@Replace) AS pr;
    
    [Developer Notes]:
     1. @Pattern IS case sensitive but can be easily modified to make it case insensitive
     2. There is no need to include the "%" before and/or after your pattern since since we 
        are evaluating each character individually
     3. Certain special characters, such as "$" and "%" need to be escaped with a "/"
        like so: [/$/%]
    
     4. Functions that use samd.ngrams8k will see huge performance gains when the optimizer
        generates a parallel execution plan. One way to get a parallel query plan (if the
        optimizer does not choose one) is to use make_parallel by Adam Machanic found here:
     sqlblog.com/blogs/adam_machanic/archive/2013/07/11/next-level-parallel-plan-porcing.aspx
    
        As is the case with functions which leverage samd.ngrams or samd.ngrams8k, 
        samd.patReplace8K is almost always dramatically faster with a parallel execution
        plan. On my PC (8 logical CPU, 64GB RAM, SQL 2019) samd.patReplace8K is about 4X
        faster when executed using all 8 of my logical CPUs. 
     5. samd.patReplace8K is deterministic. For more about deterministic functions see:
        https://msdn.microsoft.com/en-us/library/ms178091.aspx
    
    
    [Examples]:
    --===== 1. Replace numeric characters with a "*"
     SELECT pr.NewString
     FROM  samd.patReplace8K('My phone number is 555-2211','[0-9]','*') AS pr;
    
    --==== 2. Using againsts a table
     DECLARE @table TABLE(OldString varchar(60));
     INSERT  @table VALUES ('Call me at 555-222-6666'), ('phone number: (312)555-2323'),
                           ('He can be reached at 444.665.4466 on Monday.');
    
     SELECT      t.OldString, pr.NewString
     FROM        @table                                     AS t
     CROSS APPLY samd.patReplace8K(t.oldstring,'[0-9]','*') AS pr;
    
    [Revision History]:
    -----------------------------------------------------------------------------------------
     Rev 00 - 20141027 Initial Development - Alan Burstein
     Rev 01 - 20141029 - Redesigned based on the dbo.STRIP_NUM_EE by Eirikur Eiriksson
                        (see: http://www.sqlservercentral.com/Forums/Topic1585850-391-2.aspx)
            - change how the cte tally table is created 
            - put the include/exclude logic in a CASE statement instead of a WHERE clause
            - Added Latin1_General_BIN Colation
            - Add code to use the pattern as a parameter. - Alan Burstein
    Rev 02  - 20141106 - Added final performance enhancement (more cudos to Eirikur Eiriksson)
                       - Put 0 = PATINDEX filter logic into the WHERE clause
    Rev 03  - 20150516 - Updated to deal with special XML characters - Alan Burstein
    Rev 04  - 20170320 - changed @replace from char(1) to varchar(1) for whitespace handling
                       - Alan Burstein
    Rev 05  - 20200515 - Complete rewrite using samd.NGrams
                       - changed PATINDEX(...)=0 to: PATINDEX()&0x01=0;
                       - Changed CASE statement to IIF; Dropped collation - Alan Burstein
    *****************************************************************************************/
    RETURNS TABLE WITH SCHEMABINDING AS RETURN
    SELECT NewString = STRING_AGG(IIF(PATINDEX(@pattern,col.Token)&0x01=0,col.Token,
                       @replace),'') WITHIN GROUP (ORDER BY ng.position)
    FROM        samd.NGrams8K(@string,1)                      AS ng
    CROSS APPLY (VALUES(ng.token)) AS col(Token);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-02
      • 1970-01-01
      • 1970-01-01
      • 2019-01-24
      • 2015-01-24
      相关资源
      最近更新 更多