TRANSLATE function in SQL SERVER

后端 未结 5 1716
醉酒成梦
醉酒成梦 2021-01-26 20:48

I read that there is a function equivalent to the standard function TRANSLATE under DB2 under SQL Server 2017. But how to do under earlier versions?

For definition of fu

5条回答
  •  感情败类
    2021-01-26 21:07

    Adapted from @Shnugo's answer. This is closer to what you want. You just need to make certain you have a dbo.numbers table (they're REALLY useful to have).

    http://dbfiddle.uk/?rdbms=sqlserver_2016&fiddle=627828307504174dcf3f61313ba384a8

    CREATE FUNCTION dbo.MultiReplace(@ReplaceTarget NVARCHAR(MAX), @from_chars NVARCHAR(MAX), @to_chars NVARCHAR(MAX))
    RETURNS NVARCHAR(MAX)
    AS
    BEGIN
        --Quirky Update: One of the rare situations where this is a good idea 
        SELECT @ReplaceTarget=REPLACE(@ReplaceTarget,SUBSTRING(@from_chars, id+1, 1), SUBSTRING(@to_chars, id+1, 1))
        FROM numbers
        WHERE id < LEN(@from_chars) AND id < LEN(@to_chars)
        ORDER BY id;
    
        RETURN @ReplaceTarget;
    END
    


    And a slightly over the top way to meet your requirement that TRANSLATE('abc', 'abc', 'bcd') => 'bcd').

    CREATE FUNCTION dbo.Translate(@ReplaceTarget NVARCHAR(MAX), @from_chars NVARCHAR(MAX), @to_chars NVARCHAR(MAX))
    RETURNS NVARCHAR(MAX)
    AS
    BEGIN
    
      DECLARE
        @steps INT = LEN('_' + @from_chars + '_') - 2
      ;
    
      WITH
        dictionary(id, string_from, string_interim, string_to) AS
      (
        SELECT
          id, string_from, N'<' + string_from + N'>', string_to
        FROM
        (
          SELECT
            id,
            ROW_NUMBER() OVER (PARTITION BY string_from ORDER BY id)  AS occurence,
            string_from,
            string_to
          FROM
            numbers
          CROSS APPLY
          (
            SELECT
              CAST(SUBSTRING(@from_chars, numbers.id, 1) AS NVARCHAR(5))  AS string_from,
              CAST(SUBSTRING(@to_chars,   numbers.id, 1) AS NVARCHAR(5))  AS string_to
          )
            chars
          WHERE
                numbers.id >  0
            AND numbers.id <= @steps
        )
          sorted_dictionary
        WHERE
          occurence = 1
      )
      ,
        mapping_sequence(id, string_from, string_to) AS
      (
                  SELECT 1,               N'<',            N'<<>'                        WHERE @from_chars LIKE N'%<%'
        UNION ALL SELECT 2,               N'>',            N'<>>'                        WHERE @from_chars LIKE N'%>%'
        UNION ALL SELECT 3,               N'<<<>>',        N'<<>'                        WHERE @from_chars LIKE N'%<%' AND @from_chars LIKE N'%>%'
    
        UNION ALL SELECT 3 + id,          string_from,    string_interim FROM dictionary WHERE string_from NOT IN (N'<', N'>')
        UNION ALL SELECT 3 + @steps + id, string_interim, string_to      FROM dictionary
      )
      SELECT
        @ReplaceTarget = REPLACE(@ReplaceTarget, string_from, string_to)
      FROM
        mapping_sequence
      ORDER BY
        id
      ;
    
      RETURN @ReplaceTarget;
    
    END
    

    http://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=9dbe7214ac4b5bb00060686cfaa879c2


    A possible minor optimisation of the above (To reduce the number of REPLACE calls where possible)...

    CREATE FUNCTION dbo.Translate(
      @ReplaceTarget NVARCHAR(MAX),
      @from_chars    NVARCHAR(MAX),
      @to_chars      NVARCHAR(MAX)
    )
    RETURNS NVARCHAR(MAX)
    AS
    BEGIN
    
      DECLARE
        @steps INT = LEN('_' + @from_chars + '_') - 2
      ;
    
      WITH
        dictionary AS
      (
        SELECT
          id, string_from, string_to
        FROM
        (
          SELECT
            ROW_NUMBER() OVER (    ORDER BY string_from            )  AS id,
            ROW_NUMBER() OVER (PARTITION BY string_from ORDER BY id)  AS occurence,
            string_from,
            string_to
          FROM
            numbers
          CROSS APPLY
          (
            SELECT
              CAST(SUBSTRING(@from_chars, numbers.id, 1) AS NVARCHAR(5))  AS string_from,
              CAST(SUBSTRING(@to_chars,   numbers.id, 1) AS NVARCHAR(5))  AS string_to
          )
            chars
          WHERE
                numbers.id >  0
            AND numbers.id <= @steps
        )
          sorted_dictionary
        WHERE
          occurence = 1
      ),
        two_stage AS
      (
        SELECT
          map.*
        FROM
          dictionary   dict
        CROSS APPLY
        (
          SELECT COUNT(*) FROM dictionary WHERE dictionary.id > dict.id AND dictionary.string_from = dict.string_to
        )
          remap(hits)
        CROSS APPLY
        (
          SELECT id,                     dict.string_from,               dict.string_to          WHERE remap.hits = 0 AND dict.string_from NOT IN (N'<', N'>')
          UNION ALL
          SELECT id,                     dict.string_from,        N'<' + dict.string_from + N'>' WHERE remap.hits > 0 AND dict.string_from NOT IN (N'<', N'>')
          UNION ALL
          SELECT id + @steps,     N'<' + dict.string_from + N'>',        dict.string_to          WHERE remap.hits > 0 AND dict.string_from NOT IN (N'<', N'>')
          UNION ALL
          SELECT id + @steps * 2, N'<' + dict.string_from + N'>',        dict.string_to          WHERE                    dict.string_from     IN (N'<', N'>')
        )
          map
      )
      ,
        mapping_sequence(id, string_from, string_to) AS
      (
                  SELECT 1,               N'<',          N'<<>'                   WHERE @from_chars LIKE N'%<%'
        UNION ALL SELECT 2,               N'>',          N'<>>'                   WHERE @from_chars LIKE N'%>%'
        UNION ALL SELECT 3,               N'<<<>>',      N'<<>'                   WHERE @from_chars LIKE N'%<%' AND @from_chars LIKE N'%>%'
    
        UNION ALL SELECT 3 + id,          string_from,   string_to FROM two_stage
      )
      SELECT
        @ReplaceTarget = REPLACE(@ReplaceTarget, string_from, string_to)
      FROM
        mapping_sequence
      ORDER BY
        id
      ;
    
      RETURN @ReplaceTarget;
    
    END
    

    http://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=8af6ae050dc8d425521ae911b70a7968

    Or...

    http://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=1451aa88780463b1e7cfe15dd0071194

    Or...

    http://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=3079d4dd4289e8696072f6ee37be76ae

提交回复
热议问题