需求:看如下表格的统计需求

生产调度中心部门需要从IT技术部门得到这些统计数据

步骤:

(1)获取所有的子公司列表

(2)遍历所有的子公司,获取每个子公司的库存信息

(3)遍历所有的库存信息,并对库存信息进行扩充

(4)生成汇总库存信息(这里使用Redis进行生成)

(5)使用Excel工具类将汇总统计数据导出Excel

问题:

(1)为什么使用控制台后台生成统计数据

a. 因为在导出Excel的时候可能会出现120s的timeout问题,因为导出Excel是在浏览器操作完成的,web浏览器使用的是HTTP协议,HTTP协议的最大请求返回时间为120s,如果120s内,web服务器没有完成返回响应报文,则web服务器会返回一个120s超时的报文。

b. 是否是120s,是可以在web服务配置文件中设置的,如果设置成最大则封顶120s。

c. 所以,将生成统计数据功能放在控制台程序中完成,而降纯粹使用已经生成好的统计数据导出Excel在浏览器中完成。

当然也可以使用控制台程序导出Excel, 但是控制台程序只有IT部门才能运行操作;而web浏览器则所有人都可以操作

(2)为什么使用Redis生成汇总数据

a. 生成汇总数据是一个map-reduce的问题,比较耗费内存,为了防止内存溢出,则使用Redis内存,使用Redis的API进行map-reduce操作,而不直接使用web服务器机器的内存

b. 所以,所有比较占用内存的运算存储操作都应该使用专门的内存机器

(3)如何在浏览器导出Excel,如何在控制台导出Excel

a. 导出Excel当然使用php-excel进行操作Excel,最好引入php-excel并自己写一个导出Excel的工具类;

b. 如果使用浏览器导出Excel超时,则可以使用控制台程序导出Excel,即不适用HTTP协议,而是直接使用Socket创建连接,使用TCP协议

(4)生成使用Excel形成汇总数据

a. 首先,Excel的功能是非常的齐全的,可以使用Excel画单据的设计; 可以使用Excel的求和公式进行求和;可以使用Excel形成汇总透视表

b. 使用Excel形成汇总透视表的操作为: 插入->透视表; 当然,也可以生成透视图

(5)如何确认生成的统计数据是正确的数据

最好的方法是和其他一些统计功能进行对比核对,当然,有时需要更改一些统计条件才能得到和其他统计功能一模一样的数据

(6)有哪些好用的统计数据BI的相关工具

一般来说,有Superset和CBoard

代码如下:

在控制台中执行命令php yii wms-info/wms-export

  1. /**
  2. * 将库存及销售生成统计数据并导出EXCEL
  3. */
  4. public function actionWmsExport(){
  5. $data = [];
  6. $wms_check_begin_at = 0;
  7. # 统计数据的截止日期,使用当前日期则可以和其他统计功能进行核对数据是否正确
  8. // $wms_check_end_at = time();
  9. # 统计数据的截止日期
  10. $wms_check_end_at = strtotime('2019-03-30 23:59:59');
  11.  
  12. # 调用API接口获取统计数据
  13. $common_producer_info_list = \core\models\CommonProducerInfo::getCommonProducerInfoList();
  14. foreach ($common_producer_info_list as $common_producer_info){
  15. $common_producer_info_id = $common_producer_info['division_id'];
  16. $dataProvider = \core\models\WmsInfo::_getWmsProductInfoListProvider($common_producer_info_id, $wms_check_begin_at, $wms_check_end_at);
  17. # 不适用二层循环的方法,时间复杂度为n*n
  18. /*
  19. foreach ($dataProvider->getModels() as $model){
  20. $data[] = $model;
  21. }
  22. */
  23. # 使用此方法,降低时间复杂度,时间复杂度为n
  24. $data = array_merge($data, $dataProvider->getModels());
  25. }
  26.  
  27. # 遍历统计数据,将统计数据进行扩充,并生成汇总统计数据
  28. foreach ($data as &$item){
  29. $in_sheet_number = $item['in_sheet_number'];
  30. # 不推荐在循环结构中进行数据库查询,因为会进行n次数据库查询,占用数据库连接资源和内存,时间;
  31. # 但是这里调用了现有的API则这样使用,不再编写新的API,增加开发速度
  32. $in_sheet = \core\models\WmsProductInSheet::findOne(['wms_product_in_sheet_number'=>$in_sheet_number]);
  33. # 是退货类型的入库单则为退货库存,无需查询销售退货表,这样降低了逻辑复杂度
  34. if (strpos($in_sheet->stock_origin_type, '退货') !==false){
  35. $item['refund_weight'] = \common\models\Base::weightBcdiv($item['weight']);
  36. $item['not_refund_weight'] = 0;
  37. }else{
  38. $item['refund_weight'] = 0;
  39. $item['not_refund_weight'] = \common\models\Base::weightBcdiv($item['weight']);
  40. }
  41.  
  42. # 这里,本不推荐直接编写sql进行查询数据库,单为了开发速度,不得已而用之
  43. $command = (new \yii\db\Query())->select([
  44. 'po.wms_product_out_sheet_type As type',
  45. '
  46. IFNULL(
  47. podet.wms_product_out_detail_info_out_weight,
  48. 0
  49. )
  50. AS sum_out_weight',
  51. ])->from('{{%wms_product_out_detail_info}} podet')
  52. ->leftJoin('{{%wms_product_out_sheet}} po', 'po.wms_product_out_sheet_number = podet.wms_product_out_sheet_number')
  53. ->where('podet.is_del = 0
  54. AND po.is_del = 0
  55. AND po.wms_product_out_sheet_status = 1
  56. AND podet.wms_product_in_sheet_number = :in_sheet_number
  57. AND po.wms_product_out_sheet_product_out_date >= :wms_check_begin_at
  58. AND po.wms_product_out_sheet_product_out_date <= :wms_check_end_at',
  59. [
  60. ':in_sheet_number'=>$in_sheet_number,
  61. ':wms_check_begin_at'=>$wms_check_begin_at,
  62. ':wms_check_end_at'=>$wms_check_end_at
  63. ])->createCommand();
  64. $ol = $command->queryAll();
  65. # 调试用sql
  66. // echo $command->getRawSql();die;
  67.  
  68. # 不推荐使用遍历求和,增加时间复杂度, 应该直接使用map-reduce进行求和,或直接使用统计求和的sql语句
  69. $sales_out_weight = 0;
  70. foreach ($ol as $o){
  71. if (trim($o['type']) == '销售' || empty($o['type']) || trim($o['type']) == '现款现货订单'){
  72. $sales_out_weight = bcadd($sales_out_weight, $o['sum_out_weight']);
  73. }
  74. }
  75.  
  76. // $sales_out_weight = \core\components\ArrayHelper::sumByColumn($ol, 'sum_out_weight');
  77.  
  78. # 为采购入库则为定向采购出库,不再查询销售订单相关数据表,降低逻辑复杂度
  79. if (strpos($in_sheet->stock_origin_type, '采购') !==false){
  80. $item['ding_weight'] = \common\models\Base::weightBcdiv(intval($sales_out_weight));
  81. $item['not_ding_weight'] = 0;
  82. }else{
  83. $item['ding_weight'] = 0;
  84. $item['not_ding_weight'] = \common\models\Base::weightBcdiv(intval($sales_out_weight));
  85. }
  86.  
  87. if(!(empty(intval($item['refund_weight'])) && empty(intval($item['not_refund_weight'])) && empty(intval($item['ding_weight'])) && empty(intval($item['not_ding_weight'])))) {
  88. $rk = $item['producer_id'] . "-" . $item['info_name'] . "-" . $item['grade_name'];
  89. $producer_name = \core\models\WmsTransfer::getCommonProducerInfoName($item['producer_id']);
  90. if (strpos($producer_name, '亳州')){
  91. $producer_name = '亳州市汉广天工绿色中药材初加工有限公司';
  92. }
  93. # 使用Redis进行存储统计汇总数据,不占用系统内存;也可以直接使用map-reduce进行操作
  94. if (\Yii::$app->redis->exists($rk)) {
  95. $refund_weight = \Yii::$app->redis->hget($rk, 'refund_weight');
  96. $not_refund_weight = \Yii::$app->redis->hget($rk, 'not_refund_weight');
  97. $ding_weight = \Yii::$app->redis->hget($rk, 'ding_weight');
  98. $not_ding_weight = \Yii::$app->redis->hget($rk, 'not_ding_weight');
  99. \Yii::$app->redis->del($rk);
  100. \Yii::$app->redis->hmset($rk,
  101. 'producer_name', $producer_name,
  102. 'info_name', $item['info_name'],
  103. 'grade_name', $item['grade_name'],
  104. 'refund_weight', bcadd($refund_weight, $item['refund_weight'], 2),
  105. 'not_refund_weight', bcadd($not_refund_weight, $item['not_refund_weight'], 2),
  106. 'ding_weight', bcadd($ding_weight, $item['ding_weight'], 2),
  107. 'not_ding_weight', bcadd($not_ding_weight, $item['not_ding_weight'], 2));
  108. } else {
  109. \Yii::$app->redis->hmset($rk,
  110. 'producer_name', $producer_name,
  111. 'info_name', $item['info_name'],
  112. 'grade_name', $item['grade_name'],
  113. 'refund_weight', $item['refund_weight'],
  114. 'not_refund_weight', $item['not_refund_weight'],
  115. 'ding_weight', $item['ding_weight'],
  116. 'not_ding_weight', $item['not_ding_weight']);
  117. }
  118.  
  119. # 输出到控制台,方便调试; 也可以使用日志模块,将此信息输出到日志文件或者网络日志
  120. $tip = Console::ansiFormat(implode('==', \Yii::$app->redis->hgetall($rk)), [Console::FG_GREEN]);
  121. Console::output($tip);
  122. unset($tip);
  123. }
  124. unset($item);
  125.  
  126. }
  127. }

在浏览器中访问此链接导出Excel,  http://erp.chinahanguang.com/wms-check/export

  1. public function actionExport()
  2. {
  3. # 此为导出统计基本数据,方便核对数据使用
  4. /*
  5. $excel_name = '销售库存统计';
  6. $headers = [
  7. 'producer_name' => '基地',
  8. 'info_name' => '品种',
  9. 'grade_name' => '等级',
  10. 'in_sheet_number' => '单号',
  11. 'refund_weight' => '库存退货',
  12. 'not_refund_weight' => '其他库存',
  13. 'ding_weight' => '销售出库定向采购',
  14. 'not_ding_weight' => '销售出库非定向采购',
  15. ];
  16.  
  17. $options = [
  18. 'creator'=>'中国汉广集团IT信息中心',
  19. 'last_modified_by'=>'中国汉广集团IT信息中心',
  20. 'title'=>$excel_name,
  21. 'subject'=>$excel_name,
  22. 'description'=>$excel_name,
  23. 'keywords'=>$excel_name,
  24. 'category'=>$excel_name,
  25. 'summary'=>[
  26. 'producer_name' => false,
  27. 'info_name' => false,
  28. 'grade_name' => false,
  29. 'in_sheet_number' => false,
  30. 'refund_weight' => false,
  31. 'not_refund_weight' =>false,
  32. 'ding_weight' => false,
  33. 'not_ding_weight' =>false,
  34. ]
  35. ];
  36.  
  37. $style_options = [
  38. 'h_align'=>[
  39. 'producer_name' => 'left',
  40. 'info_name' => 'left',
  41. 'grade_name' => 'left',
  42. 'in_sheet_number' => 'left',
  43. 'refund_weight' => 'right',
  44. 'not_refund_weight' =>'right',
  45. 'ding_weight' => 'right',
  46. 'not_ding_weight' =>'right',
  47. ]
  48. ];
  49. \core\components\MyExcelHelper::array2excel($data, $excel_name, $headers, $options, $style_options);
  50. die;*/
  51.  
  52. $ret_data = [];
  53.  
  54. # 从Redis获取统计汇总数据
  55. $rks = \Yii::$app->redis->keys('*-*-*');
  56. # 将Redis的键进行排序
  57. sort($rks, SORT_STRING);
  58. foreach ($rks as $rk){
  59. $rk_st = explode('-', $rk);
  60. if (count($rk_st) == 3){
  61. $ret_data[] = [
  62. 'producer_name'=>\Yii::$app->redis->hget($rk, 'producer_name'),
  63. 'info_name'=>$rk_st[1],
  64. 'grade_name'=>$rk_st[2],
  65. 'refund_weight'=>\Yii::$app->redis->hget($rk, 'refund_weight'),
  66. 'not_refund_weight'=>\Yii::$app->redis->hget($rk, 'not_refund_weight'),
  67. 'ding_weight'=>\Yii::$app->redis->hget($rk, 'ding_weight'),
  68. 'not_ding_weight'=>\Yii::$app->redis->hget($rk, 'not_ding_weight'),
  69. ];
  70. \Yii::$app->redis->del($rk);
  71. }
  72.  
  73. }
  74.  
  75. # 过滤所有重量都为0的数据
  76. $ret_data = array_filter($ret_data, [self::className(), '_filterZero']);
  77.  
  78. $excel_name = '销售库存统计';
  79. $headers = [
  80. 'producer_name' => '基地',
  81. 'info_name' => '品种',
  82. 'grade_name' => '等级',
  83. 'refund_weight' => '库存退货',
  84. 'not_refund_weight' => '其他库存',
  85. 'ding_weight' => '销售出库定向采购',
  86. 'not_ding_weight' => '销售出库非定向采购',
  87. ];
  88.  
  89. $options = [
  90. 'creator'=>'中国汉广集团IT信息中心',
  91. 'last_modified_by'=>'中国汉广集团IT信息中心',
  92. 'title'=>$excel_name,
  93. 'subject'=>$excel_name,
  94. 'description'=>$excel_name,
  95. 'keywords'=>$excel_name,
  96. 'category'=>$excel_name,
  97. 'summary'=>[
  98. 'producer_name' => false,
  99. 'info_name' => false,
  100. 'grade_name' => false,
  101. 'refund_weight' => false,
  102. 'not_refund_weight' =>false,
  103. 'ding_weight' => false,
  104. 'not_ding_weight' =>false,
  105. ]
  106. ];
  107.  
  108. $style_options = [
  109. 'h_align'=>[
  110. 'producer_name' => 'left',
  111. 'info_name' => 'left',
  112. 'grade_name' => 'left',
  113. 'refund_weight' => 'right',
  114. 'not_refund_weight' =>'right',
  115. 'ding_weight' => 'right',
  116. 'not_ding_weight' =>'right',
  117. ]
  118. ];
  119.  
  120. # 调用Excel工具类API,使用HTTP协议导出Excel
  121. \core\components\MyExcelHelper::array2excel($ret_data, $excel_name, $headers, $options, $style_options);
  122.  
  123. }
  124.  
  125. // 数据过滤器,过滤重量为0的数据
  126. public static function _filterZero($item){
  127. if (empty($item['refund_weight']) && empty($item['not_refund_weight']) && empty($item['ding_weight']) && empty($item['not_ding_weight'])){
  128. return false;
  129. }else{
  130. return true;
  131. }
  132. }

导出的汇总统计Excel表如下:

生成的格式化汇总统计表如下:

生成的汇总统计图如下:

生成统计数据并导出Excel的更多相关文章

  1. 01 UIPath抓取网页数据并导出Excel(非Table表单)

    上次转载了一篇<UIPath抓取网页数据并导出Excel>的文章,因为那个导出的是table标签中的数据,所以相对比较简单.现实的网页中,有许多不是通过table标签展示的,那又该如何处理 ...

  2. asp.net将内容导出到Excel,Table表格数据(html)导出EXCEL

    代码: /// <summary> /// HTML Table表格数据(html)导出EXCEL /// </summary> /// <param name=&quo ...

  3. POI导出大量数据的简单解决方案(附源码)-Java-POI导出大量数据,导出Excel文件,压缩ZIP(转载自iteye.com)

    说明:我的电脑 2.0CPU 2G内存 能够十秒钟导出 20W 条数据 ,12.8M的excel内容压缩后2.68M 我们知道在POI导出Excel时,数据量大了,很容易导致内存溢出.由于Excel ...

  4. piwik优化之定时任务生成统计数据

    piwik的ui界面,使用起来是无比的慢,让苏南大叔不得不对比wordpress的使用体验.当然了,如果你的服务器足够强大,这些都是小事儿.官方对此给出了一系列的优化建议,大家可以读一下:https: ...

  5. .net实现一个简单的通用查询数据、导出Excel的网页

    背景:临时提供一个简单的网页,供其他人浏览数据库(Oracel.MSSQL)的某些数据,并导出Excel.支持在配置文件中随时添加或修改sql. 实现:把sql语句等信息保存一个xml文件中,前端页面 ...

  6. OAF_文件系列10_实现OAF将数据资料导出Excel到本地JXL(案例)

    20150729 Created By BaoXinjian

  7. AX 利用windows粘贴板功能实现批量数据快速导出EXCEL

    static void test(Args _args) { int lineNum; int titleLines; SysExcelApplication excel; SysExcelWorkb ...

  8. 获取数据后导出Excel

    List<PortResourceInfo> list = getList()//获取数据源 //导出excle Response.Clear(); Response.ContentTyp ...

  9. winform中DataGridView的数据实现导出excel

    1,窗体设计 首先需要引入程序集:Microsoft.Office.Interop.Excel  (如果没有引用过的需要右键添加引用再搜索就行了) 实现的方法: /// <summary> ...

随机推荐

  1. php 10进制转62进制,可用于短网址生成

    <?php /** * 十进制数转换成62进制 * * @param integer $num * @return string */ function from10_to62($num) { ...

  2. day25 Python __setattr__

    #__getattr__只有在使用点调用属性且属性不存在的时候才会触发 class Foo: x=1 def __init__(self,y): self.y=y def __getattr__(se ...

  3. 数组复制的五种方式(遍历循环一一赋值、System.arraycopy、地址赋值、克隆clone()、Arrays.copyof())

    package com.Summer_0424.cn; import java.util.Arrays; import java.util.concurrent.CopyOnWriteArrayLis ...

  4. Linux内核入门到放弃-进程管理和调度-《深入Linux内核架构》笔记

    进程优先级 硬实时进程 软实时进程 普通进程 O(1)调度.完全公平调度器 抢占式多任务处理(preemptive multitasking):各个进程都分配到一定的时间段可以执行.时间段到期后,内核 ...

  5. AI tensorflow MNIST

    MNIST 数据 train-images-idx3-ubyte.gz:训练集图片 train-labels-idx1-ubyte.gz:训练集图片类别 t10k-images-idx3-ubyte. ...

  6. Mysql数据库表被锁定处理

    1.查进程,查找被锁表的那个进程的ID show processlist; command 为waitting的就是锁住的表,info为执行某条语句的信息,id为进程. 2.kill掉锁表的进程ID ...

  7. 使用keras的LSTM进行预测----实战练习

    代码 import numpy as np from keras.models import Sequential from keras.layers import Dense from keras. ...

  8. 关于GitHub的Hello Word

    最近GitHub一直是最火的配置库技术之一,各个技术大牛也都纷纷入驻GitHub 我每天都打交道的DITA-OT开源项目也宣布迁入GitHub. 那么GitHub到底有什么过人之处呢?给各位先扫个盲. ...

  9. 蓝牙speaker配对流程源码分析

    这篇文章简单分析一下 蓝牙音箱配对流程.现在的音箱基本都支持security simple pairing.所以这里的流程基本上就是ssp的代码流程. 源码参考的是 Android 6.0 上面的bl ...

  10. 腾讯首批 5000 人群,现在加入【FineUI总群】,极速体验!

    腾讯首批 5000 人群,稀缺资源,绝无仅有,快来体验! 加群链接:http://shang.qq.com/wpa/qunwpa?idkey=e81f012f9920c25a77c4fd8b0c767 ...