Подстрока с объединением, работающим по-разному в пользовательской функции

#sql #sql-server #tsql #user-defined-functions

Вопрос:

Я пытаюсь разделить предложение с неограниченным количеством символов на 7 кратных, используя SQL UDF. Это означает, что я пытаюсь разбить предложение на строки по 7 символов в каждой строке. Это подход, который, по моему мнению, был самым быстрым.

 CREATE OR ALTER FUNCTION dbo.UDF_SplitStringIntoRows
(
    @inputstring nvarchar(MAX)
)
RETURNS @OutputTbl TABLE
(
    txt nvarchar(MAX),
    seq INT IDENTITY 
)
AS
BEGIN
    IF(LEN(@inputstring) <= 40)
    BEGIN 
        INSERT INTO @OutputTbl (txt) 
            SELECT SUBSTRING(@inputstring, 1,7) UNION 
            SELECT SUBSTRING(@inputstring, 8,7) UNION 
            SELECT SUBSTRING(@inputstring, 15,7) UNION 
            SELECT SUBSTRING(@inputstring, 23,7) UNION 
            SELECT SUBSTRING(@inputstring, 30,7) UNION 
            SELECT SUBSTRING(@inputstring, 37,7) UNION 
            SELECT SUBSTRING(@inputstring, 44,7)
    END

    RETURN
END
 

Мой вопрос:

 SELECT *
FROM dbo.UDF_SplitStringIntoRows('This is a demo function which') AS USSIR
WHERE USSIR.txt <> ''
 

Выход:

введите описание изображения здесь

Что нарушает последовательность предложения. Я что-то здесь упускаю? Пожалуйста, предложите.

Комментарии:

1. Ваша функция, похоже, разбивается на 7 символов в строке, что не то, о чем вы просите, возможно, помогут примеры данных и желаемые результаты.

2. Существуют гораздо лучшие способы разделения строк без необходимости многократного выбора и объединения, рассмотрите возможность использования cross apply с таблицей подсчета

3. У вас также есть единичная ошибка в вычислениях подстрок

4. LEN(@inputstring) <= 40 Согласуется с SUBSTRING(@inputstring, 44,7) ?

5. Если вы ожидаете только строку из 40 (44?) символов, зачем использовать nvarchar(max) ? А что произойдет, если вы передадите строку длиной более 40 символов (функция с удовольствием возьмет мегабайты)?

Ответ №1:

Только что выяснил ошибку. На самом деле все портит Профсоюз. Я просто заменил Union на Union All, и, похоже, он получает желаемый результат.

Еще раз спасибо.

Ответ №2:

Вам гораздо лучше использовать встроенную табличную функцию

 CREATE OR ALTER FUNCTION dbo.UDF_SplitStringIntoRows
(
    @inputstring nvarchar(56),
    @splitSize int
)
AS RETURN

SELECT
    SUBSTRING(@inputstring, n * @splitSize   1, @splitSize) txt,
    ROW_NUMBER() OVER (ORDER BY n) seq
FROM (VALUES
  (0),(1),(2),(3),(4),(5),(6),(7)
) v(n)
WHERE n * @splitSize   1 >= LEN(@inputstring);

GO
 

Если строка может быть неограниченной длины, вы можете использовать функцию подсчета Ицика Бен-Гана для создания строк

 CREATE OR ALTER FUNCTION dbo.GetNums
  (@low AS BIGINT = 1, @high AS BIGINT)
RETURNS TABLE
AS
RETURN
 
  WITH
    L0 AS ( SELECT 1 AS c 
            FROM (VALUES(1),(1),(1),(1),(1),(1),(1),(1),
                        (1),(1),(1),(1),(1),(1),(1),(1)) AS D(c) ),
    L1 AS ( SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B ),
    L2 AS ( SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B ),
    L3 AS ( SELECT 1 AS c FROM L2 AS A CROSS JOIN L2 AS B ),
    Nums AS ( SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS rownum
              FROM L3 )
  SELECT TOP(@high - @low   1)
     rownum AS rn,
     @high   1 - rownum AS op,
     @low - 1   rownum AS n
  FROM Nums
  ORDER BY rownum;

GO
 
 CREATE OR ALTER FUNCTION dbo.UDF_SplitStringIntoRows
(
    @inputstring nvarchar(MAX),
    @splitSize int
)
AS RETURN

SELECT
    SUBSTRING(@inputstring, n * @splitSize   1, @splitSize) txt,
    rownum AS seq
FROM dbo.GetNums(0, (LEN(@inputstring) - 1) / @splitSize) v;