如何根据流水帐记录使用一条SQL产生某月财务期初、期未库存表?
流水帐表结构如下:
品名            发生日期            规格          入出库标志          数量
马赛克          2008-01-08          10*10         1                   100
马赛克          2008-02-06          10*10         1                   30
马赛克          2008-04-01          10*15         1                   50
马赛克          2008-05-13          10*14         1                   60
马赛克          2008-01-16          10*14         -1                  30
马赛克          2008-02-21          10*15         -1                  20
马赛克          2008-03-25          10*10         -1                  50
马赛克          2008-05-31          10*10         -1                  70
马赛克          2008-06-09          10*14         -1                  10
大理石板        2008-01-08          100*100       1                   100
大理石板        2008-02-06          100*100       1                   30
大理石板        2008-04-01          100*150       1                   50
大理石板        2008-05-13          100*140       1                   60
大理石板        2008-01-16          100*140       -1                  30
大理石板        2008-02-21          100*150       -1                  20
大理石板        2008-03-25          100*100       -1                  50
大理石板        2008-05-31          100*100       -1                  70
大理石板        2008-06-09          100*140       -1                  10
统计结果如下:
SQL输入5月,含五月的数据
月份      品名      规格      期初数量      本期收入      本期发出      期未数量
2008-05   马赛克    10*10     80            0             70            10
2008-05   马赛克    10*14     -30           60            0             30
2008-05   马赛克    10*15     30            0             0             30
2008-05   大理石板  100*100   80            0             70            10
2008-05   大理石板  100*140   -30           60            0             30
2008-05   大理石板  100*150   30            0             0             30
此SQL涉及几个问题:
1.将入出库标志变横显示.
2.期未数量运算显示
3.期初数量运算显示

解决方案 »

  1.   

    嗯。这个语句是针对所有月的,但我可以在这个SQL上加个条件算出第一个月的。所以我现在需要一个全部的。
      

  2.   

    你这个数据要用另外一个表存储才行的;
    已经发生的第一笔数据不可改变,用一个JOB运行,每月的第一天把上月数据结算一次;如果你用一条sql语句查询,那期初库存是多少?还不是由另外一条sql语句出来计算出上月的期末库存
    那上月的上月期末库存又是多少?
    你总不能写那么的套嵌sql语句
      

  3.   

    sum(to_number(t.入出库标志) * t.数量) over(partition by t.品名, t.规格 order by t.品名, t.发生日期)就可以了,
      

  4.   


    select aa.品名,
           aa.month,
           aa.规格,
           bb.月初,
           case
             when aa.月末 - bb.月初 >= 0 then
              aa.月末 - bb.月初
             else
              0
           end 本期收入,
           case
             when aa.月末 - bb.月初 <= 0 then
              bb.月初 - aa.月末
             else
              0
           end 本期发出,
           aa.月末
      from (select a.品名,
                   to_char(a.发生日期, 'yyyy-mm') month,
                   a.规格,
                   a.日结 月末
              from (select t.品名,
                           t.发生日期,
                           t.规格,
                           sum(to_number(t.入出库标志) * t.数量) over(partition by t.品名, t.规格 order by t.品名, t.发生日期) 日结
                      from tablea t) a,
                   
                   (select 品名, 发生日期, 规格
                      from (select t.*,
                                   row_number() over(partition by t.品名, t.规格, to_char(t.发生日期, 'yyyy-mm') order by t.品名, t.规格, t.发生日期 desc) rn
                              from tablea t)
                     where rn = 1) b
             where a.品名 = b.品名
               and a.发生日期 = b.发生日期
               and a.规格 = b.规格) aa,
           (select a.品名,
                   to_char(a.发生日期, 'yyyy-mm') month,
                   a.规格,
                   a.日结 月初
              from (select t.品名,
                           t.发生日期,
                           t.规格,
                           sum(to_number(t.入出库标志) * t.数量) over(partition by t.品名, t.规格 order by t.品名, t.发生日期) 日结
                      from tablea t) a,
                   
                   (select 品名, 发生日期, 规格
                      from (select t.*,
                                   row_number() over(partition by t.品名, t.规格, to_char(t.发生日期, 'yyyy-mm') order by t.品名, t.规格, t.发生日期) rn
                              from tablea t)
                     where rn = 1) b
             where a.品名 = b.品名
               and a.发生日期 = b.发生日期
               and a.规格 = b.规格) bb where aa.品名 = bb.品名
       and aa.month = bb.month
       and aa.规格 = bb.规格
    sum(to_number(t.入出库标志) * t.数量) over(partition by t.品名, t.规格 order by t.品名, t.发生日期) 就可可得到日结,月结和报表就可以写了!不过好像有点复杂
      

  5.   

    我可以用一条SQL写出这样语句,但是我在这个SQL里用到了多次分析函数,感觉不是很好,希
    望能有其它办法加快我的查询速度。
    以下是上面提到通过日结表解决的一条语句完成的SQLselect tb.pm,tb.frq,tb.gg,
           decode(row_number() over(partition by tb.pm order by tb.PM,tb.RQ,tb.gg),1,0,
           LAG(l_bqlj, 1, 0) OVER (ORDER BY tb.PM,TB.RQ,tb.gg)) AS prev_BQJC,
           tb.bqrk,tb.bqck,tb.l_bqlj
     from
    (select ta.pm,ta.gg,ta.rq,ta.bqrk,ta.bqck,
           SUM(ta.bqlj) OVER (PARTITION BY ta.pm,ta.gg
                               ORDER BY ta.PM,ta.gg,ta.rq
                               RANGE UNBOUNDED PRECEDING) l_bqlj
     from
    (SELECT LSZ.品名 pm, LZS.规格 GG,to_char(LSZ.发生日期,'yyyy-mm') rq, 
           sum(decode(LSZ.入出库标志,1, LSZ.数量,0)) bqrk,
           sum(decode(LSZ.入出库标志,-1,LSZ.数量,0)) bqck,
           sum(decode(LSZ.入出库标志,1, LSZ.数量,0)) -
           sum(decode(LSZ.入出库标志,-1,LSZ.数量,0)) bqlj 
      FROM (select 品名,发生日期,规格,入出库标志,数量
            from 流水帐 b
           where to_char(发生日期,'yyyymm') = '200810'
           union
           SELECT 品名,发生日期,规格,入出库标志,数量
           FROM 月结表 a
           WHERE to_char(发生日期,'yyyymm') = ADD_MONTHS(TO_DATE('200810','YYYYMM'),-1)) LSZ
     group by to_char(LSZ.发生日期,'yyyy-mm'),LSZ.品名
     order by to_char(LSZ.发生日期,'yyyy-mm'),LSZ.品名) TA) tB
    下南是不需要月结表时统计当月的期初、期未库存数据。select tb.pm,tb.frq,tb.gg,
           decode(row_number() over(partition by tb.pm order by tb.PM,tb.RQ,tb.gg),1,0,
           LAG(l_bqlj, 1, 0) OVER (ORDER BY tb.PM,TB.RQ,tb.gg)) AS prev_BQJC,
           tb.bqrk,tb.bqck,tb.l_bqlj
     from
    (select ta.pm,ta.gg,ta.rq,ta.bqrk,ta.bqck,
           SUM(ta.bqlj) OVER (PARTITION BY ta.pm,ta.gg
                               ORDER BY ta.PM,ta.gg,ta.rq
                               RANGE UNBOUNDED PRECEDING) l_bqlj
     from
    (SELECT LSZ.品名 pm, LZS.规格 GG,to_char(LSZ.发生日期,'yyyy-mm') rq, 
           sum(decode(LSZ.入出库标志,1, LSZ.数量,0)) bqrk,
           sum(decode(LSZ.入出库标志,-1,LSZ.数量,0)) bqck,
           sum(decode(LSZ.入出库标志,1, LSZ.数量,0)) -
           sum(decode(LSZ.入出库标志,-1,LSZ.数量,0)) bqlj 
      FROM (select 品名,发生日期,规格,入出库标志,数量
            from 流水帐 b
           where to_char(发生日期,'yyyymm') >= '200810'
           union
           SELECT a.品名,to_date('200810','yyyymm') 发生日期,a.规格,0 入出库标志,0 数量
           FROM 流水帐 a
           GROUP BY a.品名,a.发生日期,a.规格
     group by to_char(LSZ.发生日期,'yyyy-mm'),LSZ.品名
     order by to_char(LSZ.发生日期,'yyyy-mm'),LSZ.品名) TA) tB
    两段语句间的差别就在于虚拟表A的使用。
    第一段的虚拟表A不用解释,他就是把虚拟表变成实体的月结表。
    第二段是中原始数据的来源因为是整个流水帐表,所以虚拟表B中不需要添加限制等于要求月的条件,而
    只要在使A中添国一份当月的空数据,即可将以前月份的结转到本月。
      

  6.   

    我的意思是,得到日结表
    select t.品名,
                           t.发生日期,
                           t.规格,
                           sum(to_number(t.入出库标志) * t.数量) over(partition by t.品名, t.规格 order by t.品名, t.发生日期) 日结
                      from tablea t这个应该不难!
    在日结表中按月统计一个月中最小时间的一条日结为月初值,最大的为月末值!
      

  7.   

    -- 好像没有看到规格10*15和100*150是2008-05月份的数据,从测试数据中共找出4条对应的记录:
    CREATE TABLE test_finance(品名 VARCHAR2(20),发生日期 DATE,规格 VARCHAR2(20),入出库标志 NUMBER,数量 NUMBER);SQL> SELECT TO_CHAR(发生日期, 'YYYY-MM') 月份, 品名, 规格, 期初数量, 本期收入, 本期发出, 期未数量
      2    FROM (SELECT TF.*,
      3                 SUM(入出库标志 * 数量) OVER(PARTITION BY 品名, 规格 ORDER BY 发生日期) - (入出库标志 * 数量) 期初数量,
      4                 DECODE(SIGN(入出库标志), 1, 数量, 0) 本期收入,
      5                 DECODE(SIGN(入出库标志), -1, 数量, 0) 本期发出,
      6                 SUM(入出库标志 * 数量) OVER(PARTITION BY 品名, 规格 ORDER BY 发生日期) 期未数量
      7            FROM TEST_FINANCE TF) TT
      8   WHERE TO_CHAR(发生日期, 'YYYY-MM') = '2008-05';月份    品名                 规格                   期初数量   本期收入   本期发出   期未数量
    ------- -------------------- -------------------- ---------- ---------- ---------- ----------
    2008-05 大理石板             100*100                      80          0         70         10
    2008-05 大理石板             100*140                     -30         60          0         30
    2008-05 马赛克               10*10                        80          0         70         10
    2008-05 马赛克               10*14                       -30         60          0         30
      

  8.   

    你看我回复的第二段SQL,
    "第二段是中原始数据的来源因为是整个流水帐表,所以虚拟表B中不需要添加限制等于要求月的条件,而 
    只要在使A中添国一份当月的空数据,即可将以前月份的结转到本月。 "
      

  9.   

    LZ,你的一个产品的一个规则每个月只有一条流水账记录?如果是那就简化多了!如果有多条,那上面的SQL也许已经能达到要求了!
      

  10.   

    修正下select aa.品名,
           aa.month,
           aa.规格,
           bb.月初,
           case
             when aa.月末 - bb.月初 >= 0 then
              aa.月末 - bb.月初
             else
              0
           end 本期收入,
           case
             when aa.月末 - bb.月初 <= 0 then
              bb.月初 - aa.月末
             else
              0
           end 本期发出,
           aa.月末
      from (select a.品名,
                   to_char(a.发生日期, 'yyyy-mm') month,
                   a.规格,
                   a.日结 月末
              from (select t.品名,
                           t.发生日期,
                           t.规格,
                           sum(to_number(t.入出库标志) * t.数量) over(partition by t.品名, t.规格 order by t.品名, t.发生日期) 日结
                      from tablea t) a,
                   
                   (select 品名, 发生日期, 规格
                      from (select t.*,
                                   row_number() over(partition by t.品名, t.规格, to_char(t.发生日期, 'yyyy-mm') order by t.品名, t.规格, t.发生日期 desc) rn
                              from tablea t)
                     where rn = 1) b
             where a.品名 = b.品名
               and a.发生日期 = b.发生日期
               and a.规格 = b.规格) aa,
           (select a.品名,
                   to_char(a.发生日期, 'yyyy-mm') month,
                   a.规格,
                   a.日结 月初
              from (select t.品名,
                           t.发生日期,
                           t.规格,
                           sum(to_number(t.入出库标志) * t.数量) over(partition by t.品名, t.规格 order by t.品名, t.发生日期) - to_number(t.入出库标志) * t.数量 日结
                      from tablea t) a,
                   
                   (select 品名, 发生日期, 规格
                      from (select t.*,
                                   row_number() over(partition by t.品名, t.规格, to_char(t.发生日期, 'yyyy-mm') order by t.品名, t.规格, t.发生日期) rn
                              from tablea t)
                     where rn = 1) b
             where a.品名 = b.品名
               and a.发生日期 = b.发生日期
               and a.规格 = b.规格) bb where aa.品名 = bb.品名
       and aa.month = bb.month
       and aa.规格 = bb.规格;
      

  11.   


    --还是有点没写好,参考一下吧:SQL> select * from io_tab;P_NAME           IO_DATE     P_TYPE               FLAG   P_COUNT
    ---------------- ----------- -------------------- ---- ---------
    马赛克           2008-1-8    10*10                1          100
    马赛克           2008-2-6    10*10                1           30
    马赛克           2008-4-1    10*15                1           50
    马赛克           2008-5-13   10*14                1           60
    马赛克           2008-3-25   10*10                -1          50
    马赛克           2008-5-31   10*10                -1          70
    马赛克           2008-6-9    10*14                -1          10
    大理石板         2008-1-8    100*100              1          100
    大理石板         2008-2-6    100*100              1           30
    大理石板         2008-4-1    100*150              1           50
    大理石板         2008-5-13   100*140              1           60
    大理石板         2008-1-16   100*140              -1          30
    大理石板         2008-2-21   100*150              -1          20
    大理石板         2008-3-25   100*100              -1          50
    大理石板         2008-5-31   100*100              -1          70
    大理石板         2008-6-9    100*140              -1          1016 rows selectedSQL> 
    SQL> select t.a  月份,
      2         t.b  品名,
      3         t.c  规格,
      4         t1.d 期初数量,
      5         t2.d 本期收入,
      6         t3.d 本期发出,
      7         t4.d 期未数量
      8    from (select to_char(io_date, 'yyyy-mm') a, p_name b, p_type c
      9            from io_tab
     10           group by to_char(io_date, 'yyyy-mm'), p_name, p_type) t,
     11         (select to_char(add_months(io_date, -1), 'yyyy-mm') a,
     12                 p_name b,
     13                 p_type c,
     14                 sum(to_number(flag) * p_count) d
     15            from io_tab
     16          where to_char(add_months(io_date, -1), 'yyyy-mm') >= '2008-01'
     17          and to_char(add_months(io_date, -1), 'yyyy-mm')<=to_char(add_months(io_date, -1), 'yyyy-mm')
     18           group by to_char(add_months(io_date, -1), 'yyyy-mm'),
     19                    p_name,
     20                    p_type) t1,
     21         (select to_char(io_date, 'yyyy-mm') a,
     22                 p_name b,
     23                 p_type c,
     24                 sum(decode(flag, 1, p_count, 0)) d
     25            from io_tab
     26           group by to_char(io_date, 'yyyy-mm'), p_name, p_type) t2,
     27         (select to_char(io_date, 'yyyy-mm') a,
     28                 p_name b,
     29                 p_type c,
     30                 sum(decode(flag, -1, p_count, 0)) d
     31            from io_tab
     32           group by to_char(io_date, 'yyyy-mm'), p_name, p_type) t3,
     33         (select to_char(io_date, 'yyyy-mm') a,
     34                 p_name b,
     35                 p_type c,
     36                 sum(to_number(flag) * (p_count)) d
     37            from io_tab
     38           where to_char(io_date, 'yyyy-mm') >= '2008-01'
     39             and to_char(io_date, 'yyyy-mm') <= to_char(io_date, 'yyyy-mm')
     40           group by to_char(io_date, 'yyyy-mm'), p_name, p_type) t4
     41   where t.a = to_char(add_months(to_date(t1.a, 'yyyy-mm'), 1), 'yyyy-mm')
     42     and t.a = t2.a(+)
     43     and t.a = t3.a(+)
     44     and t.a = t4.a(+)
     45     and t.b = t1.b(+)
     46     and t.b = t2.b(+)
     47     and t.b = t3.b(+)
     48     and t.b = t4.b(+)
     49     and t.c = t1.c(+)
     50     and t.c = t2.c(+)
     51     and t.c = t3.c(+)
     52     and t.c = t4.c(+)
     53   order by t.a, t.b, t.c;月份    品名             规格                   期初数量   本期收入   本期发出   期未数量
    ------- ---------------- -------------------- ---------- ---------- ---------- ----------
    2008-02 大理石板         100*100                      30         30          0         30
    2008-02 大理石板         100*150                     -20          0         20        -20
    2008-02 马赛克           10*10                        30         30          0         30
    2008-03 大理石板         100*100                     -50          0         50        -50
    2008-03 马赛克           10*10                       -50          0         50        -50
    2008-04 大理石板         100*150                      50         50          0         50
    2008-04 马赛克           10*15                        50         50          0         50
    2008-05 大理石板         100*100                     -70          0         70        -70
    2008-05 大理石板         100*140                      60         60          0         60
    2008-05 马赛克           10*10                       -70          0         70        -70
    2008-05 马赛克           10*14                        60         60          0         60
    2008-06 大理石板         100*140                     -10          0         10        -10
    2008-06 马赛克           10*14                       -10          0         10        -1013 rows selectedSQL> 
      

  12.   

    drop table TABLEA;
    create table TABLEA
    (
      品名    VARCHAR2(30),
      发生日期  DATE,
      规格    VARCHAR2(20),
      入出库标志 VARCHAR2(10),
      数量    NUMBER
    );
    insert into TABLEA (品名, 发生日期, 规格, 入出库标志, 数量)
    values ('马赛克', to_date('06-02-2008', 'dd-mm-yyyy'), '10*10', '1', 30);
    insert into TABLEA (品名, 发生日期, 规格, 入出库标志, 数量)
    values ('马赛克', to_date('01-04-2008', 'dd-mm-yyyy'), '10*15', '1', 50);
    insert into TABLEA (品名, 发生日期, 规格, 入出库标志, 数量)
    values ('马赛克', to_date('13-05-2008', 'dd-mm-yyyy'), '10*14', '1', 60);
    insert into TABLEA (品名, 发生日期, 规格, 入出库标志, 数量)
    values ('马赛克', to_date('16-01-2008', 'dd-mm-yyyy'), '10*14', '-1', 30);
    insert into TABLEA (品名, 发生日期, 规格, 入出库标志, 数量)
    values ('马赛克', to_date('21-02-2008', 'dd-mm-yyyy'), '10*15', '-1', 20);
    insert into TABLEA (品名, 发生日期, 规格, 入出库标志, 数量)
    values ('马赛克', to_date('25-03-2008', 'dd-mm-yyyy'), '10*10', '-1', 50);
    insert into TABLEA (品名, 发生日期, 规格, 入出库标志, 数量)
    values ('马赛克', to_date('31-05-2008', 'dd-mm-yyyy'), '10*10', '-1', 70);
    insert into TABLEA (品名, 发生日期, 规格, 入出库标志, 数量)
    values ('马赛克', to_date('09-06-2008', 'dd-mm-yyyy'), '10*14', '-1', 10);
    insert into TABLEA (品名, 发生日期, 规格, 入出库标志, 数量)
    values ('大理石板', to_date('08-01-2008', 'dd-mm-yyyy'), '100*100', '1', 100);
    insert into TABLEA (品名, 发生日期, 规格, 入出库标志, 数量)
    values ('大理石板', to_date('06-02-2008', 'dd-mm-yyyy'), '100*100', '1', 30);
    insert into TABLEA (品名, 发生日期, 规格, 入出库标志, 数量)
    values ('大理石板', to_date('01-04-2008', 'dd-mm-yyyy'), '100*150', '1', 50);
    insert into TABLEA (品名, 发生日期, 规格, 入出库标志, 数量)
    values ('大理石板', to_date('13-05-2008', 'dd-mm-yyyy'), '100*140', '1', 60);
    insert into TABLEA (品名, 发生日期, 规格, 入出库标志, 数量)
    values ('大理石板', to_date('16-01-2008', 'dd-mm-yyyy'), '100*140', '-1', 30);
    insert into TABLEA (品名, 发生日期, 规格, 入出库标志, 数量)
    values ('大理石板', to_date('21-02-2008', 'dd-mm-yyyy'), '100*150', '-1', 20);
    insert into TABLEA (品名, 发生日期, 规格, 入出库标志, 数量)
    values ('大理石板', to_date('25-03-2008', 'dd-mm-yyyy'), '100*100', '-1', 50);
    insert into TABLEA (品名, 发生日期, 规格, 入出库标志, 数量)
    values ('大理石板', to_date('31-05-2008', 'dd-mm-yyyy'), '100*100', '-1', 70);
    insert into TABLEA (品名, 发生日期, 规格, 入出库标志, 数量)
    values ('大理石板', to_date('09-06-2008', 'dd-mm-yyyy'), '100*140', '-1', 10);
    insert into TABLEA (品名, 发生日期, 规格, 入出库标志, 数量)
    values ('马赛克', to_date('12-01-2008', 'dd-mm-yyyy'), '10*10', '1', 20);
    insert into TABLEA (品名, 发生日期, 规格, 入出库标志, 数量)
    values ('大理石板', to_date('20-02-2008', 'dd-mm-yyyy'), '100*100', '1', 45);
    insert into TABLEA (品名, 发生日期, 规格, 入出库标志, 数量)
    values ('马赛克', to_date('08-01-2008', 'dd-mm-yyyy'), '10*10', '1', 100);
    commit;
    select aa.品名,
           aa.month,
           aa.规格,
           bb.月初,
           case
             when aa.月末 - bb.月初 >= 0 then
              aa.月末 - bb.月初
             else
              0
           end 本期收入,
           case
             when aa.月末 - bb.月初 <= 0 then
              bb.月初 - aa.月末
             else
              0
           end 本期发出,
           aa.月末
      from (select a.品名,
                   to_char(a.发生日期, 'yyyy-mm') month,
                   a.规格,
                   a.日结 月末
              from (select t.品名,
                           t.发生日期,
                           t.规格,
                           sum(to_number(t.入出库标志) * t.数量) over(partition by t.品名, t.规格 order by t.品名, t.发生日期) 日结
                      from tablea t) a,
                   
                   (select 品名, 发生日期, 规格
                      from (select t.*,
                                   row_number() over(partition by t.品名, t.规格, to_char(t.发生日期, 'yyyy-mm') order by t.品名, t.规格, t.发生日期 desc) rn
                              from tablea t)
                     where rn = 1) b
             where a.品名 = b.品名
               and a.发生日期 = b.发生日期
               and a.规格 = b.规格) aa,
           (select a.品名,
                   to_char(a.发生日期, 'yyyy-mm') month,
                   a.规格,
                   a.日结 月初
              from (select t.品名,
                           t.发生日期,
                           t.规格,
                           sum(to_number(t.入出库标志) * t.数量) over(partition by t.品名, t.规格 order by t.品名, t.发生日期) - to_number(t.入出库标志) * t.数量 日结
                      from tablea t) a,
                   
                   (select 品名, 发生日期, 规格
                      from (select t.*,
                                   row_number() over(partition by t.品名, t.规格, to_char(t.发生日期, 'yyyy-mm') order by t.品名, t.规格, t.发生日期) rn
                              from tablea t)
                     where rn = 1) b
             where a.品名 = b.品名
               and a.发生日期 = b.发生日期
               and a.规格 = b.规格) bb
      where aa.品名 = bb.品名
       and aa.month = bb.month
       and aa.规格 = bb.规格;
      

  13.   

    首先谢谢各位的关注。对于我在提问中自己给出的SQL虽已满足需求,
    但也有不尽完善的地方,因为如果我每个月的产品及规格不多即单月品种流量不大时,
    我用分析函数做查询时速度是可以保障的。反之则会影响系统查询性能。
    看了各位给出的其它SQL解决思路后,给我了一定的启发,
    在此我很强调性能问题,因为我一张流水帐表一天有可能生成7k以上的记录。我在普通的机器测试13万条的数据,
    用我的SQL需要0.13秒左右,如果同时在线的操作人员不算多的时候,用虚拟表速度应该还可以接受。