php分别判断3000多数据是否在15w数据(数据库)里面,区分开

首先要说一下我遇到这个问题的背景,为了可以针对性解决问题

背景:这是我参与的 “亿佳和” 项目,在生产入库模块上传的一个问题,上传的条码比较多在后来可能是百万级的,而目前到了15w级别。现在用户又上传条码了,大概3000个左右。成功即全部插入,失败提示重复,部分成功,并把另一部分重复条码返回给用户。

要解决的问题:上传3000多条码,如何区分哪些是已经存在于数据库中,哪些是未插入的呢?

我尝试过的办法:

  1. 组成一条select语句where把3000条码用or进行拼接查询。
  2. 循环3000条码,挨个用where条件访问数据库进行判断。
  3. 然后是学长的一个思路,把要插入的数据直接全部插入,之后再把这3000条码查出来,和原来要插入的全部数据做差集,这样就可以得到插入失败的条码,并返回给用户了。
  4. 把数据库的15w条码查出来存进数组,循环3000条码,分别用in_array函数判断是否在15w条码里面,在的话就放入重复条码列表显示给用户。否则放入另外一个随后插入。
  5. 这算是上一个方法的改版,就是把15W数据键值对进行翻转,大家都知道 data[$key]=$value。
  6. 任务化,像微信公众号一样推送消息,只需要发送请求,任务会在后台慢慢执行至执行完成。

这些方法的结果:

  1. 这种方法没什么毛病,但是or拼接出来数据库返回即成功或失败,并不会区分哪些or后面的条件是可行的。
  2. 思路最简单的一个办法,可是数据库访问次数太多,作为一个初学者我能这么早意识到这个问题看来我还是蛮鸡贼的哈哈。
  3. 根据我自己测试的,insert语句values一次多个数据,如果有一个失败那么整条语句就over。这就导致如果3000数据一次插入只要一个条码重复一个都差不进去,也无法再做差集找到重复条码。但据学长说可行,这个我随后要去讨论一番。(追加:确实可行,详情请见http://blog.csdn.net/qq_20797031/article/details/52756482)。这个博客的第一个办法 用关键字ignore,然后thinkphp的curd方法没法插入这个关键字,我也就没用。慢慢尝试吧。
  4. 这是我初期使用的一个思路,避免了多次访问数据库而且可以很好的区分哪些条码重复,哪些可以插入,思路也不难。但是用了一段时间之后问题就来了,就像本次的背景,问题就是这个时候发生的。首先说一下in_array这个函数,这个函数的时间复杂度是O(n),也就是说目前数据库15W数据,循环要插入的3000,每次都要拿其中一个去15W数据里挨个找,找一个下来就需要比较15w次,而比较3000个的结果粗略一算就需要比较4.5亿次,然后造成上传之后用户这边一脸懵逼,到底是上传成功还是不成功?这个时候如果用f12打开开发者工具的话从控制台可以看出来报错(500),虽然那个错我没有百度翻译(我是个学渣英语不好),不过猜出来应该是超时,我今天大二上学期刚学完数据结构,算法题随之就来,这就好玩了!
    /*
        $smallall[]    //数据库现有条码存入的数组
        $wait[][]        //此次上传的要插入的条码
        $diff[]            //重复的条码列表
        $ndiff[]        //不重复的条码列表,可插入
     */
        foreach ($wait as $key => $value) {
            if(!in_array($value['smallCode'],$smallall)){    //smallCode是条码字段
                $ndiff[]=$value;
            }else{
                $diff[]=$value;
            }
        }
  5. 这个取值过程时间复杂度是O(1)级别的,键被设置过以后可以通过isset这个函数进行直接判断。循环3000条码,用isset函数判断当前条码是否已经被设置为键,如果返回true就说明翻转后的15W条码里存在该键(条码),也就是重复的条码。否则就是可插入的。把数据库15W数据翻转,至少需要15W的运算量,但是循环3000条码用isset进行直接取值判断,只需要3000次就可以,15W+3000还不到16W,比原来的4.5亿次快很多有木有。哈哈,我瞎说的,确实快了很多不过运算量是我自己猜测的。
    /*
        $smallall[]    //数据库现有条码存入的数组
        $wait[][]        //此次上传的要插入的条码
        $diff[]            //重复的条码列表
        $ndiff[]        //不重复的条码列表,可插入
     */
        $res=array_flip($smallall);    //键值对翻转
        foreach ($wait as $key => $value) {
            if(!isset($res[$value['smallCode']])){    //smallCode是条码字段
                $ndiff[]=$value;
            }else{
                $diff[]=$value;
            }
        }
  6. 这个我不会。还需要学习。。。

目前问题解决了,一个代码质量的问题。花了一天时间解决的。虽然比较慢但是这样的进步收获是巨大的。

加油!

2月5日:年前又出问题了,由于这个项目后期会有几百万数据,现在到了几十万当然超过15w了,然后select直接报错。查阅很多资料才发现是php.ini限制调用内存大小不能超过128m,我去修改配置文件搜索不到那一串配置内存大小的代码,好的是又发现 ini_set("memory_limit","-1"); 这句代码填进程序中,可以无限制内存大小。我想了想几百万数据我这内存应该够使吧。。。不行得再想办法,持续更新ing……

原文地址:https://www.cnblogs.com/marchsoft-wangqi/p/6295518.html