报表分组通用 sql 解决方案已出 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
madworks
V2EX    Java

报表分组通用 sql 解决方案已出

  •  
  •   madworks 2020-11-02 10:53:34 +08:00 2991 次点击
    这是一个创建于 1880 天前的主题,其中的信息可能已经有所发展或是发生改变。

    接上一主题 t/719136

    通过 dba 的苦思冥想,sql 已经出来了,大家有这方面的可以参考下 sql 效果图见下,懂的人已懂

    https://vkceyugu.cdn.bspapp.com/VKCEYUGU-imgbed/9a586ec3-41ea-4190-9b03-37ceb074c486.png

    行分组字段 row1,row2,列分组字段 column1,column2,column1 有 012 三种状态,column2 有 01 两种状态,sql 可以呈现 column1,column2 所有组合的值并小计总计,而且可以随意扩展,sql 也是很简洁,稍微变换下可以解决行的分组,可以完美呈现类似下面的报表

    https://vkceyugu.cdn.bspapp.com/VKCEYUGU-imgbed/0dfbb436-d5b1-493e-b0d9-2918aecd145e.png 不用自己再逻辑处理数据,动态分组处理逻辑代码简直写的崩溃,不信的人可以去试下

    17 条回复    2020-11-02 16:15:09 +08:00
    madworks
        1
    madworks  
    OP
       2020-11-02 10:56:38 +08:00
    建表 sql
    SET FOREIGN_KEY_CHECKS=0;
    DROP TABLE IF EXISTS `xx`;
    CREATE TABLE `xx` (
    `id` int(11) NOT NULL,
    `fee` int(255) DEFAULT NULL,
    `column1` varchar(255) DEFAULT NULL,
    `column2` varchar(255) DEFAULT NULL,
    `row1` varchar(255) DEFAULT NULL,
    `row2` varchar(255) DEFAULT '',
    PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

    INSERT INTO `xx` VALUES ('1', '454', '0', '1', '李', '湖北');
    INSERT INTO `xx` VALUES ('2', '35', '1', '0', '王', '上海');
    INSERT INTO `xx` VALUES ('3', '23', '1', '1', '李', '上海');
    INSERT INTO `xx` VALUES ('4', '567', '1', '0', '李', '上海');
    INSERT INTO `xx` VALUES ('5', '43', '0', '2', '王', '湖北');
    INSERT INTO `xx` VALUES ('6', '456', '0', '1', '李', '河南');
    zoharSoul
        2
    zoharSoul  
       2020-11-02 10:57:06 +08:00
    听起来就是行转列?
    madworks
        3
    madworks  
    OP
       2020-11-02 10:57:12 +08:00
    实现 sql
    SELECT
    row1,
    row2,
    IFNULL(column1,'总计'),
    IFNULL(column2,'小计'),
    sum(fee) fee
    FROM
    (
    SELECT
    row1,
    row2,
    column1,
    column2,
    fee
    FROM
    (
    SELECT
    *
    FROM
    (
    SELECT
    x.row1,
    x.row2,
    y.column1,
    y.column2,
    0 AS fee
    FROM
    (
    SELECT
    row1,
    row2
    FROM
    xx
    GROUP BY
    row1,
    row2,
    column1,
    column2
    ) x,
    (
    SELECT
    *
    FROM
    (
    SELECT
    0 AS column1
    UNION
    SELECT
    1 AS column1
    ) AS t1
    LEFT JOIN (
    SELECT
    0 AS column2
    UNION
    SELECT
    1 AS column2
    UNION
    SELECT
    2 AS column2
    ) AS t2 ON 1 = 1
    ) y
    WHERE
    (3 * y.column1 + y.column2) NOT IN (
    SELECT
    3 * m.column1 + m.column2
    FROM
    xx m
    WHERE
    m.row1 = x.row1
    AND m.row2 = x.row2
    )
    GROUP BY
    row1,
    row2,
    column1,
    column2
    ) n
    UNION
    SELECT
    row1,
    row2,
    column1,
    column2,
    sum(fee) fee
    FROM
    xx
    GROUP BY
    row1,
    row2,
    column1,
    column2
    ) temp
    GROUP BY
    row1,
    row2,
    column1,
    column2
    ) AS temp
    GROUP BY
    row1,
    row2,
    column1,
    column2 WITH ROLLUP
    madworks
        4
    madworks  
    OP
       2020-11-02 11:00:20 +08:00
    @zoharSoul,不是行转列,有 2 个行,2 个列,每行每列都有固定的几个状态,一些行列组合肯定没有记录,现在写个 sql 可以展现所有组合的值,没有记录的赋值 0,你怎么写?听起来很简单,但是实现起来还是比较难的
    weizhen199
        5
    weizhen199  
       2020-11-02 11:06:53 +08:00
    pivot 应该是可以的

    还有 dba 帮忙写业务 sql 的,太羡慕。。。/哭
    madworks
        6
    madworks  
    OP
       2020-11-02 11:12:16 +08:00
    @weizhen199 是啊,pbi,tableau 这种写这个不要太简单,可惜不开源,不能搞在项目里,只能单独的搭建一套报表系统
    madworks
        7
    madworks  
    OP
       2020-11-02 11:17:47 +08:00
    @weizhen199 @zoharSoul 你们说的好像是一个,我了解下,我以为你说的 bi pivot
    l00t
        8
    l00t  
       2020-11-02 12:05:11 +08:00
    这样不还是存在你在原帖中自己评论的问题?值多了怎么办?打算写多长?

    不如搞个字典表,直接关联上字典表,比你这么挨个 select 要简洁很多。而且字典表也方便管理这种状态值,甚至可以把中文说明都给直接带出来。
    madworks
        9
    madworks  
    OP
       2020-11-02 13:53:14 +08:00
    @l00t 哈哈,是这样的,多谢
    madworks
        10
    madworks  
    OP
       2020-11-02 14:25:54 +08:00
    SELECT
    row1,
    row2,
    IFNULL(column1, '总计'),
    IFNULL(column2, '小计'),
    sum(fee) fee
    FROM
    (
    SELECT
    row1,
    row2,
    column1,
    column2,
    fee
    FROM
    (
    SELECT
    *
    FROM
    (
    SELECT
    x.row1,
    x.row2,
    y.column1,
    y.column2,
    0 AS fee
    FROM
    (
    SELECT
    row1,
    row2
    FROM
    xx_index
    GROUP BY
    row1,
    row2
    ) x,
    (
    SELECT
    column1,
    column2
    FROM
    xx_index
    GROUP BY
    column1,
    column2
    ) y
    WHERE
    (3 * y.column1 + y.column2) NOT IN (
    SELECT
    3 * m.column1 + m.column2
    FROM
    xx m
    WHERE
    m.row1 = x.row1
    AND m.row2 = x.row2
    )
    GROUP BY
    row1,
    row2,
    column1,
    column2
    ) n
    UNION
    SELECT
    row1,
    row2,
    column1,
    column2,
    sum(fee) fee
    FROM
    xx
    GROUP BY
    row1,
    row2,
    column1,
    column2
    ) temp
    GROUP BY
    row1,
    row2,
    column1,
    column2
    ) AS temp
    GROUP BY
    row1,
    row2,
    column1,
    column2 WITH ROLLUP
    wysnylc
        11
    wysnylc  
       2020-11-02 14:29:54 +08:00
    代码可能不会超过 10 行(lambda)
    madworks
        12
    madworks  
    OP
       2020-11-02 14:43:32 +08:00
    @wysnylc 太自信了
    zifangsky
        13
    zifangsky  
       2020-11-02 15:29:14 +08:00
    你这个 SQL 写得也太复杂了吧,先不说在数据量大的情况下执行速度怎么样,光是后期维护也非常困难吧。而且,一般来说复杂逻辑都是放在业务代码里手动实现,SQL 只做简单高效的查询就可以了
    madworks
        14
    madworks  
    OP
       2020-11-02 16:04:59 +08:00
    @zifangsky 这 sql 只是看起来长,不复杂啊,修改的话就套模板,很快的。执行速度的话只要行列分组不超过 5 个都在 1 分钟之类,这种报表用逻辑代码写才是噩梦,我写出来要几百行代码,各种递归,小计总计还要判断位置,没写过想象不到的复杂
    madworks
        15
    madworks  
    OP
       2020-11-02 16:10:32 +08:00
    @zifangsky 随便给你看下插入小计的位置的代码
    public void getInsertPosition(int levelFlag, List<List<Integer>> groupOrderInfoNumCollection,
    List<OrderInfo> orderInfoList, int len, List<List<Integer>> nodeIndexList, List<List<Integer>> nestOrderInfoRepeatCollection)
    {
    int insertPos = 0;
    int position = 0;
    int[] listLen = new int[levelFlag];

    if (levelFlag >= 1)
    {
    int[] index = new int[levelFlag];
    for (int i = 0; i < levelFlag - 1; i++)
    {
    index[i] = groupOrderInfoNumCollection.get(i + 1).get(0);
    }
    for (Integer integer : groupOrderInfoNumCollection.get(0))
    {
    position += len * integer;
    orderInfoList.remove(position);
    insertPos += integer;
    nodeIndexList.get(0).add(insertPos);
    if(integer == 1){
    nestOrderInfoRepeatCollection.get(0).add(insertPos);
    }
    for (int i = 0; i < levelFlag - 1; i++)
    {
    if (insertPos == index[i])
    {
    nodeIndexList.get(i + 1).add(insertPos);
    orderInfoList.remove(insertPos * len);
    listLen[i]++;
    if (listLen[i] < groupOrderInfoNumCollection.get(i + 1).size())
    {
    index[i] += groupOrderInfoNumCollection.get(i + 1).get(listLen[i]);
    }
    }
    }
    }
    if(orderInfoList.size() > 1){
    orderInfoList.remove(orderInfoList.size() - 1);
    }
    }
    }
    madworks
        16
    madworks  
    OP
       2020-11-02 16:14:26 +08:00
    把结果集整理成需要的形式的代码
    public void resultSetHandler(int rowLen, int levelFlag, Map<String, Object> map,
    Map<String, Object> rowGroupMap, String[] rowParamArr,
    List<List<OrderInfo>> nestOrderInfoCollection, int len, int i)
    {
    if (rowLen >= 1)
    {
    List<List<Integer>> temp = new ArrayList<>();
    levelFlag--;
    map.remove(rowParamArr[rowLen]);
    rowGroupMap.remove(rowParamArr[rowLen]);
    nestOrderInfoCollection.get(i).addAll(performanceService.getOrderInfo(map));
    deleteNodeTotal(nestOrderInfoCollection.get(i), len, rowGroupMap, rowParamArr, levelFlag,temp,temp);
    rowLen--;
    i++;
    resultSetHandler(rowLen, levelFlag, map, rowGroupMap, rowParamArr, nestOrderInfoCollection, len, i);
    }
    }
    madworks
        17
    madworks  
    OP
       2020-11-02 16:15:09 +08:00
    删除节点统计的代码
    public void deleteNodeTotal(List<OrderInfo> orderInfoList, int len,
    Map<String, Object> rowGroupMap, String[] rowParamArr, int levelFlag, List<List<Integer>> nestOrderInfoIndexCollection, List<List<Integer>> nestOrderInfoRepeatCollection)
    {

    List<List<Integer>> groupOrderInfOnumCollection= new ArrayList<List<Integer>>();
    for (int i = 0; i < levelFlag; i++)
    {
    nestOrderInfoIndexCollection.add(new ArrayList<Integer>());
    nestOrderInfoRepeatCollection.add(new ArrayList<Integer>());
    groupOrderInfoNumCollection.add(new ArrayList<Integer>());
    }

    if (rowParamArr.length > 1)
    {

    getGroupOrderInfoNum(levelFlag, rowGroupMap, rowParamArr, groupOrderInfoNumCollection);

    if (levelFlag >= 1)
    {
    getInsertPosition(levelFlag, groupOrderInfoNumCollection, orderInfoList, len, nestOrderInfoIndexCollection, nestOrderInfoRepeatCollection);
    }
    }

    }
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1259 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 25ms UTC 17:21 PVG 01:21 LAX 09:21 JFK 12:21
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86