├── LICENSE ├── README.MD ├── README_EN.MD ├── conf ├── common.ini └── local.ini ├── favicon.png ├── gensql.php ├── index.php ├── index_en.php └── sqlparse.php /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 wintercoder 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # 在线MYSQL测试数据生成工具 2 | 3 | 根据SQL表结构生成Mysql测试数据,根据字段名猜测类型,生成默认值 4 | 5 | [English Readme](./README_EN.MD) 6 | 7 | ## 生成规则 8 | 9 | 普通规则见[官网](http://www.datamaker.online/),此处讲个规则的组合使用 10 | 11 | **自增日期(组自增) + 常量列表(组模式)** 12 | 13 | 使用场景: 一个肯德基总店下有一堆子店,希望生成[每个子店每天]的数据,一行记录包含一个店一天的汇总数据 14 | 15 | 使用方法: 在子店ID 字段上选择 `常量列表(组模式)` 用逗号分隔写上各店的ID,日期选择 `自增日期(组自增)` 即可 16 | 17 | **Unique Key** 18 | 19 | 有 `UNIQUE KEY (user_id,platform) ` 的情况 20 | 21 | 方案一: 22 | 这种platform只有1和2的可以考虑使用 给 `platform` 选 `常量列表(组模式)` 填 `1,2`,然后user_id选自增之类保证不重就OK。 23 | 24 | 方案二: 25 | 选择重复Key选项,测试数据一般跳过就好,注意选 `REPLACE INTO` 可能出现 `AUTO_INCREMENT` + 2 的情况 26 | 27 | ## 个性化 28 | 你可以通过部署到自己的服务器上,对一些字段进行默认值配置,非常**适合公司内网** 29 | 部署也方便,代码里无任何框架依赖,能跑PHP就行 30 | 31 | ### 配置 32 | `/conf/common.ini` 为通用配置 33 | `/conf/local.ini` 为个性化配置,适合不宜暴露的默认值,如测试账号ID等,该文件已加入 `.gitignore` 里,如果发现不生效则跑下命令 34 | `git update-index --assume-unchanged conf/local.ini` 35 | 36 | 配置样例: 37 | 38 | ``` 39 | [0] 40 | key = index_day 41 | method = INCR_DAY 42 | value = 20180301 43 | way = match 44 | [1] 45 | key = url 46 | method = RAND_PIC_URL 47 | value = 300,400 48 | way = search 49 | ``` 50 | `key` : 字段名 51 | `method` : 生成规则,如下表 52 | `value` : 给前端的输入框值 53 | `way` : 输入的key跟配置文件的key匹配规则,目前支持 `match` 完全相等、`search` 字符串包含,默认为`match` 54 | 55 | 56 | | 规则英文 | 中文 | 参数 | 57 | |----------|------|------| 58 | | INCR_INT | 自增int | from : 从from开始自增,步长1 59 | | RAND_INT | 随机int | from,to : 生成[from,to]闭区间整数 60 | | RAND_FLOAT | 随机浮点 | from,to,round : 生成[from,to]闭区间浮点,保留round位小数,这三个参数都是整数 61 | | INCR_DAY | 日期自增 | from : 从from开始自增,步长1天,格式20180304 62 | | INCR\_DAY\_GROUPLY | 日期自增(组模式) | 同上,但每组值里的这个日期不变,下条SQL才变 63 | | RAND_TIMESTAMP | 随机时间戳 | from,to : 生成[from,to]这些天里的秒级时间戳,如 20180304,20180305 64 | | RAND\_TIMESTAMP\_MYSQL | 随机时间(Mysql格式) | from,to : 生成[from,to]这些天里的秒级时间,格式:2018-04-07 18:08:34 65 | | IGNORE | 不生成该列 | 66 | | CONST_STR | 常量 | 常量值 67 | | CONST\_STR\_LIST | 常量列表(组模式) | 输入 a,b,每条SQL都输出 a,b,此时组数固定 68 | | RAND_STR | 随机串 | length : 英文串长度 69 | | RAND\_STR\_LIST | 随机串(指定列表) | a,b,c : 该字段只出现a,b,c之一,可重复出现 70 | | INCR\_STR\_SUFFIX | 前缀+自增数字 | 输入 小王,输出 小王1,小王2,用于人名等 71 | | RAND\_PIC\_URL | 图片地址 | width,height : 图片的宽高 72 | 73 | ## 添加新生成规则流程 74 | 75 | 假设叫 rand_what 76 | 77 | 1. 前端 `index.php` 78 | 79 | `getDefaultValueByMethod()` 增加该规则的默认值 80 | `getHoverContent()` 增加该规则的hover文案 81 | `fillTabelWithData()` 增加对应代码 82 | 83 | 2. 后端 `gensql.php` 84 | 85 | `ValueGenerator` 类里新增 `randWhat()` 函数就行, 即下划线转驼峰 86 | 87 | 88 | 89 | ## License 90 | 91 | **The MIT License** 92 | -------------------------------------------------------------------------------- /README_EN.MD: -------------------------------------------------------------------------------- 1 | # Online Sql Test Data Generator 2 | 3 | 4 | Generate sql test data by Mysql table structure 5 | 6 | 7 | 8 | Input: 9 | 10 | ``` 11 | CREATE TABLE `student` ( 12 | `id` int(11) NOT NULL AUTO_INCREMENT, 13 | `user_id` int(11) NOT NULL, 14 | `name` varchar(20) NOT NULL , 15 | PRIMARY KEY (`id`), 16 | ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 ; 17 | ``` 18 | 19 | Output: 20 | 21 | ``` 22 | INSERT INTO `student (user_id,name) VALUES ('10001','John1'),('10002','John2'); 23 | 24 | INSERT INTO `student (user_id,name) VALUES ('10003','John3'),('10004','John4'); 25 | 26 | ``` 27 | 28 | 29 | 30 | **[Try It Now ](http://datamaker.online/index_en.php)** 31 | 32 | 33 | 34 | ## Individuation 35 | You can deploy this project on your server to set default value for frequently used fields, good for local area network like company 36 | 37 | Easy to deploy: It only require PHP (without any framework) and webserver 38 | 39 | ### Config 40 | `/conf/common.ini` is common config 41 | `/conf/local.ini` is personal config, good for same private value like test account. For safe, you should run 42 | `git update-index --assume-unchanged conf/local.ini` 43 | to avoid traced to git 44 | 45 | Sample: 46 | 47 | ``` 48 | [0] 49 | key = index_day 50 | method = INCR_DAY 51 | value = 20180301 52 | way = match 53 | [1] 54 | key = url 55 | method = RAND_PIC_LIST 56 | value = 300,400 57 | way = search 58 | ``` 59 | `key` : field name 60 | `method` : generate rule in the following table 61 | `value` : default value for front-end 62 | `way` : `match`:match this config if the input key is equals to key ; `search` match this config if the input key contains this key; the defalut value is `match` 63 | 64 | 65 | | Rule Method | Description | Parameter | 66 | |----------|------|------| 67 | | INCR_INT | auto increment integer | `from` : increase from `from` 68 | | RAND_INT | random int | `from,to` : random in interval `[from,to]` 69 | | RAND_FLOAT | random float | `from,to,round` : Random in interval `[from,to]` , retains `round` digits after the decimal point 70 | | INCR_DAY | increment day | `from` : increase from `from` with format 20180304 71 | | INCR\_DAY\_GROUPLY | increment day (grouply same) | same as above,but the value is same in the same group 72 | | RAND_TIMESTAMP | random timestamp | `from,to` : timestamp in day `[from,to]`, input format is 20180304,20180305 73 | | RAND\_TIMESTAMP\_MYSQL | random time (Mysql) | `from,to` : same as above,but the result format is 2018-04-07 18:08:34 74 | | IGNORE | ignore this column | 75 | | CONST_STR | const value | 76 | | CONST\_STR\_LIST | const (group list) | `a,b`: every group in sql will output `a,b` 77 | | RAND_STR | random string | `length` : the length of string with alphabet 78 | | RAND\_STR\_LIST | random string in list | `a,b,c` : random pick one in `a,b,c` 79 | | INCR\_STR\_SUFFIX | Prefix + Number (Incr) | `test` : it will ouput test1,test2,test3 ... 80 | | RAND\_PIC\_URL | picture url | `width,height` : the width and height of url 81 | 82 | ## How to add new generator 83 | 84 | When you add a method call `rand_what` 85 | 86 | 1. Frontend: `index.php` 87 | 88 | `getDefaultValueByMethod()` add the default value 89 | `getHoverContent()` add the hover content 90 | `fillTabelWithData()` modify it 91 | 92 | 2. Backend`gensql.php` 93 | 94 | Add the function call `randWhat()` In class `ValueGenerator` ( Camel-Case of `rand_what` ) 95 | 96 | 97 | 98 | ## License 99 | 100 | **The MIT License** 101 | -------------------------------------------------------------------------------- /conf/common.ini: -------------------------------------------------------------------------------- 1 | [0] 2 | key = avatar 3 | method = RAND_PIC_URL 4 | value = 300,400 5 | way = search 6 | [1] 7 | key = photo 8 | method = RAND_PIC_URL 9 | value = 300,400 10 | way = search 11 | [2] 12 | key = picture 13 | method = RAND_PIC_URL 14 | value = 300,400 15 | way = search 16 | [3] 17 | key = url 18 | method = RAND_PIC_URL 19 | value = 300,400 20 | way = search 21 | [4] 22 | key = time 23 | method = RAND_TIMESTAMP 24 | value = 20180407,20180408 25 | way = search 26 | [5] 27 | key = phone 28 | method = RAND_INT 29 | value = 15602000001,15602009999 30 | way = search 31 | [6] 32 | key = insert_day 33 | method = INCR_DAY_GROUPLY 34 | value = 20180301 35 | way = match 36 | [7] 37 | key = index_day 38 | method = INCR_DAY 39 | value = 20180301 40 | way = match 41 | [8] 42 | key = status 43 | method = RAND_STR_LIST 44 | value = 0,1 45 | way = search 46 | [9] 47 | key = data_day 48 | method = INCR_DAY_GROUPLY 49 | value = 20180301 50 | way = match 51 | [10] 52 | key = is_ 53 | method = RAND_INT 54 | value = 0,1 55 | way = search 56 | [11] 57 | key = flag 58 | method = RAND_INT 59 | value = 0,1 60 | way = search 61 | [12] 62 | key = score 63 | method = RAND_INT 64 | value = 0,100 65 | way = search 66 | -------------------------------------------------------------------------------- /conf/local.ini: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wintercoder/datamaker/c42e12258dc17c75e55dc654b67e29712309e79b/conf/local.ini -------------------------------------------------------------------------------- /favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wintercoder/datamaker/c42e12258dc17c75e55dc654b67e29712309e79b/favicon.png -------------------------------------------------------------------------------- /gensql.php: -------------------------------------------------------------------------------- 1 | randTimestamp($input,$groupSize); 148 | foreach ($ret as &$item){ 149 | $item = date("Y-m-d H:i:s",$item); 150 | } 151 | return $ret; 152 | } 153 | 154 | /** 155 | * 常量 156 | * @param $input 157 | * value 常量值 158 | * @param $groupSize int 每组多少条合并成一个数组,也是该次函数返回的数组大小 159 | * @return array 160 | */ 161 | public function constStr($input,$groupSize){ 162 | return explode('$#$',str_repeat($input.'$#$',$groupSize)); 163 | } 164 | 165 | 166 | /** 167 | * 常量列表 168 | * @param $input 169 | * value 常量值 170 | * @param $groupSize int 每组多少条合并成一个数组,也是该次函数返回的数组大小 171 | * @return array 172 | */ 173 | public function constStrList($input,$groupSize){ 174 | $input = !empty($input) ? $input : '百度$#$阿里$#$腾讯'; 175 | return explode('$#$',$input); 176 | } 177 | 178 | /** 179 | * 随机字符串(字符集:大小写字母) 180 | * @param $input array 181 | * length 长度 182 | * @param $groupSize int 每组多少条合并成一个数组,也是该次函数返回的数组大小 183 | * @return array 184 | */ 185 | public function randStr($length,$groupSize) 186 | { 187 | $ret = []; 188 | $count = 0; 189 | while ($count++ < $groupSize) { 190 | $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; 191 | $ranStr = ''; 192 | for ($i = 0; $i < $length; $i++) { 193 | $ranStr .= $characters[rand(0, strlen($characters) - 1)]; 194 | } 195 | $ret []= $ranStr; 196 | } 197 | return $ret; 198 | } 199 | /** 200 | * 在字符串列表里 随机,全局可重复 201 | * @param $input array 202 | * length 长度 203 | * @param $groupSize int 每组多少条合并成一个数组,也是该次函数返回的数组大小 204 | * @return array 205 | */ 206 | public function randStrList($input,$groupSize) 207 | { 208 | $input = !empty($input) ? $input : 'ofo,mobike'; 209 | $value = explode(',',$input); 210 | $ret = []; 211 | $count = 0; 212 | while ($count++ < $groupSize) { 213 | $ranStr = $value[rand(0, count($value) - 1)]; 214 | $ret []= $ranStr; 215 | } 216 | return $ret; 217 | } 218 | 219 | 220 | /** 221 | * 自增中文,根据 前缀+1 这种,如程序员1,程序员2 222 | * @param $input array 223 | * pre_str 前缀 224 | * @param $groupSize int 每组多少条合并成一个数组,也是该次函数返回的数组大小 225 | * @return array 226 | */ 227 | public function incrStrPrefix($input,$groupSize,$sqlCounter) 228 | { 229 | $pre = !empty($input) ? $input : '测试店'; 230 | $inputArr['from'] = 1; 231 | $intArr = $this->incrInt($inputArr,$groupSize,$sqlCounter); 232 | $ret = []; 233 | foreach ($intArr as $intVal){ 234 | $str = $pre . $intVal; 235 | $ret []= $str; 236 | } 237 | return $ret; 238 | } 239 | 240 | /** 241 | * 随机图片,目前是 http://lorempixel.com/ 242 | * 也可以考虑用百度的 http://image.baidu.com/channel/listjson?pn=0&rn=30&tag1=%E7%BE%8E%E5%A5%B3&tag2=%E5%85%A8%E9%83%A8&ie=utf8 243 | * @param $input 244 | * @param $groupSize int 每组多少条合并成一个数组,也是该次函数返回的数组大小 245 | * @return array 246 | */ 247 | public function randPicUrl($input,$groupSize){ 248 | $exp = explode(',',$input); 249 | $width = !empty($exp[0]) ? intval($exp[0]) : 300; 250 | $height = !empty($exp[1]) ? intval($exp[1]) : 300; 251 | $url = "http://lorempixel.com/{$width}/{$height}/"; 252 | 253 | return explode(',',str_repeat($url.',',$groupSize)); 254 | } 255 | } 256 | 257 | class WorkHandler{ 258 | 259 | // /** 260 | // * 同步的数据导出,格式为.sql 261 | // */ 262 | // public function syncExportStr2File($fileName, $content){ 263 | // header_remove(); 264 | // ini_set('memory_limit', '128M'); 265 | // set_time_limit(1800); 266 | // header("Content-type:text/html;charset=utf-8"); 267 | // // header("Content-Transfer-Encoding: binary"); 268 | // // header("Content-Type: application/force-download;"); 269 | // header("Content-type: application/octet-stream"); 270 | // header("Cache-control: no-cache"); 271 | // header("Content-Transfer-Encoding: binary"); 272 | // header("Content-Disposition: attachment; filename=$fileName.sql"); 273 | // header("Expires: 0"); 274 | // header("Cache-control: private"); 275 | // header("Pragma: no-cache"); 276 | // header('Content-Length: ' . strlen($content)); 277 | 278 | // // $content = iconv('UTF-8', 'GBK//IGNORE', $content); 279 | // echo $content.PHP_EOL; 280 | // exit(); 281 | // } 282 | 283 | public function checkParams($input){ 284 | $input = json_decode($input,true); //json转数组 285 | if(empty($input) || empty($input['list'])){ 286 | echo "参数不是JSON格式";exit(); 287 | } 288 | $input['count'] = intval($input['count']); 289 | $input['group_size'] = intval($input['group_size']); 290 | if( $input['count'] <= 0 || $input['group_size'] <= 0 291 | || $input['count'] >= 5000 || $input['group_size'] >= 5000) { 292 | echo "条数、组数只能是 1 到 5000 以内";exit(); 293 | } 294 | } 295 | 296 | public function execute($input){ 297 | $this->checkParams($input); 298 | 299 | $input = json_decode($input,true); //json转数组 300 | $inputList = $input['list']; 301 | 302 | //对于自增ID等忽略的类型,删除它并重排下标,不能用 array_splice ,删除多个时会乱 303 | foreach ($inputList as $key => $item){ 304 | if($item['method'] == 'ignore'){ 305 | unset($inputList[$key]); 306 | } 307 | } 308 | $inputList = array_values($inputList); //从0开始 重建下标 309 | 310 | $genCount = !empty($input['count']) ? $input['count'] : 5 ; 311 | $tableName = !empty($input['table_name']) ? $input['table_name'] : 'test'; 312 | $groupSize = $input['group_size']; //每个SQL有多少value组 313 | $insertWay = !empty($input['insert_way']) ? $input['insert_way'] : 'INSERT INTO '; 314 | 315 | $keyArr = array_column($inputList,'key'); 316 | 317 | $generator = new ValueGenerator(); 318 | 319 | $sql = ''; 320 | //insert的SQL条数 321 | for($genI = 1; $genI <= $genCount; $genI++) { 322 | 323 | $genResult = []; //生成结果数组: key => 生成方法+下标 防止同样的方法覆盖数据,value => 按该方法生成的数据数组 324 | foreach ($inputList as $itemIndex => $item){ 325 | $genService = $this->camelize($item['method']); //方法名转驼峰 326 | $genResult [ $item['method'].$itemIndex ] = $generator->$genService($item['value'],$groupSize,$genI ); 327 | } 328 | $valueStr = ''; 329 | 330 | //每个SQ有多少value组 331 | for($cnt = 0; $cnt < $groupSize; $cnt++){ 332 | 333 | $valueStr .= '('; 334 | //取每个字段的值,单引号引起来 335 | foreach ($genResult as $result) { 336 | $valueStr .= "'{$result[$cnt]}',"; 337 | } 338 | $valueStr = rtrim($valueStr,','); 339 | $valueStr .= '),'; 340 | } 341 | $valueStr = rtrim($valueStr,','); 342 | $valueStr .= ';'; 343 | $sql .= $insertWay . "{$tableName} (" . implode(",",$keyArr) .") VALUES $valueStr"; 344 | $sql .= "\n"; 345 | $sql .= "\n"; 346 | } 347 | //一直没成功,还怀疑是jq的post方法不是超链导致,最终用前端去做了 348 | // $this->syncExportStr2File('datamake_'.date('YmdHis', time()).'sql' ,$sql); 349 | echo $sql; 350 | } 351 | 352 | /** 353 | * 下划线转驼峰 354 | * @param $str 355 | * @param string $separator 356 | * @return string 357 | */ 358 | private function camelize($str,$separator='_') 359 | { 360 | $str = $separator. str_replace($separator, " ", strtolower($str)); 361 | return ltrim(str_replace(" ", "", ucwords($str)), $separator ); 362 | } 363 | } 364 | 365 | 366 | date_default_timezone_set("Asia/Shanghai"); 367 | 368 | $postData = file_get_contents('php://input'); //提交的是JSON,不能直接$post获取 369 | if(empty($postData)){ 370 | echo "我是空白"; 371 | exit(); 372 | } 373 | //echo ($postData);exit(); 374 | 375 | 376 | $handler = new WorkHandler(); 377 | $handler->execute($postData); 378 | exit(); 379 | 380 | /* 381 | $parser = new CreateSqlParser(); 382 | $ret = $parser->execute($sql); 383 | if(0 == ($ret['error'])){ 384 | 385 | $input = json_encode($ret['data']); 386 | $handler = new WorkHandler(); 387 | $handler->execute($input); 388 | } else{ 389 | echo json_encode($ret);exit(); 390 | 391 | } 392 | 393 | */ -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | SQL测试数据生成 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
32 |
33 | 84 | 85 | 86 |
87 |
88 | 89 | 90 | 从 show create table tablename 获得 91 | 96 |
97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 |
106 | 107 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 191 | 192 | 193 | 194 | 484 | 485 |



486 | 487 | 490 | 491 |
492 |
493 |
494 | 495 |
496 |
497 | 498 |
499 |
500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | -------------------------------------------------------------------------------- /index_en.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | SQL Data Generator 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 |
30 |
31 | 83 | 84 | 85 |
86 |
87 | 88 | 89 | show create table tablename 90 | 95 |
96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 |
105 | 106 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 186 | 187 | 188 | 189 | 477 | 478 |



479 | 480 | 483 | 484 |
485 |
486 |
487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | -------------------------------------------------------------------------------- /sqlparse.php: -------------------------------------------------------------------------------- 1 | $matches[0][$cnt], 76 | 'key' => $matches[2][$cnt], 77 | 'type' => $matches[3][$cnt], 78 | 'others' => $matches[4][$cnt], 79 | ]; 80 | $ret []= $item; 81 | } 82 | return $ret; 83 | } 84 | 85 | 86 | /** 87 | * 解析字段名有 ` 包含的SQL,常见软件:Navicat 88 | * 正则匹配规则: 任意字符若干(空格或KEY) + `列名`+ 空格 + 非空字符串(类型) + 空格 + 额外信息 + 逗号或者),其中右括号结尾是表里没有任何索引的情况,支持跨行匹配 89 | * 如果需要改动,需要注意: COMMENT 换行、 表无索引时最后个字段的情况,目前以 ` 来区分字段而非空格 90 | * @param $sql 91 | CREATE TABLE `im_feed` ( 92 | `id` int(11) NOT NULL AUTO_INCREMENT, 93 | `parent_id` int(11) NOT NULL DEFAULT '0', 94 | `user_id` bigint(11) NOT NULL DEFAULT '0' COMMENT '学号或者老师工号', 95 | `content` varchar(255) COLLATE utf8_bin NOT NULL DEFAULT '', 96 | `is_deleted` tinyint(4) NOT NULL DEFAULT '0', 97 | `photos` varchar(255) COLLATE utf8_bin NOT NULL DEFAULT '', 98 | `create_time` int(11) NOT NULL DEFAULT '0', 99 | PRIMARY KEY (`id`) 100 | ) ENGINE=InnoDB AUTO_INCREMENT=28 DEFAULT CHARSET=utf8 COLLATE=utf8_bin; 101 | * @return array 每个元素包含如下字段 102 | * origin: 原SQL 103 | * key: 字段名 104 | * type: 类型,包含可选的长度 int(11) 、text 105 | * others: 其他,NOT NULL AUTO_INCREMENT COMMENT '我是注释' 这种 106 | */ 107 | private function parseWithBackQuote($sql){ 108 | //提前删除 INDEX 相关 INDEX cust_id (cust_id) USING BTREE, 109 | $sql = preg_replace("# *INDEX.+#","",$sql); 110 | 111 | $pattern = "#(.*)`(.+)` ([^\s]+) ([\s\S]+?)[,)]#im"; 112 | preg_match_all($pattern, $sql, $matches); 113 | // echo json_encode($matches);exit(); 114 | $ret = []; 115 | for ($cnt = 0; $cnt < count($matches[0]); $cnt++) { 116 | if (false !== stripos($matches[1][$cnt], 'KEY')) { //索引 排除 117 | continue; 118 | } 119 | 120 | $item = [ 121 | 'origin' => $matches[0][$cnt], 122 | 'key' => $matches[2][$cnt], 123 | 'type' => $matches[3][$cnt], 124 | 'others' => $matches[4][$cnt], 125 | ]; 126 | $ret []= $item; 127 | } 128 | return $ret; 129 | } 130 | 131 | public function execute($input) 132 | { 133 | $sql = $input; 134 | $ret = []; 135 | 136 | //解析表名,兼容以下两种,返回的表名带不带 `都可以成功插入 137 | // CREATE TABLE `test` ( 138 | // CREATE TABLE t_supplier_product 139 | // ( 140 | $sql = str_replace("IF NOT EXISTS","",$sql); 141 | 142 | $pattern = "#CREATE TABLE (.+?)[\s]#i"; 143 | preg_match($pattern, $sql, $matches); 144 | if (empty($matches)) { 145 | return $this->getApiReturn(self::errorParseError, '不是建表SQL,未包含 CREATE TABLE', []); 146 | } 147 | $ret['table_name'] = $matches[1]; 148 | 149 | //解析字段,推荐个在线正则网站 https://regexr.com/ 150 | $matchList = $this->parseWithBackQuote($sql); //带`的解析失败则用不带`的解析,大部分SQL是带`的 151 | if (empty($matchList)) { 152 | $matchList = $this->parseWithoutBackQuote($sql); 153 | } 154 | $ret['list'] = []; 155 | if (empty($matchList)) { 156 | return $this->getApiReturn(self::errorParseError, '未查找到SQL字段', []); 157 | } 158 | 159 | // echo json_encode($matchList); exit(); 160 | 161 | // 解析后拿到的每个item: 162 | // "origin":" `baoguang_pv` int(11) NOT NULL DEFAULT '0' COMMENT '昨日曝光pv',", 163 | // "key":"baoguang_pv", ======= 字段名 164 | // "type":"bigint(20)", ======= 类型,包含可选的长度 int(11) 、text 165 | // "others":"NOT NULL DEFAULT '0' COMMENT '昨日曝光pv'" ======= 其他,NOT NULL AUTO_INCREMENT COMMENT '我是注释' 这种 166 | foreach ($matchList as $item){ 167 | $size = 0; 168 | $type = $item['type']; 169 | $sizeArr = explode('(',$item['type']); 170 | 171 | //如果有()说明是有数字的那种 172 | if(!empty($sizeArr) && count($sizeArr) >= 2 ) { 173 | $type = $sizeArr[0]; 174 | $size = explode( ")" ,$sizeArr[1] )[0] ; 175 | } 176 | $entry = $this->genDefaultAttribute($item['key'], $type , $size, $item['others']); 177 | $entry['key'] = $item['key']; 178 | $ret['list'] [] = $entry; 179 | } 180 | 181 | $ret['group_size'] = 5; //组大小 182 | $ret['count'] = 3; //多少条SQL 183 | 184 | return $this->getApiReturn(0, '', $ret); 185 | } 186 | 187 | /** 188 | * @param $key 189 | * @param $type string SQL的字段类型 varchar,int 190 | * @param $size string SQL的字段类型后跟随的大小 如 varchar(10) 中的10 191 | * @param $others string varchar(10) 后面的一串其他完整内容,包含 自增、非空、默认值等 192 | * @return array item 必备包含 'key' 字段名 'method' 生成规则 'value' 默认值 193 | */ 194 | private function genDefaultAttribute($key, $type, $size, $others) 195 | { 196 | $type = strtolower(trim($type)); 197 | $incrStrPre = ['Boss', 'Player', 'Test', 'PM', 'Programmer', 'Worker', 'Actor', 'SB']; 198 | 199 | switch ($type) { 200 | case 'varchar': 201 | $item = [ 202 | 'desc' => '前缀+自增', 203 | 'method' => self::INCR_STR_PREFIX, 204 | 'value' => $incrStrPre[rand(0, count($incrStrPre) - 1)], 205 | ]; 206 | break; 207 | case 'int': 208 | case 'mediumint': 209 | case 'integer': 210 | $item = [ 211 | 'desc' => '随机整数', 212 | 'method' => self::RAND_INT, 213 | 'value' => '100,500', 214 | ]; 215 | break; 216 | case 'bigint': 217 | $item = [ 218 | 'desc' => '随机整数', 219 | 'method' => self::RAND_INT, 220 | 'value' => "1000000,99999999", 221 | ]; 222 | break; 223 | case 'tinyint': 224 | $item = [ 225 | 'desc' => '随机整数', 226 | 'method' => self::RAND_INT, 227 | 'value' => "0,{$size}", //tinyint(4) 一般是0-4的枚举值 228 | ]; 229 | break; 230 | case 'float': 231 | case 'double': 232 | $item = [ 233 | 'desc' => '随机浮点', 234 | 'method' => self::RAND_FLOAT, 235 | 'value' => '1,10,5', 236 | ]; 237 | break; 238 | case 'date': //这个待定 239 | case 'datetime': 240 | case 'timestamp': 241 | $item = [ 242 | 'method' => self::RAND_TIMESTAMP_MYSQL, 243 | 'value' => '20180407,20180408', 244 | ]; 245 | break; 246 | case 'text': 247 | default: 248 | $item = [ 249 | 'method' => self::CONST_STR, 250 | 'value' => '1', 251 | ]; 252 | break; 253 | } 254 | 255 | //自增ID 256 | $autoInc = stripos($others, "AUTO_INCREMENT"); 257 | if ($autoInc !== false) { 258 | $item = [ 259 | 'method' => self::IGNORE, 260 | 'desc' => '自增ID,忽略', 261 | ]; 262 | } 263 | 264 | //注释 265 | $expComment = explode("COMMENT ", $others); 266 | if (!empty($expComment) && count($expComment) >= 2 ) { 267 | $item['comment'] = trim($expComment[1], "',"); 268 | } 269 | 270 | //通用配置 271 | $commonItem = $this->parseFileForAttribute('conf/common.ini',$key); 272 | if(!empty($commonItem)){ 273 | $item = $commonItem; 274 | } 275 | //个性化配置 276 | //针对你、你公司 常用的字段设置默认值,存放不可告人的数据秘密,配置文件在.gitignore里 277 | $localItem = $this->parseFileForAttribute('conf/local.ini',$key); 278 | if(!empty($localItem)){ 279 | $item = $localItem; 280 | } 281 | return $item; 282 | } 283 | 284 | /** 285 | * 解析ini文件拿到自定义默认值,根据字段名猜测用户想要的是哪个类型,配置文件样例如下,目前只支持 模糊查找 和 精确匹配 286 | [0] 287 | key = avatar 288 | method = RAND_PIC_URL 289 | value = 300,400 290 | way = search 跟key的匹配方式,search为模糊搜索,输入key包含 avatar 就走这个匹配 291 | 292 | [1] 293 | key = index_day 294 | method = INCR_DAY 295 | value = 20180301 296 | way = match match为精确匹配,输入key 等于 index_day 就走这个匹配 297 | * 298 | * 299 | * @param $fileName string 配置文件路径 300 | * @param $key string 字段名 301 | * @return array 302 | */ 303 | private function parseFileForAttribute($fileName,$key) 304 | { 305 | if(!file_exists($fileName)){ 306 | return []; 307 | } 308 | $item = []; 309 | $matchArray = parse_ini_file($fileName,true); 310 | 311 | foreach ($matchArray as $match) { 312 | if(empty($match['way'])){ //默认key规则为相等 313 | $match['way'] = 'match'; 314 | } 315 | if($match['way'] == 'search' && false !== stripos($key, $match['key'])){ 316 | $item = $match; 317 | $item['method'] = strtolower($item['method']); 318 | }else if ($match['way'] == 'match' && $key == $match['key']) { 319 | $item = $match; 320 | $item['method'] = strtolower($item['method']); 321 | } 322 | } 323 | unset($item['way']); 324 | return $item; 325 | } 326 | 327 | 328 | } 329 | date_default_timezone_set("Asia/Shanghai"); 330 | 331 | 332 | $defaultSql = " 333 | CREATE TABLE `im_feed` ( 334 | `id` int(11) NOT NULL AUTO_INCREMENT, 335 | `parent_id` int(11) NOT NULL DEFAULT '0', 336 | `user_id` bigint(11) NOT NULL DEFAULT '0' COMMENT '学号或者老师工号', 337 | `content` varchar(255) COLLATE utf8_bin NOT NULL DEFAULT '', 338 | `is_deleted` tinyint(4) NOT NULL DEFAULT '0', 339 | `photos` varchar(255) COLLATE utf8_bin NOT NULL DEFAULT '', 340 | `create_time` int(11) NOT NULL DEFAULT '0', 341 | PRIMARY KEY (`id`) 342 | ) ENGINE=InnoDB AUTO_INCREMENT=28 DEFAULT CHARSET=utf8 COLLATE=utf8_bin; 343 | "; 344 | 345 | $defaultSql2 = " 346 | CREATE TABLE t_supplier_product 347 | ( 348 | id INT AUTO_INCREMENT 349 | PRIMARY KEY, 350 | supplier_id INT NOT NULL 351 | COMMENT '供应商id', 352 | product_detail_id INT NOT NULL 353 | COMMENT '单品id', 354 | price DOUBLE NOT NULL 355 | COMMENT '采购价', 356 | KEY (`supplier_id`) 357 | ) 358 | COMMENT '供应商货品' 359 | ENGINE = InnoDB 360 | CHARSET = utf8; 361 | "; 362 | 363 | $parser = new CreateSqlParser(); 364 | if(empty($_POST['sql'])){ 365 | $sql = $defaultSql; 366 | }else{ 367 | $sql = $_POST['sql']; 368 | } 369 | $ret = $parser->execute($sql); 370 | echo json_encode($ret);exit(); 371 | --------------------------------------------------------------------------------