zoukankan      html  css  js  c++  java
  • SQL Server 经典案例

    1、先进先出

    • 例1

    WITH [ta] ([商品编号], [批次号], [库存数量]) AS (
    SELECT '0001', '090801', 200 UNION ALL
    SELECT '0001', '090501', 50 UNION ALL
    SELECT '0002', '090101', 30 UNION ALL
    SELECT '0002', '090701', 200
    ),[tb] ([商品编号], [订货数量]) AS ( 
    SELECT '0001', 60 UNION ALL 
    SELECT '0002', 20
    )
    SELECT   a.*, 出货数量=a.库存数量-CASE WHEN SUM(c.库存数量)-b.订货数量<0 THEN 0 ELSE SUM(c.库存数量)-b.订货数量 END
    FROM     ta a
             JOIN tb b ON a.商品编号=b.商品编号
             JOIN ta c ON a.商品编号=c.商品编号 AND c.批次号<=a.批次号
    GROUP BY a.商品编号, a.批次号, a.库存数量, b.订货数量
    HAVING   a.库存数量>SUM(c.库存数量)-b.订货数量;
    View Code
    • 例2

    CREATE TABLE 订单表
    (
      订单号 CHAR(8),
      下单日期 DATE,
      产品 CHAR(15),
      订单数量 INT,
      订单余数 INT
    );
    GO
    INSERT INTO 订单表 VALUES 
    ('MD180090','2018-04-03','BH14-084-099-05',600,248),
    ('MD180091','2018-04-03','BH14-084-099-18',700,295),
    ('MD180147','2018-06-02','BH14-084-099-18',400,400),
    ('MD180148','2018-06-02','BH14-084-099-05',500,500);
    GO
    
    CREATE TABLE 销售表
    (
      销售日期 DATE,
      销售单号 CHAR(10),
      产品 CHAR(15),
      销售数量 INT
    );
    GO
    INSERT INTO 销售表 VALUES 
    ('2018-07-09','4212345704','BH14-084-099-05',112),
    ('2018-07-09','4212345704','BH14-084-099-18',102),
    ('2018-07-09','4212345710','BH14-084-099-05',112),
    ('2018-07-09','4212345710','BH14-084-099-18',102),
    ('2018-07-09','4212345716','BH14-084-099-05',186),
    ('2018-07-09','4212345716','BH14-084-099-18',170),
    ('2018-07-09','4212345722','BH14-084-099-05',92),
    ('2018-07-09','4212345722','BH14-084-099-18',84),
    ('2018-07-09','4212345728','BH14-084-099-05',118),
    ('2018-07-09','4212345728','BH14-084-099-18',107);
    GO
    ----------------------------------------
    --订单表是公司的所有订单,因生产日期不同,生产成本可能不同
    --销售表是5家经销店的销售记录,销售单价可能不同
    --现在需要将各个销售单号归到订单号中去,以计算利润
    --运行结果参考如下:
    ----------------------------------------
    /*
    销售日期    订单号      销售单号      产品           数量
    2018-7-9    MD180090    4212345704    BH14-084-099-05    112
    2018-7-9    MD180090    4212345710    BH14-084-099-05    112
    2018-7-9    MD180090    4212345716    BH14-084-099-05    24
    2018-7-9    MD180091    4212345704    BH14-084-099-18    102
    2018-7-9    MD180091    4212345710    BH14-084-099-18    102
    2018-7-9    MD180091    4212345716    BH14-084-099-18    91
    2018-7-9    MD180147    4212345716    BH14-084-099-18    79
    2018-7-9    MD180147    4212345722    BH14-084-099-18    84
    2018-7-9    MD180147    4212345728    BH14-084-099-18    107
    2018-7-9    MD180148    4212345716    BH14-084-099-05    162
    2018-7-9    MD180148    4212345722    BH14-084-099-05    92
    2018-7-9    MD180148    4212345728    BH14-084-099-05    118
    */
    
    
    IF object_id('tempdb..#ordersDis') is not null
    Begin
        DROP TABLE #ordersDis
    End
    if object_id('tempdb..#salesTmp') is not null
    Begin
        DROP TABLE #salesTmp
    End
    
    SELECT t2.[销售日期],t1.[订单号],t2.[销售单号],t1.[产品],t1.[订单数量] [数量]
        INTO #ordersDis
        FROM [订单表] t1 inner Join [销售表] t2 on t1.[产品] = t2.[产品] and 1 > 2 
    
    SELECT [销售日期],[销售单号],[产品],[销售数量] INTO #salesTmp FROM [销售表];
        
    DECLARE @orderNO NVARCHAR(32);
    DECLARE @orderGoods NVARCHAR(32);
    DECLARE @orderLastGoods NVARCHAR(32);
    DECLARE @orderQty INT;
    DECLARE @orderDate DATE;
    
    DECLARE @saleDate DATE;
    DECLARE @saleNO NVARCHAR(32);
    DECLARE @saleGoods NVARCHAR(32);
    DECLARE @saleQty INT;
    
    set @orderQty = 0;
    set @saleQty = 0;
    set @orderLastGoods = '';
    
    DECLARE orderCursor CURSOR FOR SELECT [订单号],[产品],[订单余数],[下单日期] FROM [订单表] order by [下单日期] ASC; 
    OPEN orderCursor
    FETCH NEXT FROM orderCursor INTO @orderNO,@orderGoods,@orderQty,@orderDate 
    WHILE (@@fetch_status=0 Or @orderQty > 0)
    Begin     
        Begin                
            DECLARE saleCursor CURSOR FOR SELECT [销售日期],[销售单号],[产品],[销售数量] FROM #salesTmp 
                                            WHERE [产品]= @orderGoods and [销售数量] > 0 ORDER BY [销售日期] ASC; 
            SET @orderLastGoods = @orderGoods;
            
            OPEN saleCursor
            FETCH NEXT FROM saleCursor into @saleDate,@saleNO,@saleGoods,@saleQty 
            DECLARE @hasSaleDataTag INT;
            SET @hasSaleDataTag = 0;
            
            while (@@fetch_status=0)
            Begin
                SET @hasSaleDataTag = 1;
                If @saleQty > 0 and @orderQty > 0
                Begin
                    if @orderQty >= @saleQty
                    Begin
                        Insert into #ordersDis([销售日期],[订单号],[销售单号],[产品],[数量]) values(@saleDate,@orderNO,@saleNO,@orderGoods,@saleQty);
                        set @orderQty = @orderQty - @saleQty;
                        set @saleQty = 0;
                        --可添加更新减少订单表订单余量的语句,或是更新销售单已分配处理销售数量的语句
                        UPDATE #salesTmp SET [销售数量] = @saleQty WHERE [销售单号] = @saleNO and [产品] = @orderGoods;
                    End
                    else if @orderQty < @saleQty
                    Begin
                        Insert into #ordersDis([销售日期],[订单号],[销售单号],[产品],[数量]) values(@saleDate,@orderNO,@saleNO,@orderGoods,@orderQty);
                        set @saleQty = @saleQty - @orderQty;
                        set @orderQty = 0;
                        --可添加更新减少订单表订单余量的语句,或是更新销售单已分配处理销售数量的语句
                        UPDATE #salesTmp SET [销售数量] = @saleQty WHERE [销售单号] = @saleNO and [产品] = @orderGoods;
                    End
                End
                if @saleQty <= 0
                    FETCH NEXT FROM saleCursor INTO @saleDate,@saleNO,@saleGoods,@saleQty 
                if @orderQty <= 0
                    break;
            End
            
            IF @hasSaleDataTag = 0
            BEGIN
                SET @orderQty = 0;
            END
            
            CLOSE saleCursor
            DEALLOCATE saleCursor
        End
        
        If @orderQty <= 0
        Begin
            FETCH NEXT FROM orderCursor INTO @orderNO,@orderGoods,@orderQty,@orderDate;
        End
    End
    CLOSE orderCursor
    DEALLOCATE orderCursor 
    
    SELECT [销售日期],[订单号],[销售单号],[产品],[数量] FROM #ordersDis;
    View Code
    • 例3

    --测试数据
    if not object_id('outtable') is null
        drop table outtable
    Go
    Create table outtable([bill] nvarchar(20),[goodscode] varchar(100),mount int ,prodate date)
    Insert outtable
    select '单号1','060',100,'2018/5/1' 
    union all
    select '单号1','138',200,'2018/5/1' 
    union all
    select '单号2','179',300,'2018/5/1' 
    union all
    select '单号2','138',200,'2018/5/1' 
    union all
    select '单号3','060',150,'2018/5/1' 
    union all
    select '单号3','179',200,'2018/5/1'
    union all
    select '单号4','060',250,'2018/5/1' 
    union all
    select '单号4','138',350,'2018/5/1' 
    select * from outtable
    
    if not object_id('intable') is null
        drop table intable
    Go
    Create table intable([goodscode] varchar(100),mount int ,prodate date)
    Insert intable
    select '060',200,'2018/5/1' 
    union all
    select '060',300,'2018/4/1' 
    union all
    select '138',700,'2018/5/1' 
    union all
    select '138',50,'2018/4/1' 
    union all
    select '179',200,'2018/5/1' 
    union all
    select '179',300,'2018/4/1' 
    
    select * from intable
    --想要结果
    
    if not object_id('outtable1') is null
        drop table outtable
    Go
    Create table outtable1([bill] nvarchar(20),[goodscode] varchar(100),mount int ,prodate date)
    Insert outtable1
    select '单号1','060',100,'2018/5/1' 
    union all
    select '单号1','138',200,'2018/5/1' 
    union all
    select '单号2','179',300,'2018/4/1' 
    union all
    select '单号2','138',200,'2018/5/1' 
    union all
    select '单号3','060',100,'2018/5/1' 
    union all
    select '单号3','060',50,'2018/4/1' 
    union all
    select '单号3','179',200,'2018/5/1'
    union all
    select '单号4','060',250,'2018/4/1' 
    union all
    select '单号4','138',300,'2018/5/1' 
    union all
    select '单号4','138',50,'2018/4/1' 
    select * from outtable1
    
    --方法一
    SELECT a.bill
         , a.goodscode
         , CASE WHEN a.sumMount>b.sumMount THEN b.sumMount
                    ELSE a.sumMount
               END-CASE WHEN a.sumMount-a.mount>b.sumMount-b.mount THEN a.sumMount-a.mount
                        ELSE b.sumMount-b.mount
                   END AS mount
         , b.prodate
    FROM
    (SELECT *
          ,(SELECT  SUM(mount) FROM outtable WHERE  [goodscode]=o.[goodscode] AND [bill]<=o.[bill]) AS sumMount
     FROM outtable AS o) AS a
    INNER JOIN
    (SELECT *
          , (SELECT SUM(mount) FROM intable WHERE [goodscode]=i.[goodscode] AND prodate<=i.prodate) AS sumMount
     FROM intable AS i) AS b ON a.goodscode=b.goodscode
    WHERE a.sumMount>b.sumMount-b.mount
          AND b.sumMount>a.sumMount-a.mount
    ORDER BY 1;
    
    --方法二
    IF OBJECT_ID('outtable1') IS NOT NULL DROP TABLE outtable1;
    WITH
    DATA AS(
        SELECT * ,
            mount as 剩余,
            ROW_NUMBER()OVER(PARTITION BY goodscode, prodate ORDER BY mount) as _row_id
        FROM outtable
    ),
    RE AS(
        SELECT DATA.*, DATA.mount as 累计
        FROM DATA
        WHERE _row_id = 1
        UNION ALL
        SELECT DATA.*, DATA.mount+RE.累计 as 累计
        FROM DATA, RE
        WHERE DATA.goodscode = RE.goodscode AND DATA.prodate=RE.prodate
            AND DATA._row_id = RE._row_id+1
    )
    SELECT * INTO outtable1 FROM RE
    OPTION(MAXRECURSION 0)
    ;
    -- [goodscode] + prodate 汇总范围内的 Bill
    UPDATE DATA
    SET _row_id = -DATA._row_id,
        剩余 = DATA.累计 - A.mount
    FROM outtable1 DATA, intable A
    WHERE DATA.goodscode = A.goodscode AND DATA.prodate = A.prodate
        AND DATA.累计 - DATA.剩余 < A.mount
    ;
    -- -- [goodscode] + prodate 范围内,未包含足够明细的汇总数据
    IF OBJECT_ID('tempdb..#intable') IS NOT NULL DROP TABLE #intable;
    WITH
    DATA AS(
        SELECT A.goodscode, A.prodate, A.mount - ISNULL(DATA.累计-DATA.剩余, 0) as mount
        FROM intable A
            LEFT JOIN(
                SELECT *
                FROM outtable1 DATA
                WHERE _row_id < 0
                    AND NOT EXISTS(
                        SELECT * FROM outtable1 A
                        WHERE DATA.goodscode = A.goodscode AND DATA.prodate = A.prodate
                            AND A._row_id < DATA._row_id
                    )
            ) DATA
                ON DATA.goodscode = A.goodscode AND DATA.prodate = A.prodate
    )
    SELECT *, ROW_NUMBER()OVER(PARTITION BY goodscode ORDER BY prodate) as _row_id
    INTO #intable
    FROM DATA
    WHERE mount > 0
    ;
    -- 针对每个未包含足够明细的汇总项的明细数据调整
    DECLARE @row_id int = 1;
    WHILE EXISTS( SELECT * FROM #intable WHERE _row_id = @row_id )
    BEGIN;
        -- 已经分配给某汇总项,但分配有剩余的 bill 拆分
        INSERT INTO outtable1( bill, goodscode, mount, prodate, 剩余, _row_id )
        SELECT bill, goodscode, 剩余 as mount, prodate, 剩余, -_row_id
        FROM outtable1 DATA
        WHERE 剩余 > 0 AND _row_id < 0
            AND NOT EXISTS(
                SELECT * FROM outtable1 A
                WHERE DATA.goodscode = A.goodscode AND DATA.prodate = A.prodate
                    AND A._row_id < DATA._row_id
            )
        ;
        -- 未分配给汇总项的 bill 排序累计信息
        WITH
        DATA AS(
            SELECT goodscode, prodate, _row_id,
                mount,
                ROW_NUMBER()OVER(PARTITION BY goodscode ORDER BY mount DESC, prodate) as _row_id_new
            FROM outtable1
            WHERE _row_id > 0
        ),
        RE AS(
            SELECT DATA.*, DATA.mount as 累计
            FROM DATA
            WHERE _row_id_new = 1
            UNION ALL
            SELECT DATA.*, DATA.mount+RE.累计 as 累计
            FROM DATA, RE
            WHERE DATA.goodscode = RE.goodscode AND DATA.prodate=RE.prodate
                AND DATA._row_id_new = RE._row_id_new+1
        )
        UPDATE DATA
        SET _row_id = A._row_id_new,
            累计 = A.累计
        FROM outtable1 DATA, RE A
        WHERE DATA.goodscode = A.goodscode AND DATA.prodate = A.prodate AND DATA._row_id = A._row_id
        OPTION(MAXRECURSION 0)
        ;
        -- 明细数据分配给汇总项
        UPDATE DATA
        SET _row_id = -DATA._row_id,
            剩余 = DATA.累计 - A.mount,
            prodate = A.prodate
        FROM outtable1 DATA, #intable A
        WHERE DATA._row_id > 0
            AND A._row_id = @row_id
            AND DATA.goodscode = A.goodscode
            AND DATA.累计 - DATA.剩余 < A.mount
        ;
        SET @row_id = @row_id + 1;
    END;
    -- 更新拆分的 mount
    UPDATE outtable1 SET mount = mount - 剩余 WHERE 剩余 > 0;
    -- 显示最终处理结果
    SELECT bill, goodscode, mount, prodate FROM outtable1;
     
    -- 总总验证修正后的数据
    SELECT goodscode, prodate, SUM(mount) as mount
    FROM outtable1
    GROUP BY goodscode, prodate
    ORDER BY 1, 2, 3
    ;
    SELECT goodscode, prodate, mount FROM intable ORDER BY 1, 2, 3;
    View Code

     2、记录排序

    CREATE TABLE #t ([Id] [INT] IDENTITY(1, 1) NOT NULL,
                     [Title] [NVARCHAR](50),
                     [CreationTime] [DATETIME] DEFAULT(GETDATE()),
                     [OrderIndex] [INT], );
    GO
    
    INSERT INTO #t(Title, OrderIndex)VALUES('文章A', 0);
    INSERT INTO #t(Title, OrderIndex)VALUES('文章B', 0);
    INSERT INTO #t(Title, OrderIndex)VALUES('文章C', 2);
    INSERT INTO #t(Title, OrderIndex)VALUES('文章D', 0);
    INSERT INTO #t(Title, OrderIndex)VALUES('文章E', 6);
    INSERT INTO #t(Title, OrderIndex)VALUES('文章F', 3);
    INSERT INTO #t(Title, OrderIndex)VALUES('文章G', 0);
    INSERT INTO #t(Title, OrderIndex)VALUES('文章H', 4);
    INSERT INTO #t(Title, OrderIndex)VALUES('文章I', 0);
    SELECT * FROM #t
    --方法(1)
    SELECT rowId, Id, Title, CreationTime, OrderIndex
    FROM   (SELECT rowId=Id, rn=ROW_NUMBER() OVER (ORDER BY Id)
            FROM   #t
            WHERE  Id NOT IN(SELECT OrderIndex FROM #t))A
           INNER JOIN(SELECT *, rn=ROW_NUMBER() OVER (ORDER BY Id)FROM #t WHERE OrderIndex=0)B ON A.rn=B.rn
    UNION ALL
    SELECT OrderIndex, * FROM #t WHERE OrderIndex>0
    ORDER BY 1;
    --方法(2)
    SELECT DISTINCT number, #t.*
    INTO   #basic
    FROM   master..spt_values a
           LEFT JOIN #t ON number=OrderIndex
    WHERE  number BETWEEN 1 AND(SELECT MAX(OrderIndex)FROM #t);
    SELECT   ISNULL(X1.Id, X2.Id) AS ID, ISNULL(X1.Title, X2.Title) AS title, ISNULL(X1.CreationTime, X2.CreationTime) AS CreationTime, ISNULL(X1.OrderIndex, X2.OrderIndex) AS orderIndex
    FROM     (SELECT tmp=(SELECT SUM(CASE WHEN Id IS NULL THEN 1 ELSE 0 END)
                          FROM   #basic
                          WHERE  number<=A.number AND Id IS NULL), *
              FROM   #basic AS A)X1
             FULL JOIN(SELECT tmp=(SELECT SUM(CASE WHEN OrderIndex=0 THEN 1 ELSE 0 END)FROM #t WHERE Id<=A.Id), *
                       FROM   #t AS A
                       WHERE  OrderIndex=0)X2 ON X1.tmp=X2.tmp AND X1.Id IS NULL
    ORDER BY CASE WHEN X1.number IS NOT NULL THEN 1 ELSE 2 END, X1.number, X2.Id;
    View Code

    3、根据品号查询所有BOM清单

    ALTER PROCEDURE [dbo].[pro_GetBOMList]
     -- 存储过程参数
     @PROID varchar(100)
    AS
    BEGIN
    
        --检查临时表
        If object_id('tempdb..##BOMINFO') is not null Drop Table ##BOMINFO
    
        --创建临时表
        Create table ##BOMINFO
        (
               BOM_NO varchar(100) not null,    --BOM号
               PRD_NO varchar(100) PRIMARY KEY, --品号
               PRDNAME varchar(100),            --品名
            MIN_PURCHASE INT default(0),     --最小采购量
               PRICE [numeric](18,5) default(0),           --单价
               PRICESubtotal [numeric](18,5) default(0),   --单个部品金额 采购量*单价
               SUP_COUNT INT default(0),        --供应商/家
               ID_NO varchar(100),              --子件ID
               LAYER INT,
        )
    
        --递归读取BOM表(With前面有语句需要用分好隔开)
        ;With TBOM as 
     ( 
      Select BOM_NO,PRD_NO,[NAME],ID_NO,QTY from DB_TD02.DB_TD02.dbo.TF_BOM WHERE BOM_NO >=@PROID+'->'
      union all
      Select B.BOM_NO,B.PRD_NO,B.NAME,B.ID_NO,B.QTY from TBOM
      inner join DB_TD02.DB_TD02.dbo.TF_BOM B on TBOM.ID_NO=B.BOM_NO
     )
    
        --Select * from TBOM (测试数据)
        --根据型号查询BOM记录复制到临时表(层级)
        Insert into ##BOMINFO(BOM_NO,PRD_NO,PRDNAME,ID_NO) Select B.BOM_NO,B.PRD_NO,B.NAME,B.ID_NO from TBOM B
    
    
        -----------------------------------------------------------------------------------------------------
        -------------------------------游标执行对递归后TBOM的其他计算处理-------------------------------------
        -----------------------------------------------------------------------------------------------------
        --申明变量(用户游标复制操作)
        declare @PRD_NO varchar(100)
        declare @PRDNAME varchar(100)
        declare @BomCusCount int       --货品对应供应商总数
        declare @PRICE numeric(18,8)   --部品单价
        declare @MIN_PURCHASE int      --最小采购量
        declare @Layer int --层级
        declare @KND varchar(1)   --大类代号
        declare @IDX1 varchar(10) --中类代号(包装类、喷油件、电镀件...)
        declare @SUP1 varchar(12) --主供应商
        declare @MAXDAYS numeric(18,8) --最长前置期
        declare @MAXDAYSVALUE numeric(18,8) --存放最终前置值
        Select @MAXDAYSVALUE=0 --初始化最长前置期
        Select @MAXDAYS=0      --初始化最长前置期
    
        --声明一个游标cur_BOMINFO,select语句中参数的个数必须要和从游标取出的变量名相同
        declare cur_BOMINFO cursor for select PRD_NO from ##BOMINFO
        --打开游标
        Open cur_BOMINFO
        --读取游标 一条记录插入变量
        Fetch next from cur_BOMINFO into @PRD_NO
        While(@@fetch_status = 0)
        Begin
            --根据品号查找对应供应商个数
            Select @BomCusCount=COUNT(C.CUS_NO) from DB_TD02.DB_TD02.dbo.PRDT_CUS C WHERE C.PRD_NO =@PRD_NO       
            --修改前Select top 1 @KND=KND,@IDX1=IDX1,@SUP1=SUP1 from DB_TD02.DB_TD02.dbo.PRDT P WHERE P.PRD_NO =@PRD_NO        
            --根据品号供应商信息更新
            Update ##BOMINFO set SUP_COUNT=SUP_COUNT+@BomCusCount where PRD_NO=@PRD_NO
             
            --查找货品查找单价
            Select top 1 @PRICE=isnull(UP_DEF.UP,0.0000) from DB_TD02.DB_TD02.dbo.UP_DEF UP_DEF WHERE PRD_NO=@PRD_NO
            --根据品号查找单价信息更新
            Update ##BOMINFO set PRICE=@PRICE where PRD_NO=@PRD_NO
    
            --根据品号查找最小采购量
            Select @MIN_PURCHASE=isnull(QTY_MIN,0),@MAXDAYS=NEED_DAYS from DB_TD02.DB_TD02.dbo.PRDT WHERE PRD_NO=@PRD_NO
            --根据品号查找最先采购量信息更新
            Update ##BOMINFO set MIN_PURCHASE=@MIN_PURCHASE where PRD_NO=@PRD_NO
            
            --判断最长前置期
            IF @MAXDAYS>@MAXDAYSVALUE
               begin
                Set  @MAXDAYSVALUE=@MAXDAYSVALUE
               end
    
            --继续....
            Fetch next from cur_BOMINFO into @PRD_NO
        End
        --关闭游标
        Close cur_BOMINFO
        --删除游标
        Deallocate cur_BOMINFO
    
        --查询结果
        --print @MAXDAYSVALUE
        --Select * from ##BOMINFO
    END
    View Code

    4、动态BOM成本计算

    BOM表

    树型结构如下: 

    求:
    1、要求结果按物料清单最底层原料起计算推算出半成品、成品的单价,希望能做成函数或存储过程。

    2、如果BOM数据量比较大时上面的方法运算就比较慢了,有没有办法只查询某货品如 A001时, 只计算跟A001相关货品。 WHERE M.GOODSID='A001'时的结果如下: 

     

    --> 生成测试数据表:BOM
    If not object_id('[BOM]') is null
        Drop table [BOM]
    Go
    Create table [BOM]([BILLID] int,[MGOODSID] nvarchar(4),[MQTY] decimal(18,2),[DGOODSID] nvarchar(4),[ITEMNO] decimal(18,2),[DQTY] decimal(18,2),[DPRICE] decimal(18,2)) 
    Insert BOM 
    Select 584,'A001',100.00,'B001',1.00,100.00,0.0 union all 
    Select 584,'A001',100.00,'C001',2,100,1.00 union all 
    Select 584,'A001',100.00,'D001',3,100,1 union all 
    Select 588,'B001',100.00,'E001',1,100,1 union all 
    Select 588,'B001',100.00,'F001',2,100,0.0 union all 
    Select 560,'F001',100.00,'G001',1,100,1 union all 
    Select 560,'F001',100.00,'J001',2,100,1 
    Go 
    --Select * from BOM
    /*
    BILLID MGOODSID MQTY DGOODSID ITEMNO DQTY DPRICE
    ------ -------- ---- -------- ------ ---- ------
       584 A001      100 B001          1  100      0
       584 A001      100 C001          2  100      1
       584 A001      100 D001          3  100      1
       588 B001      100 E001          1  100      1
       588 B001      100 F001          2  100      0
       560 F001      100 G001          1  100      1
       560 F001      100 J001          2  100      1
    */
    
    -->SQL查询如下:
    If not object_id('[Fn_BOM]') is null
        Drop function [Fn_BOM]
    Go
    Create Function Fn_BOM(@MGoodsID varchar(20))
    returns @t table(MGOODSID varchar(20),MQTY dec(18,2),DGOODSID varchar(20),ITEMNO dec(18,2),DQTY dec(18,2),DPRICE dec(18,2),DAMT dec(18,2),lvl int)
    as 
    begin
        declare @lvl int
        set @lvl=0
        insert @t select MGOODSID,MQTY,DGOODSID,ITEMNO,DQTY,DPRICE,NULL,@lvl from BOM where MGoodsID=@MGoodsID 
        while @@rowcount>0
            begin
                set @lvl=@lvl+1
                insert @t 
                select a.MGOODSID,a.MQTY,a.DGOODSID,a.ITEMNO,a.DQTY,a.DPRICE,a.DQTY*a.DPRICE,@lvl 
                from BOM a,@t b
                where a.MGoodsID=b.DGoodsID 
                    and b.lvl=@lvl-1
            end
        declare @i int
        select @i=max(lvl) from @t where dprice =0
        while @i>=0
            begin
                update a set 
                    dprice=(select sum(damt) from @t where a.DGOODSID=MGOODSID)/dqty,
                    damt=(select sum(damt) from @t where a.DGOODSID=MGOODSID)
                from @t a
                where lvl=@i
                    and dprice =0
                set @i=@i-1
            end
        update @t set DAMT=DQTY*DPRICE where DAMT is null
        return
    end
    go
    select * from fn_bom('a001') 
    /*
    MGOODSID MQTY DGOODSID ITEMNO DQTY DPRICE DAMT lvl
    -------- ---- -------- ------ ---- ------ ---- ---
    A001      100 B001          1  100      3  300   0
    A001      100 C001          2  100      1  100   0
    A001      100 D001          3  100      1  100   0
    B001      100 E001          1  100      1  100   1
    B001      100 F001          2  100      2  200   1
    F001      100 G001          1  100      1  100   2
    F001      100 J001          2  100      1  100   2
    */
    
    --计算成本存储过程
    If not object_id('[Sp_BOM]') is null
        Drop proc [Sp_BOM]
    Go
    Create proc Sp_BOM(@GOODSID varchar(20))
    as
    select MGOODSID,MQTY,sum(DAMT) DAMT 
    from fn_bom(@GOODSID) 
    where MGOODSID=@GOODSID
    group by MGOODSID,MQTY
    GO
    
    --调用过程
    exec [Sp_BOM] 'B001'
    /*
    MGOODSID MQTY DAMT
    -------- ---- ----
    B001      100  300
    */
    exec [Sp_BOM] 'A001'
    /*
    MGOODSID MQTY DAMT
    -------- ---- ----
    A001      100  500
    */
    
    --计算成本存储过程
    If not object_id('[Sp_BOM]') is null
        Drop proc [Sp_BOM]
    Go
    Create proc Sp_BOM(@GOODSID varchar(20))
    as
    select MGOODSID,MQTY,DGOODSID ,DQTY,DPRICE,DAMT 
    from fn_bom(@GOODSID) 
    where MGOODSID=@GOODSID
    GO
    
    --调用过程
    exec [Sp_BOM] 'B001'
    /*
    MGOODSID MQTY DGOODSID DQTY DPRICE DAMT
    -------- ---- -------- ---- ------ ----
    B001      100 E001      100      1  100
    B001      100 F001      100      2  200
    */
    exec [Sp_BOM] 'A001'
    /*
    MGOODSID MQTY DGOODSID DQTY DPRICE DAMT
    -------- ---- -------- ---- ------ ----
    A001      100 B001      100      3  300
    A001      100 C001      100      1  100
    A001      100 D001      100      1  100
    */
    View Code

    5、分组统计

    --方法一
    CREATE TABLE #Department(Nr VARCHAR(50),Name NVARCHAR(50));
    INSERT INTO #Department
            ( Nr, Name )
    VALUES  ( '001',N'人事部'),( '002',N'销售部'),( '003',N'财务部')
    
    CREATE TABLE #Person(DeparNr VARCHAR(50),Nr VARCHAR(50),Name NVARCHAR(50),Age INT);
    
    INSERT INTO #Person
            ( DeparNr, Nr, Name, Age )
    VALUES  ( '001', '9527', N'张三', 18 ),
            ( '001', '9528', N'李四', 25 ),
            ( '002', '9529', N'王五', 27 ),
            ( '003', '9530', N'赵六', 31 ),
            ( '001', '9531', N'钱七', 25 ),
            ( '001', '9532', N'孙八', 25 )
    
    ;WITH CTE AS
    (
        SELECT CONVERT(VARCHAR(50),'18~20') AS DisplayName,18 AS MinAge,20 AS MaxAge
        UNION ALL
        SELECT CONVERT(VARCHAR(50),RTRIM(CTE.MaxAge) + '~' + RTRIM(CTE.MaxAge+10)) ,CTE.MaxAge,CTE.MaxAge+10
            FROM CTE
            WHERE CTE.MaxAge < 60
    ),CTE2 AS(
    SELECT *
        FROM #Department 
            FULL JOIN CTE ON 1=1)
    
    SELECT a.Name,a.DisplayName,STUFF(b.Col.query('Employee/text()').value('.','nvarchar(100)'),1,1,'')
        FROM CTE2 a
            CROSS APPLY(SELECT ',' + Name AS Employee FROM #Person WHERE a.Nr = DeparNr AND Age >= a.MinAge AND Age < a.MaxAge FOR XML PATH(''),TYPE) b(Col)
            WHERE b.Col IS NOT NULL
            
    --方法二
    ;WITH Staff AS
    (
        SELECT  部门编号
                ,CASE WHEN 年龄 BETWEEN 18 AND 20 THEN '18~20'
                     WHEN 年龄 BETWEEN 21 AND 30 THEN '20~30'
                     ELSE 'other'
                END AS 年龄段
                ,姓名
        FROM    员工表
    )
    SELECT  a.部门名称
           ,b.年龄段
           ,COUNT(*) AS 人数
           ,姓名=STUFF((SELECT ','+姓名 FROM Staff WHERE 部门编号=b.部门编号 AND 年龄段=b.年龄段 FOR XML PATH('') ),1,1,'')
    FROM    部门表 AS a
            INNER JOIN Staff AS b ON a.部门编号 = b.部门编号
    GROUP BY a.部门名称
           ,b.年龄段
           ,b.部门编号;
           
    --注: 虽然两个方法结果不太一样,但可以借鉴思路
    View Code

    6、九九乘法表

     

    WITH cte AS (
        SELECT 1 AS Val UNION ALL SELECT Val+1 FROM cte WHERE Val<9
    ),tmp AS (
        SELECT * FROM (SELECT A.Val X,B.Val Y,IIF(B.Val>A.Val,'',CONCAT(B.Val,'×',A.Val,'',A.Val*B.Val)) AS XY FROM cte A,cte B) D PIVOT(MIN(XY) FOR Y IN ([1],[2],[3],[4],[5],[6],[7],[8],[9])) P
    )
    SELECT * FROM tmp
    View Code

    7、合并统计

     

    WITH Station(Name1,Name2,Value) AS (
        SELECT '南昌', '北京', 1.0 UNION ALL
        SELECT '南昌', '北京', 2.0 UNION ALL
        SELECT '北京', '南昌', 3.0 UNION ALL
        SELECT '南昌', '上海', 4.0 UNION ALL
        SELECT '上海', '南昌', 5.0 UNION ALL
        SELECT '北京', '上海', 6.0 UNION ALL
        SELECT '上海', '北京', 7.0 UNION ALL
        SELECT '南京', '北京', 8.0 UNION ALL
        SELECT '南昌', '南京', 9.0 UNION ALL
        SELECT '南昌', '南京', 10.0
    ),t1 AS (
        SELECT *, List=Station.Name1+'-'+Station.Name2+' / '+Station.Name2+'-'+Station.Name1
        FROM Station
    ),t2 AS (
        SELECT a.Name1, a.Name2, a.Value, b.List
        FROM t1 a
         OUTER APPLY(SELECT TOP 1 t1.List
                     FROM t1
                     WHERE t1.List LIKE '%'+a.Name1+'-'+a.Name2+'%')b
    )
    SELECT t2.List 路线, SUM(t2.Value) 合计 FROM t2 GROUP BY t2.List
    View Code
  • 相关阅读:
    如何将数组初始化为全0?
    什么是优先级队列(priority queue)?
    C语言中指针的指针是如何工作的?
    什么是队列(Queue)?
    理解*ptr++
    【Luogu】P4172水管局长(LCT)
    【Luogu】P4159迷路(矩阵优化)
    【Luogu】P3971Alice And Bob(贪心)
    【Luogu】P3211XOR和路径(高斯消元)
    【Luogu】P2445动物园(最大流)
  • 原文地址:https://www.cnblogs.com/zhaoshujie/p/10149077.html
Copyright © 2011-2022 走看看