Разделение одной строки на несколько на основе столбца - PullRequest
0 голосов
/ 27 сентября 2019

У меня есть определенное количество строк, как показано ниже:

| Material No   | Quantity | Weight | Unit |
--------------------------------------------
| 111-22283/4   |    2     |   53   |  kg  |
| 123-ABC45/7   |    5     |   41   |   g  |
| 133-67879/80  |    7     |   31   |   g  |
| 144-54628     |    1     |   14   |  kg  |

Теперь я хочу произвести вывод, как показано ниже:

| Material No   | Quantity | Weight | Unit |
--------------------------------------------
| 111-22283     |    2     |   53   |  kg  |
| 111-22284     |    2     |   53   |  kg  |
| 123-ABC45     |    5     |   41   |   g  |
| 123-ABC46     |    5     |   41   |   g  |
| 123-ABC47     |    5     |   41   |   g  |
| 133-67879     |    7     |   31   |   g  |
| 133-67880     |    7     |   31   |   g  |
| 144-54628     |    1     |   14   |  kg  |

Логика : на основематериала нет, я должен разделить строки.Если '/' в конце материала нет, то его нужно пролить.Затем мы должны найти разницу ч / б последнего номера в материале № и число /.Если это 2, тогда я хочу 2 разных строки с каждым номером в качестве номера материала ( означает, что если последняя цифра 83/4, то я хочу, чтобы номер материала заканчивался 83 и 84 ).Сложная часть, когда у нас 89/90.Он будет содержать 2 числа после / ( только для 10, 20 и т. Д., ).Все остальные столбцы останутся такими же для каждого материала №.

Для достижения этого в настоящее время у нас есть очень большая процедура, которая содержит около 50-80 строк кода (найдите строку с '/' и удалите отдельно, затемнайти индекс / и тд).Я хотел бы знать, можно ли это сделать с помощью простого запроса или очень короткой процедуры.

Ответы [ 2 ]

0 голосов
/ 27 сентября 2019

сложная часть, вероятно, разделяет material no, чтобы получить числа starting и ending.

После этого это просто простой рекурсивный запрос для увеличения числа и объединения обратно в формуномер материала

;with rcte as
(
    select  MaterialNo,
            base, st, en, n = st,
            material    = convert(varchar(20), base + isnull(convert(varchar(10), st), ''))
    from    material m
            -- get the position of the `/`
            cross apply
            (
                select  split = charindex('/', MaterialNo)          
            ) s     
            -- extract the ending number and convert to integer
            cross apply
            (
                select  en  = case  when    split > 0
                                    then    convert(int, right(MaterialNo, len(MaterialNo) -  split))
                                    end     
            ) en
            -- extract the starting number and convert to integer
            cross apply
            (
                select  st  = case  when    split > 0
                        then    convert(int, substring(MaterialNo, split - len(en), len(en)))
                        end
            ) st
            -- extract the base material no for concatenate
            cross apply
            (
                select  base    = case  when    split   > 0
                                        then    left(MaterialNo, split - len(en) - 1)
                                        else    MaterialNo
                                        end
            ) b

    union all

    select  MaterialNo, base, st, en, 
            n = n + 1,
            material = convert(varchar(20), base + convert(varchar(10), n + 1))
    from    rcte 
    where   n < en
)
select  *
from    rcte
order by MaterialNo, material

Все это основано на предположении, что окончание номера материала является чисто числовым.

Примечание: если у вас есть таблица учета, вы можете использовать ее длязамени рекурсивный cte

0 голосов
/ 27 сентября 2019

ОБНОВЛЕНИЕ

Когда значение из MATERIALNO может представлять серию значений (как обновленный вопрос):

DDL

CREATE FUNCTION UFN_STRTOSERIES (@materialNo VARCHAR(80), @qte INT, @weight INT, @unit VARCHAR(50))  
RETURNS @result TABLE (
  MATERIALNO VARCHAR(80),
  QTE INT,
  Weight INT,
  UNIT VARCHAR(50)
)
AS  
BEGIN
  DECLARE @base VARCHAR(50) = LEFT(@materialNo,CHARINDEX('/',@materialNo)-1-LEN(RIGHT(@materialNo,LEN(@materialNo)-CHARINDEX('/',@materialNo))));
  DECLARE @start INT = CONVERT( INT , RIGHT( LEFT(@materialNo,CHARINDEX('/',@materialNo)-1) , LEN(RIGHT(@materialNo,LEN(@materialNo)-CHARINDEX('/',@materialNo))) ) );
  DECLARE @end INT = CONVERT( INT , RIGHT(@materialNo,LEN(@materialNo)-CHARINDEX('/',@materialNo)) );

  DECLARE @i INT = @start;
  WHILE @i <= @end
  BEGIN
    INSERT @result  
    SELECT CONCAT(@base,@i) AS MATERIALNO, @qte, @weight, @unit;
    SET @i = @i + 1;
  END;

  RETURN
END;

CREATE TABLE MATERIALS
(
  MATERIALNO VARCHAR(80),
  QTE INT,
  Weight INT,
  UNIT VARCHAR(50)
)

INSERT INTO MATERIALS VALUES
('111-22283/4',2,53,'kg'),
('123-33345/7',5,41,'g' ),
('123-ABC45/7',5,41,'g'),
('133-67879/80',7,31,'g'),
('144-54628',1,14,'kg')

DML

SELECT MATERIALNO,QTE,Weight,UNIT
FROM (
SELECT MATERIALNO,QTE,Weight,UNIT
FROM MATERIALS
WHERE CHARINDEX('/',MATERIALNO) < 1
UNION
SELECT series.MATERIALNO,series.QTE,series.Weight,series.UNIT
FROM MATERIALS m
CROSS APPLY UFN_STRTOSERIES(MATERIALNO,QTE,Weight,UNIT) series
WHERE CHARINDEX('/',m.MATERIALNO) > 1
) base
ORDER BY base.MATERIALNO

СТАРЫЙ ОТВЕТ

Когда значение из MATERIALNO представляет только два значения:

UNION - самый простой ответ (тогда вам нужно проверить выступления):

DDL

CREATE TABLE MATERIALS
(
  MATERIALNO VARCHAR(80),
  QTE INT,
  Weight INT,
  UNIT VARCHAR(50)
 )

 INSERT INTO MATERIALS VALUES
('111-22283/4',2,53,'kg'),
('123-33345/7',5,41,'g' ),
('133-67879/80',7,31,'g'),
('144-54628',1,14,'kg'  )

Запрос

SELECT MATERIALNO,QTE,Weight,UNIT
FROM (
SELECT MATERIALNO,QTE,Weight,UNIT
FROM MATERIALS
WHERE CHARINDEX('/',MATERIALNO) < 1
UNION
SELECT LEFT(MATERIALNO,CHARINDEX('/',MATERIALNO)-1),QTE,Weight,UNIT
FROM MATERIALS
WHERE CHARINDEX('/',MATERIALNO) > 1
UNION
SELECT 
CONCAT(LEFT(MATERIALNO,CHARINDEX('/',MATERIALNO)-1-LEN(RIGHT(MATERIALNO,LEN(MATERIALNO)-CHARINDEX('/',MATERIALNO)))),RIGHT(MATERIALNO,LEN(MATERIALNO)-CHARINDEX('/',MATERIALNO))) AS MATERIALNO
,QTE,Weight,UNIT
FROM MATERIALS
WHERE CHARINDEX('/',MATERIALNO) > 1
) BASE
ORDER BY BASE.MATERIALNO

Скрипка: http://sqlfiddle.com/#!18/8e768/2

...