根据列

时间:2019-09-27 07:08:32

标签: sql sql-server split

我的行数如下:

| 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  |

逻辑:基于材料,我不必拆分行。如果在材料末尾没有'/',则需要将其打散。然后,我们必须在物料编号和编号/之间找到最后一个数字b / w的差。如果它是2,那么我想要2个不同的行,每个数字都作为物料编号(表示最后一位数字是83/4,那么我希望物料编号以83和84 结尾)。棘手的部分是当我们有89/90时。 /后面将包含2个数字(仅用于10、20等,)。每种物料编号的所有其他列将保持不变。

要实现此目的,当前我们有一个非常大的过程,其中包含大约50-80行代码(找到带有'/'的行,然后分别删除然后找到/的索引,依此类推)。我想知道这是否可以通过简单的查询或很短的过程来完成。

2 个答案:

答案 0 :(得分:1)

更新

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

答案 1 :(得分:1)

最具挑战性的部分可能是将material no拆分为startingending号。

在那之后,增加数字并连接起来形成材料编号只是一个简单的递归查询。

;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