PHPackages                             zqx/sql-translation - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. zqx/sql-translation

ActiveLibrary[Database &amp; ORM](/categories/database)

zqx/sql-translation
===================

v1.2(7y ago)08MITPHPPHP &gt;=7.0

Since Oct 27Pushed 7y ago1 watchersCompare

[ Source](https://github.com/grey-zeng/sql-translation)[ Packagist](https://packagist.org/packages/zqx/sql-translation)[ Docs](https://github.com/grey-zeng/sql-translation)[ RSS](/packages/zqx-sql-translation/feed)WikiDiscussions master Synced 3w ago

READMEChangelogDependencies (1)Versions (4)Used By (0)

sql-translation
===============

[](#sql-translation)

A pure PHP SQL parser to translate custom sql to MySQL or PostgreSQL

安装
--

[](#安装)

推荐使用composer进行安装，然后引用vendor/autoload.php即可

> composer require zqx/sql-translation

What
----

[](#what)

- 将自定义的sql伪码转换成MySQL/PostgreSQL可执行sql代码；
- 提供字段映射和校验，如传入公式为单字段：`[时间]`,通过转化生成数据表真实字段名`rectime`；
- 由纯PHP代码组成，可通过composer或者下载引用，直接嵌入到现有的PHP服务中；

Why
---

[](#why)

1. 在BI系统或者和数据有关的场景下，需要让业务人员通过**动态写sql**进行查询，可以使用公式、运算符、字段、字符串、数字以及以上各种内容的组合来增强灵活性。
    - 期内充值：如数据为`新登时间:2018-10-01;时间:2018-10-03;金额:100.00`，由业务人员加上动态字段`date_diff(时间,新登时间)`识别每条充值记录为玩家新登第n日充值；
    - 权重分摊：如数据有`自营流水、联运流水、投入`等字段，每天的盈利计算公式为`自营+联运*20%-投入`；
2. 针对同一个公式，需要保证均能正常翻译成MySQL或者PostgreSQL的执行代码，减少上层业务对下层db的直接依赖。
    - 例如拼接字符串函数：MySQL中为concat(str1, str2), PostgreSQL为(str1 || str2)
3. 保证良好的安全性，避免通过动态sql直接穿透到代码执行区域，减少恶意的sql注入风险。
4. 通过别名映射，如`rectime=>时间`展示给用户，避免用户直接拿到数据库真实字段名，同时还可以屏蔽不想被访问到的字段。

How
---

[](#how)

1. 初始化

```
$translator = new Translator();
$columns = [
    [
        'alias' => '新登时间',
        'column' => 'first_login_time',
        'type' => Meta::DATA_TYPE_TIMESTAMP
    ],
    ...
];
// 设置参与编译的自定义字段
$translator->setColumns($columns);

```

2. 传入sql伪码进行编译

```
$input='date_diff([时间],[新登时间])';

// 默认MySQL编译得到:datediff(`retime`, `first_login_time`)
$mySqlDateDiff = $translator->compile($input)->translate();

// PostgreSQL环境下为:("retime"::date-"first_login_time"::date)
$pgSqlDateDiff = $translator->compile($input)->translate(Translator::DB_PGSQL);

```

具体示例可看：[demo.php](https://github.com/grey-zeng/sql-translation/blob/master/example/demo.php)

更多进行sql编译的例子可看单元测试用例：[TranslatorTest.php](https://github.com/grey-zeng/sql-translation/blob/master/tests/TranslatorTest.php)

实现分析
----

[](#实现分析)

### 流程图

[](#流程图)

[![流程图](./doc/%E7%BC%96%E8%AF%91%E5%99%A8.jpg)](./doc/%E7%BC%96%E8%AF%91%E5%99%A8.jpg)

### 步骤分析

[](#步骤分析)

#### 1. 分词

[](#1-分词)

使用正则进行分词：

```
使用正则表达式来做nfa匹配
    const REG_EMPTY     = '/^([\s,]+)/';                                    // 空值或者逗号
    const REG_STATEMENT = '/^(while|declare)(\s)?\(/';                      // 预定义关键字，目前支持while和declare
    const REG_FUNCTION  = '/^(\w+)(\s)?\(/';                                // 函数，类似funcName(
    const REG_BRACKET   = '/^(\(|\))/';                                     // 左右括号
    const REG_COLUMN    = '/^(\[([\x{4e00}-\x{9fa5}a-zA-Z0-9_\-]+)\])/u';   // 匹配使用[]包含的自定义字段，需要使用Unicode解析中文
    const REG_NUMBER    = '/^((-?\d+)(\.\d+){0,1})/';                       // 正负整型及浮点数
    const REG_STRING    = '/^((\'|\")([\s\S]*?)(\2))/';                     // 使用""或者''闭合的字符串，需要非贪婪匹配
    const REG_OPERATOR  = '/^(\+|\-|\*|\/|(>|
