qstart-soft / query-builder
用于创建 DML(数据操作语言)SQL 语句的库。
Requires
- php: ^7.4 || ^8.0
Requires (Dev)
- phpunit/phpunit: ^9.6
README
用于创建 DML(数据操作语言)SQL 语句的库。
- 第1部分:基础
- 第2部分:表格格式
- 第3部分:条件格式
- 第4部分:SELECT SQL 语句
- 第5部分:INSERT SQL 语句
- 第6部分:UPDATE SQL 语句
- 第7部分:DELETE SQL 语句
第1部分:基础
入口点
创建 SQL 语句的入口点是 Query::class
工厂。
use Qstart\Db\QueryBuilder\Query; $select = Query::select(); $insert = Query::insert(); $update = Query::update(); $delete = Query::delete();
创建
获取 SQL 语句和绑定参数
use Qstart\Db\QueryBuilder\Query; $query = Query::select(); // ... $builder = $query->getQueryBuilder() // If necessary, you can set the dialect for the query builder $builder->setDialect(DialectSQL::POSTGRESQL); $expr = $builder->build(); // Binding parameters $params = $expr->getParams(); // Sql string $sql = $expr->getExpression();
表达式
表达式是继承自 \Qstart\Db\QueryBuilder\DML\Expression\ExprInterface 接口的自定义类。
这些类允许您为 SQL 查询创建特定表达式。
初始表达式类允许您传递一个不可变的表达式,该表达式将添加到查询中而不会被修改
use Qstart\Db\QueryBuilder\DML\Expression\Expr; $expr = new Expr('created_at > now()'); // This is the sql expression $expression = $expr->getExpression(); // This is the binding params $params = $expr->getParams();
其他几个表达式
use Qstart\Db\QueryBuilder\DML\Expression\BetweenExpr; use Qstart\Db\QueryBuilder\DML\Expression\CompareExpr; use Qstart\Db\QueryBuilder\DML\Expression\InExpr; $expr = new CompareExpr('!=', 'id', 20); $expression = $expr->getExpression(); // id != :v1 $params = $expr->getParams(); // ['v1' => 20] $expr = new BetweenExpr('id', 10, 20); $expression = $expr->getExpression(); // id BETWEEN :v1 AND :v2 $params = $expr->getParams(); // ['v1' => 10, 'v2' => 20] $expr = new InExpr('id', [10, 20], true); $expression = $expr->getExpression(); // id NOT IN (:v3, :v4) $params = $expr->getParams(); // ['v3' => 10, 'v4' => 20]
确实,对于不同的 SQL 语法,相同的构造可能具有不同的语法。
为此,您可以将方言传递到表达式中。
use Qstart\Db\QueryBuilder\DML\Expression\Expr; use Qstart\Db\QueryBuilder\Helper\DialectSQL; $expr = new Expr('created_at > now()'); $expressionCh = $expr->getExpression(DialectSQL::CLICKHOUSE); $expressionPg = $expr->getExpression(DialectSQL::POSTGRESQL); $params = $expr->getParams();
第2部分:表格格式
所有可用方法中的表格格式相同
- join()
- innerJoin()
- leftJoin()
- rightJoin()
- SelectQuery::from()
- UpdateQuery::joinFrom()
- UpdateQuery::setTable()
- InsertQuery::into()
- DeleteQuery::from()
可用格式(以 SelectQuery::from() 方法为例)
use Qstart\Db\QueryBuilder\Query; use Qstart\Db\QueryBuilder\DML\Expression\Expr; $query = Query::select(); // Table name only $query->from('user'); $query->from(['user']); // Table name with alias $query->from('user u'); $query->from(['u' => 'user']); // You can also pass an expression or another query instead of the table name $query->from(['u' => Query::select()->from('user')]); // Result: SELECT * FROM (SELECT * FROM user) AS u $query->from(['u' => new Expr("(SELECT * FROM user)")]); // Result: SELECT * FROM (SELECT * FROM user) AS u // Same thing without alias $query->from(Query::select()->from('user')); $query->from(new Expr("SELECT * FROM user")); // You can also set several tables in any available format $query->from(['u' => 'user', 's' => 'session']); // Result: SELECT * FROM user AS u, session AS s
您还可以更改第一个表的别名
- SelectQuery::from()
- UpdateQuery::setTable()
- InsertQuery::into()
- DeleteQuery::from()
示例
use Qstart\Db\QueryBuilder\Query; $query = Query::select()->from(['user'])->alias('u'); // Result: SELECT * FROM user AS u $query = Query::select()->from(['u' => 'user'])->alias('t'); // Result: SELECT * FROM user AS t $query = Query::select()->from(['u' => 'user', "s" => "session"])->alias('t'); // Result: SELECT * FROM user AS t, session AS s
第3部分:条件格式
任何条件都可以以下格式传递
1. 相等条件数组。
数组是键值对。键是左侧表达式。值是以下选项之一
use Qstart\Db\QueryBuilder\Query; use Qstart\Db\QueryBuilder\DML\Expression\Expr; $conditions = ['user_id' => 10, 'session_id' => 101]; // user_id = 10 AND session_id = 101 $conditions = ['user_id' => [10, 20], 'session_id' => 101]; // user_id IN (10, 20) AND session_id = 101 $conditions = ['user_id' => Query::select()->select('id')->from('user'), 'session_id' => 101]; // user_id IN (SELECT id FROM user) AND session_id = 101 $conditions = ['user_id' => new Expr("LEAST(10, 20)"), 'session_id' => 101]; // user_id = LEAST(10, 20) AND session_id = 101 Query::select()->where($conditions);
2. 任何继承自 ExprInterface 的 Expression 实例
use Qstart\Db\QueryBuilder\Query; use Qstart\Db\QueryBuilder\DML\Expression\Expr; use Qstart\Db\QueryBuilder\DML\Expression\InExpr; $conditions = new Expr('created_at >= now()'); // created_at >= now() $conditions = new InExpr('id', [10, 20], true); // id NOT IN (10, 20) Query::select()->where($conditions);
3. 字符串格式
use Qstart\Db\QueryBuilder\Query; $conditions = 'created_at >= now()' Query::select()->from('user')->where($conditions); // SELECT * FROM user WHERE created_at >= now() $conditions = [ 'and', 'created_at >= now()', ['id' => 2] ] Query::select()->from('user')->where($conditions); // SELECT * FROM user WHERE (created_at >= now()) AND (id = 2)
4. 使用 "OR"、"AND"、"NOT" 操作符进行分组
然后需要使用 AND、OR、NOT 操作符组合条件。
所有这些组合都具有相同的格式 [operator, condition, condition, ...]
数组中的第一个必须是 AND / OR / NOT 操作符。
接下来,通过逗号分隔,是条件,可以是三种格式之一(数组、表达式、字符串)。这些条件也可以使用 AND / OR / NOT 操作符的格式
例如:['AND', $condition1, $condition2, ['OR', $condition3, $condition4]]
use Qstart\Db\QueryBuilder\DML\Expression\Expr; use Qstart\Db\QueryBuilder\DML\Expression\InExpr; $conditions = ['and', ['user_id' => 10, 'session_id' => 101], new Expr("id = LEAST(10, 20)")]; // (user_id = 10 AND session_id = 101) AND (id = LEAST(10, 20)) $conditions = ['or', ['user_id' => 10, 'session_id' => 101], new Expr("id = LEAST(10, 20)")]; // (user_id = 10 AND session_id = 101) OR (id = LEAST(10, 20)) $conditions = ['not', ['user_id' => 10, 'session_id' => 101], new Expr("id = LEAST(10, 20)")]; // NOT ((user_id = 10 AND session_id = 101) AND (id = LEAST(10, 20))) // Lets combine it $conditions = [ 'and', ['or', ['id' => 2], ['id' => 3]], ['not', ['session_id' => 10]] ]; // ((id = 2) OR (id = 3)) AND (NOT (session_id = 10))
如何使用 SELECT 查询,例如
use Qstart\Db\QueryBuilder\Query; $query = Query::select()->where(['and', $condition1, $condition2]); // Its equal with: $query = Query::select()->where($condition1)->andWhere($condition2); $query = Query::select()->where(['or', $condition1, $condition2]); // Its equal with: $query = Query::select()->where($condition1)->orWhere($condition2);
第4部分:SELECT SQL 语句
1. 选择
要构建 SELECT 子句,您需要使用方法
- Query::select()->select() 创建 SELECT 子句
- Query::select()->addSelect() 添加值到 SELECT 子句
- Query::select()->distinct(true) 添加 DISTINCT 关键字
方法 select() 会覆盖之前添加的所有值!
子句可以以不同的格式构建。
use Qstart\Db\QueryBuilder\Query; use Qstart\Db\QueryBuilder\DML\Expression\Expr; // 1. String format Query::select()->select('id, name, surname'); // 2. Array alias-value format Query::select() ->select([ 'id', 'name' => "name || ' ' || surname", new Expr('created_at::DATE as date'), 'cnt' => Query::select()->select('COUNT(*)')->from('user') ]); // Result: SELECT id, name || ' ' || surname AS name, created_at::DATE as date, (SELECT COUNT(*) FROM user) AS cnt // 3. Add values Query::select()->select('id, name')->addSelect(new Expr('created_at::DATE as date')); // Result: SELECT id, name, created_at::DATE as date // 4. Reset values Query::select()->select(null); // 4. Distinct Query::select()->select('id, name')->distinct(true); // // Result: SELECT DISTINCT id, name
2. WHERE
要构建 WHERE 子句,您需要使用方法
- Query::select()->where() 创建 WHERE 子句
Query::select()->andWhere() 使用 AND 操作符将条件添加到当前条件中。相同 - ['and', current conditions, new conditions] - Query::select()->orWhere() 使用 OR 操作符将条件添加到当前条件中。相同 - ['or', current conditions, new conditions]
方法 where() 会覆盖之前添加的所有值! - 所有方法都接受在 '条件格式' 中描述的格式中的条件。
您还可以使用将移除条件中所有 NULL 值的方法。对于 Expression 实例,将调用 ExprInterface::isEmpty() 方法。
Query::select()->filterWhere()
Query::select()->andFilterWhere()
use Qstart\Db\QueryBuilder\Query; Query::select()->where(['id' => 2])->andWhere(['user_id' => 3]);
Query::select()->orFilterWhere()
- 方法 filterWhere() 会覆盖之前添加的所有值!
- Query::select()->andFilterWhere()
- Query::select()->orFilterWhere()
方法 filterWhere() 会覆盖之前添加的所有值!
Query::select()->andFilterWhere()
use Qstart\Db\QueryBuilder\Query; use Qstart\Db\QueryBuilder\DML\Expression\CompareExpr; Query::select()->filterWhere(['id' => null])->andFilterWhere(new CompareExpr('>=', 'id', null)); // Result will be without WHERE clause
3. 分组 BY
要构建GROUP BY子句,您需要使用方法
- Query::select()->groupBy() 创建GROUP BY子句
- Query::select()->addGroupBy() 向GROUP BY子句添加值
方法groupBy()会覆盖之前添加的所有值!
子句可以以不同的格式构建。
use Qstart\Db\QueryBuilder\Query; // 1. String format Query::select()->from('user')->groupBy('id, name'); // SELECT * FROM user GROUP BY id, name // 2. Array format Query::select()->from('user')->groupBy(['id', 'name']); // SELECT * FROM user GROUP BY id, name // 3. Expression/Query format Query::select()->from('user')->groupBy(new Expr('id, name')); // SELECT * FROM user GROUP BY id, name // 4. Add values Query::select()->from('user')->groupBy('id, name')->addGroupBy(new Expr('created_at::DATE')); // Result: SELECT * FROM user GROUP BY id, name, created_at::DATE // 5. Reset values Query::select()->groupBy(null);
4. 排序
要构建ORDER BY子句,您需要使用方法
- Query::select()->orderBy() 创建ORDER BY子句
- Query::select()->addOrderBy() 向ORDER BY子句添加值
方法orderBy()会覆盖之前添加的所有值!
子句可以以不同的格式构建。
use Qstart\Db\QueryBuilder\Query; // 1. String format Query::select()->from('user')->orderBy('id, name'); // SELECT * FROM user ORDER BY id, name // 2. Array format Query::select()->from('user')->orderBy(['id' => SORT_ASC, 'name' => SORT_DESC]); // SELECT * FROM user ORDER BY id ASC, name DESC // 3. Expression/Query format Query::select()->from('user')->orderBy(new Expr('id ASC, name DESC')); // SELECT * FROM user ORDER BY id ASC, name DESC // 4. Mix format Query::select()->from('user')->orderBy(['id' => SORT_ASC, 'name' => SORT_DESC, new Expr('created_at::DATE DESC')]); // SELECT * FROM user ORDER BY id ASC, name DESC, created_at::DATE DESC // 5. Add values Query::select()->from('user')->orderBy('id DESC')->addOrderBy(new Expr('created_at::DATE DESC')); // Result: SELECT * FROM user ORDER BY id DESC, created_at::DATE DESC // 6. Reset values Query::select()->orderBy(null);
5. HAVING
要构建HAVING子句,您需要使用方法
- Query::select()->having()
- Query::select()->andHaving()
- Query::select()->orHaving()
方法having()会覆盖之前添加的所有值!
这些方法的工作方式与'WHERE'方法类似。
6. OFFSET
要构建OFFSET子句,您需要使用方法
- Query::select()->offset()
使用null值来禁用偏移。
偏移量可以是int|ExprInterface|SelectQuery|null
use Qstart\Db\QueryBuilder\Query; // 1. Integer Query::select()->from('user')->offset(10); // SELECT * FROM user OFFSET 10 // 2. Expression Query::select()->from('user')->offset(new Expr("length('SPARK')")); // SELECT * FROM user OFFSET length('SPARK') // 2. Reset value Query::select()->from('user')->offset(null);
7. LIMIT
要构建LIMIT子句,您需要使用方法
- Query::select()->limit()
使用null值来禁用限制。
偏移量可以是int|ExprInterface|SelectQuery|null
use Qstart\Db\QueryBuilder\Query; // 1. Integer Query::select()->from('user')->limit(10); // SELECT * FROM user LIMIT 10 // 2. Expression Query::select()->from('user')->limit(new Expr("length('SPARK')")); // SELECT * FROM user LIMIT length('SPARK') // 2. Reset value Query::select()->from('user')->limit(null);
8. JOIN
要构建不同的JOIN子句,您需要使用方法
- Query::select()->join()
- Query::select()->leftJoin()
- Query::select()->rightJoin()
- Query::select()->innerJoin()
所有这些方法都接受与“条件格式”部分中描述的格式相同的条件。
此外,所有这些方法都接受与“表格式”部分中描述的格式相同的表。
用法示例
use Qstart\Db\QueryBuilder\Query; Query::select()->from('user u')->leftJoin('session s', 'u.id = s.user_id'); // Result: SELECT * FROM user u LEFT JOIN session s ON u.id = s.user_id
9. UNION
要使用联合查询,您需要使用方法。
- Query::select()->union() 添加联合查询
- Query::select()->deleteUnion() 删除所有联合查询
ORDER BY子句将从所有查询中组合并添加到联合查询的末尾
查询可以是string|ExprInterface|SelectQuery
use Qstart\Db\QueryBuilder\Query; use Qstart\Db\QueryBuilder\DML\Expression\Expr; $query = Query::select() ->from('table t')->where(['user_id' => 2])->orderBy('created_at') ->union(Query::select()->from('table2 t2')->where(['user_id' => 12])->orderBy('id'), true) ->union(Query::select()->from('table3 t3')->where(['user_id' => 22])) ->union(new Expr('SELECT * FROM table4 t4 WHERE user_id = :id', ['id' => 32])) ->union('SELECT * FROM table5 t5', true); // Result: <<<SQL SELECT * FROM table t WHERE user_id = :v1 UNION ALL SELECT * FROM table2 t2 WHERE user_id = :v2 UNION SELECT * FROM table3 t3 WHERE user_id = :v3 UNION SELECT * FROM table4 t4 WHERE user_id = :id UNION ALL SELECT * FROM table5 t5 ORDER BY created_at, id SQL;
第5部分:INSERT SQL 语句
创建INSERT INTO语句的格式
INSERT INTO table_name (column1, column2, column3, ...) VALUES (value1, value2, value3, ...);
要指定表名使用
- Query::insert()->into()
此方法接受与“表格式”部分中描述的格式相同的表。
要向VALUES子句添加值组,请使用方法
- Query::insert()->addValues($data)
数据应为格式为[column1 => value1, ...]的数组或QueryInterface的实例 - Query::insert()->addMultipleValues($data)
数据应为格式为[column1 => value1, ...]的数组数组
要更改语句的开始或结束,请使用方法
- Query::insert()->setStartOfQuery()
表达式INSERT INTO
将被替换为传递的表达式 - Query::insert()->setEndOfQuery()
表达式将被添加到查询的末尾
use Qstart\Db\QueryBuilder\Query; $query = Query::insert()->into('user')->addValues(['name' => 'John', 'surname' => 'Jonson']); // Result: INSERT INTO user (name, surname) VALUES (:v1, :v2) $query->setStartOfQuery('INSERT IGNORE INTO')->setEndOfQuery('RETURNING id'); // Result: INSERT IGNORE INTO user (name, surname) VALUES (:v1, :v2) RETURNING id $query = Query::insert()->into('user')->addMultipleValues([['name' => 'John', 'surname' => 'Jonson'], ['surname' => 'Nelson', 'name' => 'Mike']]); // Result: INSERT INTO user (name, surname) VALUES (:v1, :v2), (:v4, :v3)
第6部分:UPDATE SQL 语句
1. 表
要指定表名使用
- Query::update()->setTable()
此方法接受与“表格式”部分中描述的格式相同的表。
use Qstart\Db\QueryBuilder\Query; $query = Query::update()->setTable('user'); // Result: UPDATE user
2. SET
要构建SET子句,您需要使用方法
- Query::update()->set($attributes)
要创建SET子句 - Query::update()->addSet($attributes)
要将属性添加到SET子句
方法set()会覆盖之前添加的所有属性!
属性可以以不同的格式传递。
如果我们带有关键传递它,我们将尝试将值添加为查询参数。
如果未传递键,则值将是一个字符串。
值可以传递为字符串、ExprInterface实例或QueryInterface实例。
use Qstart\Db\QueryBuilder\Query; use Qstart\Db\QueryBuilder\DML\Expression\Expr; // 1. Format of attributes $query = Query::update() ->setTable('"user"') ->set([ 'name' => 'John', // name=:v19, 'age' => new Expr('18 + 10'), // age=18 + 10, 'last_session_at' => Query::select()->from('session')->select('MAX(created_at)')->where(['user_id' => 123]) ]) ->addSet("status='active'") // status='active', ->addSet(new Expr('is_active = TRUE')); // is_active = TRUE, // Result: UPDATE "user" SET name = :v1, age = 18 + 10, last_session_at = (SELECT MAX(created_at) FROM session WHERE user_id = :v2), status='active', is_active = TRUE // 2. Reset SET clause $query = Query::update()->setTable('"user"')->set(null);
3. WHERE
要构建WHERE子句,请参阅“第3部分. 选择SQL语句”中的描述。格式和方法将与选择SQL语句完全相同。
4. JOIN
要构建不同的JOIN子句,请参阅“第3部分. 选择SQL语句”中的描述。格式和方法将与选择SQL语句完全相同。
5. JOIN FROM
要构建FROM子句,您需要使用方法
- Query::update()->joinFrom() 此方法接受与“表格式”部分中描述的格式相同的表。
使用空值禁用 FROM 子句
use Qstart\Db\QueryBuilder\Query; $query = Query::update()->setTable('user')->joinFrom('session'); // Result: UPDATE user FROM session
6. 限制
要构建 LIMIT 子句,请参阅“第 3 部分. 选择 SQL 语句”中的说明。格式和方法将与选择 SQL 语句完全相同
7. 开始和结束子句
要更改语句的开始或结束,请使用方法
- Query::update()->setStartOfQuery()
表达式UPDATE
将被传入的表达式替换 - Query::update()->setEndOfQuery()
表达式将被添加到查询的末尾
use Qstart\Db\QueryBuilder\Query; $query = Query::update() ->setTable('user') ->setStartOfQuery('UPDATE ONLY') ->setEndOfQuery('RETURNING id'); // Result: UPDATE ONLY user RETURNING id
第7部分:DELETE SQL 语句
1. 从
要指定表名使用
- Query::delete()->from()
此方法接受与“表格式”部分中描述的格式相同的表。
use Qstart\Db\QueryBuilder\Query; $query = Query::delete()->from('user'); // Result: DELETE FROM user
2. WHERE
要构建WHERE子句,请参阅“第3部分. 选择SQL语句”中的描述。格式和方法将与选择SQL语句完全相同。
3. 连接
要构建不同的JOIN子句,请参阅“第3部分. 选择SQL语句”中的描述。格式和方法将与选择SQL语句完全相同。
4. 使用
要构建 USING 子句,需要使用方法
- Query::delete()->using() 此方法接受表格格式,如上述 '表格格式' 部分中所述。
使用空值禁用 USING 子句
use Qstart\Db\QueryBuilder\Query; $query = Query::delete()->from('user')->using('session'); // Result: DELETE FROM user USING session
5. 限制
要构建 LIMIT 子句,请参阅“第 3 部分. 选择 SQL 语句”中的说明。格式和方法将与选择 SQL 语句完全相同
6. 开始和结束子句
要更改语句的开始或结束,请使用方法
- Query::delete()->setStartOfQuery()
表达式DELETE FROM
将被传入的表达式替换 - Query::delete()->setEndOfQuery()
表达式将被添加到查询的末尾
use Qstart\Db\QueryBuilder\Query; $query = Query::delete() ->from('user') ->setStartOfQuery('DELETE FROM ONLY') ->setEndOfQuery('RETURNING id'); // Result: DELETE FROM ONLY user RETURNING id