[关闭]
@Ivony 2015-02-03T23:21:29.000000Z 字数 7304 阅读 2414

QuickQuery

DbUtility


QuickQuery 是 SQL 查询语句的简化形式,致力于简化 SELECT 语句繁琐的语法格式。

一个最简单的 QuickQuery 查询像下面这样:

  1. users.username

这表示取出 users 表所有记录的的username字段,其等价于:

  1. SELECT users.username FROM users

既然是要取出 users 表的 username 字段,所以 FROM users 就是完全多余的语法,QuickQuery就是致力于免去这些多余的语法。

接下来逐一介绍 QuickQuery 的具体语法

基本查询

字段列表

要取出某个字段,只需要简单地使用字段名称即可,格式是:tablename.fieldname,像下面这样:

  1. users.username

要取出多个字段,则直接写出多个字段即可:

  1. users.username, users.email

  1. users.username users.email

可以为查询字段指定别名,格式是:alias : tablename.fieldname,像这样:

  1. name: users.username

别名

也可以对查询的表指定别名,格式是:alias: @tablenamealias: @tablename,像这样:

  1. u: @users name: u.username

一般我们可以插入换行让格式更清晰:

  1. u: @users
  2. name: u.username

也可以指定默认表名,格式是:@tablename,指定了默认表名后,字段表达式可以简化为fieldname,像这样:

  1. @users
  2. name: username

值得注意的是,别名的定义需要在使用这个别名之前,像下面的查询就是错误的:

  1. name : username
  2. @users
  1. name : u.usrename
  2. u : @users

反向别名

如果需要将别名放到后面,则可以使用反向别名标识符:>,如:

  1. users.username :> name

等价于:

  1. name : users.username

对于表名也是一样:

  1. @users :> u

等价于

  1. u : @users

条件查询

QuickQuery 使用 ? 开头的条件子句来表示查询条件,例如要取出 userid == 1 的 username 可以用下面这样的语法:

  1. @users username?userid==1

这等价于:

  1. SELECT username FROM users WHERE userid = 1

在QuickQuery中,子句的先后顺序没有关系,=和==是同样的意思,上面的查询也可以写为:

  1. @users ?userid=1 username

在不造成歧义的情况下,可以插入任意多个空白字符:

  1. @users
  2. ? userid = 1
  3. username

当有多个查询条件时,可以简单地直接用多个筛选子句

  1. @users
  2. ? userid = 1
  3. ? vip = 1
  4. username

多个查询条件自动是与的关系,即上面的QuickQuery等价于下面的SQL:

  1. SELECT users.username FROM users WHERE users.userid = 1 AND users.vip = 1

如果要表示或的关系,可以在一个 ? 后面写多个条件,例如:

  1. @users
  2. ?userid > 100 | vip = 1
  3. username

分隔符可以选用||和|中的任何一个

当然,如果你使用&或者&&作为分隔符,那么这些条件就是与的关系:

  1. @users
  2. ?userid > 100 & vip = 1
  3. username

这个和下面的表达式是一样的

  1. @users
  2. ?userid > 100 ?vip = 1
  3. username

在一个条件子句中同时出现 | 和 & 的时候,& 的优先级高于 | :

  1. @users username ?userid > 100 & level > 5 | vip = 1

等价于:

  1. @users username ?(userid > 100 & level > 5) | vip = 1

我们可以用括号来改变这个优先级:

  1. @users username ?userid > 100 & (level > 5 | vip = 1)

但是更简单的办法是写成两个子句:

  1. @users username ?userid > 100 ?level > 5 | vip = 1

灵活运用这一特性,便能避免括号的出现。

排序

排序子句用 $ 标识,例如我们要取出所有 username 并按照 userid 排序users:

  1. @users
  2. username
  3. $userid

多个排序条件可以使用多个排序子句:

  1. @users
  2. username
  3. $level $userid

默认排序方式是正序,若要倒序排,则使用两个 $ 符号标识:

  1. @users
  2. username
  3. $$level $userid

和条件子句一样,排序子句可以出现在任何地方:

  1. @users
  2. username
  3. $userid
  4. ?level > 5

但值得注意的是,要对多个字段排序的时候,多个排序子句之间是不能插入任何别的子句的,像下面这样就是错误的

  1. @users
  2. username $$level ?level>5 $userid

必须把两个排序字段写在一起:

  1. @users
  2. username $$level $userid ?level > 5

分页

若需要限制结果集的数量,可以使用分页函数,其用 >> 调用,例如只取出前 100 条记录

  1. @users
  2. username
  3. >> 100

取出第100-200条记录:

  1. @users
  2. username
  3. >> 100,200

参数和表达式

参数

在 QuickQuery 中要插入外部的参数有两种形式,命名参数和顺序参数,我们先看顺序参数。

顺序参数指从0开始的数字表示的参数,当创建 QuickQuery 时,可以提供一个参数列表,然后在 QuickQuery 中便会使用指定位置的参数值来作为顺序参数的值。

顺序参数的表达方式是 #+数字,像下面这样:

  1. @users
  2. ?userid = #0
  3. username

大多数时候,命名参数可以带来更好的可读性,命名参数的表达方式是#+参数名+#,像下面这样:

  1. @users
  2. ?userid = #id#
  3. username

命名参数甚至可以选择参数的某个属性,例如:

  1. @users
  2. ?userid = #UserCondition.ID#
  3. username

这表示取出userCondition这个参数的id属性作为参数的值

字面量

在 QuickQuery 中,用单引号或双引号括起一个字符串:

  1. @users
  2. userid ?username = "Ivony"
  1. @users
  2. userid ?username = 'Ivony'

可以用 null 关键字来表示 null 值。

  1. @users
  2. username ?type != null

算术表达式

在字段列表中,可以对字段进行简单的运算,但必须指定别名。

对于数值的字段,可以进行+、-、*、/四种运算:

  1. level : users.level - 5
  1. level : users.level - 1
  1. level : users.level * 100

对于字符串字段,则可以进行串联运算:

  1. @users
  2. name : "@" + username

这个查询会取出所有的用户名,并在前面加上"@"字符

逻辑表达式

逻辑表达式只能用于条件子句,由前面所说的逻辑运算符以及比较运算符组成。

用于数值的比较运算符有>、<、>=、<=、=/==和!=,见下:

  1. @users username ?level > 5
  1. @users username ?level <= 5

用于字符串的比较运算符有=/==、!=以及~/~=和!~/!~=,最后两个是LIKE和NOT LIKE

  1. @users username ?username ~ '%Ivony%'
  1. @users username ?username !~= '%Ivony%'

!用于对逻辑运算结果取否,如下所示:

  1. @users username ? !(username ~ '%Ivony%' | level > 5)
  1. SELECT username WHERE NOT ( username LIKE '%Ivony%' AND level > 5 )

当要对整个条件子句取否时,可以直接用 !? 代替 ? ,这样整个表达式就会变得非常简洁:

  1. @users username !?username ~ '%Ivony%' | level > 5

特殊名称

有时候字段名或者表名可能包含特殊字符,或者与现有的关键字,如 null 等冲突,此时应用反引号(`)将名称括起来,例如:

  1. @users
  2. `member.users`.username ?name = `null`

此处的member.users因为用反引号括起来了,所以被视为完整的名称,即member.users视为一个整体的表名,而null也被反引号扩住,则不会被视为代表 null 值,而是一个名为 null 的字段

若在特殊名称中包含反引号(`),则应当双写反引号,如:

  1. @users
  2. `user-name``1`

高级查询

多表查询

多表查询时,一般情况下不需要任何额外的语法,可以直接进行查询,表与表之间的连接关系,可以事先指定:

  1. users.username, profile.firstName, profile.lastName

假设我们users表和profile表已经建立了外键关系,users.userid和profile.userid是一对一的关系,则该查询等价于:

  1. SELECT users.username, profile.firstName, profile.lastName
  2. FROM users INNER JOIN profile ON users.userid = profile.userid

也可以在查询中直接指定表和表之间的连接关系,例如要指定 inner join 关系,用等号

  1. @users.userid = @profile.userid
  2. users.username, profile.firstName, profile.lastName

如果是 left join 则用:

  1. @users.userid > @profile.userid
  2. users.username, profile.firstName, profile.lastName

如果是 full join 则写为:

  1. @users.userid >< @profile.userid
  2. users.username, profile.firstName, profile.lastName

如果没有事先指定表连接关系,也没有在查询中进一步指定,则使用 cross join。

字段重命名,字段表声明

在字段列表中为字段指定的别名,只会影响到查询结果中的字段名称,不会影响到其他查询子句中的名称,考虑如下的查询:

  1. @users
  2. name : username ?name != null

此处条件子句中的name,视为users表的name字段,而不受前面的别名定义影响,即这个查询等价于:

  1. @users
  2. usrname : name ?users.name != null

如果我们需要对某个表的字段重新指定一个简洁的名字,可以使用字段重命名语法:

其语法形式为@tablename.fieldname -> alias

  1. @users.userid -> userid
  2. profile.username, profile.firstName, profile.lastName ?userid = #id# $userid

当对字段重命名后,若别名和字段名不同,那么根据别名取得字段名的时候,输出的时候用的是别名而非字段本名,考虑下面的查询:

  1. @users.userid -> id
  2. id, profile.username, profile.firstName, profile.lastName ?id = #id# $id

其等价于:

  1. @users @profile
  2. id : users.userid, username, firstName, lastName ?users.userid = #id# $users.userid

两个字段重命名为同一个名称或者为一个字段重命名两次是错误的,所以如下的查询都是错误的:

  1. @users @profile @users.userid -> userid @profile.userid -> userid
  2. userid, firstName, lastName ?userid = #id#
  1. @users @profile @users.userid -> userid @users.userid -> id
  2. userid, firstName, lastName ?userid = #id#

使用重命名后的字段,也可以在查询时再指定别名:

  1. @users.userid -> userid
  2. userid: id, username, firstName, lastName ?userid = #id# $userid

我们也可以简略的声明一个字段属于某个表,即将字段重命名为字段名本身,语法形式为@tablename.fieldname,即:

  1. @users.userid

等价于

  1. @users.userid -> userid

多个查询

可以用分号隔开多个查询:

  1. users.username?users.level>5;users.email

如果将表的连接关系和别名定义当做一个查询,那么这些设置会应用到后面所有的查询:

  1. @users.userid = @profile.userid @users @profile;
  2. username, firstName, lastName ?level>5;
  3. username, email ?userid=1;

但是写成这样是不合法的:

  1. @users.userid = @profile.userid @users @profile
  2. username, firstName, lastName ?level>5;
  3. username, email ?userid=1;

由于第一行的最后没有分号,所以这些设置只能用在第一个查询里,第二个查询会因为username和email以及userid没有指定表名而报错。

子查询

用下面的语法可以定义一个子查询:

  1. @u { users.username ?users.level>5 }

u 是这个子查询的名字,然后可以将这个子查询当做一个表来使用:

  1. @u { @users username, email ?level>5 }
  2. u.username?u.email!=null

在使用子查询之前,必须先对其进行定义,所以下面的表达式是不合法的:

  1. u.username?u.email!=null
  2. @u { @users username, email ?level>5 }

定义好子查询后,可以在子查询和子查询之间,或者子查询和现有表之间建立连接关系:

  1. @u { @users username userid ?level>5 }
  2. @u.userid = @profile.userid
  3. u.username, profile.firstName

等价的SQL为:

  1. SELECT u.username, profile.firstName FROM
  2. ( SELECT username, userid FROM users WHERE level > 5 ) AS u
  3. INNER JOIN profile ON u.userid = profile.userid

子查询的名称必须是唯一的,如果子查询的名称与现有表名相同,则会覆盖现有表名。

SQL子查询

可以直接在QuickQuery中嵌入一个SQL语句作为子查询,在花括号前面加上#标识:

  1. @u #{ SELECT username, userid FROM users WHERE level > 5 }
  2. u.username ? u.email != null

在SQL子查询中如果出现 { 或者 }, 则必须双写,即使出现在字符串中:

  1. @u #{ SELECT username, userid FROM users WHERE username LIKE '%}}%' }
  2. u.username ? u.email != null

注意:在SQL子查询中是不允许使用参数的。

分组查询

QuickQuery 支持在字段列表中使用常见的一些聚合函数,例如COUNT、SUM、AVG等,当进行聚合查询时,没有出现在聚合函数的字段则成为分组依据。

调用聚合函数必须在函数名前面加上^标识,的具体语法如下:
^group-function( expression )
或者
expression |> ^group-function

参考下面的例子:

  1. @users
  2. score : ^sum( score ), level

  1. @users
  2. score : users.score |> ^sum, users.level

等价SQL:

  1. SELECT SUM( score ) AS score, level FROM users GROUP BY level

若一个查询条件包含聚合函数,则该查询条件自动被解释为HAVING子句:

  1. @users
  2. score : ^sum( score ), level ?^sum( score ) > 0

等价的 SQL 为:

  1. SELECT SUM( score ) AS score, level FROM users GROUP BY level HAVING SUM( score ) > 0

若查询条件未包含聚合函数,则该查询条件被解释为WHERE子句:

  1. @users
  2. score: score |> ^sum, level ?level > 3

等价的 SQL 为:

  1. SELECT SUM( score ) AS score, level FROM users WHERE level > 3 GROUP BY level

集合运算

子查询、表,或者查询结果都可以进行集合运算,主要有并集:|,交集:&,差集:-,三种运算:

两个表直接运算并集:

  1. @users | @users1

等价于:

  1. SELECT * FROM users
  2. UNION
  3. SELECT * FROM users1

子查询或者查询结果运算并集:

  1. @a{@users username?level=3 }
  2. @b{@users username?level=5 }
  3. @a | @b

等价于

  1. SELECT username FROM users WHERE level = 3
  2. UNION
  3. SELECT username FROM users WHERE level = 5
  1. @a{@users username?level=3 }
  2. @a | {@users username?level=5 }
  1. {@users username?vip = 1 }
  2. &
  3. {@users username?level>3 }

等价于

  1. SELECT username FROM users WHERE level = 3
  2. INTERSECT
  3. SELECT username FROM users WHERE level = 5
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注