CQL(Cassandra Query Language)函数,cqlcassandra
CQL支持两大类功能:
- 标量函数,其简单地取多个值并且用它产生输出。
- 聚合函数,用于聚合来自SELECT语句的多行结果。
出于安全问题的考虑,默认情况下用户自定义函数是被禁止的(即使启用了,用户自定义函数的执行也是沙盒化执行并且恶意方法不被允许执行,但是没有沙盒是完美的,所以用户自定义方法是选择加入的)。请参见enable_user_defined_functionsin cassandra.yaml以启用它们。
函数通过其名称标识:
function_name ::= [keyspace_name'.' ]name
表量函数(Scalar functions)
本地函数(Native functions)
Cast
cast函数可用于将一种本地数据类型转换为另一种数据类型。
下表描述了cast函数支持的转换。Cassandra将默认忽略任何将数据类型转换为其自己的数据类型的转换。
| From | To |
|---|---|
ascii |
text,
varchar |
bigint |
tinyint,
smallint, int,float,double,
decimal,varint,text,varchar |
boolean |
text,
varchar |
counter |
tinyint,
smallint, int,bigint,float,
double,decimal,varint,text,varchar |
date |
timestamp |
decimal |
tinyint,
smallint, int,bigint,float,
double,varint,text,varchar |
double |
tinyint,
smallint, int,bigint,float,
decimal,varint,text,varchar |
float |
tinyint,
smallint, int,bigint,double,
decimal,varint,text,varchar |
inet |
text,
varchar |
int |
tinyint,
smallint, bigint,float,double,
decimal,varint,text,varchar |
smallint |
tinyint,
int, bigint,float,double,
decimal,varint,text,varchar |
time |
text,
varchar |
timestamp |
date,
text, varchar |
timeuuid |
timestamp,
date, text,varchar |
tinyint |
tinyint,
smallint, int,bigint,float,
double,decimal,varint,text,varchar |
uuid |
text,
varchar |
varint |
tinyint,
smallint, int,bigint,float,
double,decimal,text,varchar |
转换严格依赖于Java的语义。例如,double值1将被转换为文本值'1.0'。例如:
SELECT avg(cast(count as double)) FROM myTable
Token
token函数允许计算给定分区键的token。token函数的确切签名取决于有关的表和集群使用的分区器。
token的参数类型取决于分区键列的类型。返回类型取决于正在使用的分区器:
- 对于Murmur3Partitioner,返回类型是bigint。
- 对于RandomPartitioner,返回类型为varint。
- 对于ByteOrderedPartitioner,返回类型为blob。
例如,在使用默认Murmur3Partitioner的集群中,如果表定义如下:
CREATE TABLE users ( userid text PRIMARY KEY, username text, )
那么token函数将采用类型为text的单个参数(在这种情况下,分区键是userid(没有聚集列,因此分区键与主键相同)),返回类型将是bigint。
Uuid
uuid函数不使用参数,并生成适用于INSERT或UPDATE语句的随机类型4 uuid。
Timeuuid 函数
now
now函数不使用参数,并且在协调器节点上生成新的唯一timeuuid(在使用它的语句被执行的时候)。注意,这个方法对插入是有用的,但在WHERE子句中很大程度上是无意义的。例如,以下形式的查询:
SELECT * FROM myTable WHERE t = now()
将不会返回任何设计结果,因为now()返回的值保证是唯一的。
minTimeuuid andmaxTimeuuid
minTimeuuid(resp.maxTimeuuid)函数采用时间戳值t(其可以是时间戳或日期字符串<timestamps>),并返回与具有时间戳t的最小(最大)可能时间戳相对应的伪时间戳。例如:
SELECT * FROM myTable WHERE t > maxTimeuuid('2013-01-01 00:05+0000') AND t < minTimeuuid('2013-02-02 10:00+0000')
将选择timeuuid列t严格晚于'2013-01-01 00:05 + 0000'但严格早于'2013-02-02 10:00 + 0000'的所有行。请注意,t> = maxTimeuuid('2013-01-01 00:05 + 0000')仍然不会选择正好在'2013-01-01 00:05 + 0000'生成的时间流,并且基本上等于t> maxTimeuuid('2013-01-01 00:05 + 0000')。
注意:我们调用由minTimeuuid和maxTimeuuid伪造UUID生成的值,因为它们不遵守由RFC 4122指定的基于时间的UUID生成过程。特别地,这两种方法返回的值不是唯一的。这意味着您应该只使用这些方法进行查询(如上例所示)。插入这些方法的结果几乎肯定是一个坏主意。时间转换函数(Time conversion functions)
提供了许多函数来将时间符,时间戳或日期“转换”成另一种本地类型。
| 函数名词 | 输入类型 | Description |
|---|---|---|
toDate |
timeuuid |
将timeuuid参数转换为日期类型 |
toDate |
timestamp |
将时间戳参数转换为日期类型 |
toTimestamp |
timeuuid |
将timeuuid参数转换为时间戳类型 |
toTimestamp |
date |
将date参数转换为时间戳类型 |
toUnixTimestamp |
timeuuid |
将timeuuid参数转换为bigInt原始值 |
toUnixTimestamp |
timestamp |
将时间戳参数转换为bigInt原始值 |
toUnixTimestamp |
date |
将date参数转换为bigInt原始值 |
dateOf |
timeuuid |
类似于toTimestamp(timeuuid) |
unixTimestampOf |
timeuuid |
类似于toUnixTimestamp(timeuuid) |
Blob转换函数
提供了许多函数来将本地类型“转换”为进制数据(blob)。对于CQL支持的每个<native-type>类型(一个值得注意的例外是blob),函数typeAsBlob接受一个类型的参数,并将其返回为一个blob。相反,函数blobAsType采用64位blob参数,并将其转换为bigint值。因此,例如,bigintAsBlob(3)是0x0000000000000003和blobAsBigint(0x0000000000000003)是3。用户自定义函数
用户自定义函数允许在Cassandra中执行用户提供的代码。 默认情况下,Cassandra支持在Java和JavaScript中定义函数。 通过向类路径添加JAR,可以添加对其他符合JSR 223的脚本语言(例如Python,Ruby和Scala)的支持。 UDF是Cassandra模式的一部分。 因此,它们会自动传播到集群中的所有节点。UDF可以重载 - 即具有不同参数类型但具有相同函数名称的多个UDF。 例:
CREATE FUNCTION sample ( arg int ) ...; CREATE FUNCTION sample ( arg text ) ...;
用户自定义函数易受所选择的编程语言的普遍问题的影响。 因此,实现应该对空指针异常,非法参数或任何其他潜在的异常源是安全的。 在函数执行期间的异常将导致整个语句失败。
使用复杂类型(如集合,元组类型和用户定义的类型)作为参数和返回类型是有效的。元组类型和用户定义的类型由DataStax Java驱动程序的转换函数处理。有关处理元组类型和用户定义类型的详细信息,请参阅Java驱动程序的文档。
函数的参数可以是字面量或term。也可以使用准备语句的占位符。
您可以使用双引号字符串语法来包含UDF源代码。例如:
CREATE FUNCTION some_function ( arg int ) RETURNS NULL ON NULL INPUT RETURNS int LANGUAGE java AS $$ return arg; $$; SELECT some_function(column) FROM atable ...; UPDATE atable SET col = some_function(?) ...; CREATE TYPE custom_type (txt text, i int); CREATE FUNCTION fct_using_udt ( udtarg frozen ) RETURNS NULL ON NULL INPUT RETURNS text LANGUAGE java AS $$ return udtarg.getString("txt"); $$;
用户自定义函数可以在SELECT,INSERT和UPDATE语句中使用。
隐式可用的UDFContext字段(或对脚本UDF的绑定)提供了创建新UDT和元组值所必需的功能:
CREATE TYPE custom_type (txt text, i int); CREATE FUNCTION fct\_using\_udt ( somearg int ) RETURNS NULL ON NULL INPUT RETURNS custom_type LANGUAGE java AS $$ UDTValue udt = udfContext.newReturnUDTValue(); udt.setString("txt", "some string"); udt.setInt("i", 42); return udt; $$;
UDFContext接口的定义可以在org.apache.cassandra.cql3.functions.UDFContext的Apache Cassandra源代码中找到。
public interface UDFContext { UDTValue newArgUDTValue(String argName); UDTValue newArgUDTValue(int argNum); UDTValue newReturnUDTValue(); UDTValue newUDTValue(String udtName); TupleValue newArgTupleValue(String argName); TupleValue newArgTupleValue(int argNum); TupleValue newReturnTupleValue(); TupleValue newTupleValue(String cqlDefinition); }
Java UDF对已定义的公共接口和类已经有一些导入。这些导入是:
import java.nio.ByteBuffer; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.cassandra.cql3.functions.UDFContext; import com.datastax.driver.core.TypeCodec; import com.datastax.driver.core.TupleValue; import com.datastax.driver.core.UDTValue;
这些便利的导入不适用于UDF脚本。
创建函数
创建新的用户自定义函数使用CREATE FUNCTION语句:
create_function_statement ::= CREATE [ OR REPLACE ] FUNCTION [ IF NOT EXISTS]
function_name '(' arguments_declaration ')'
[ CALLED | RETURNS NULL ] ON NULL INPUT
RETURNS cql_type
LANGUAGE identifier
AS string
arguments_declaration ::= identifier cql_type ( ',' identifier cql_type )*
例如:
CREATE OR REPLACE FUNCTION somefunction(somearg int, anotherarg text, complexarg frozen<someUDT>, listarg list) RETURNS NULL ON NULL INPUT RETURNS text LANGUAGE java AS $$ // some Java code $$; CREATE FUNCTION IF NOT EXISTS akeyspace.fname(someArg int) CALLED ON NULL INPUT RETURNS text LANGUAGE java AS $$ // some Java code $$;
CREATE
FUNCTION与可选的OR REPLACE关键字创建一个函数或替换具有相同签名的现有函数。如果具有相同签名的函数已经存在,则无OR REPLACE的CREATE
FUNCTION将失败。
如果使用可选的IF NOT EXISTS关键字,则只有具有相同签名的另一个函数不存在时,才会创建函数。
OR REPLACE和IF NOT EXISTS不能一起使用。
必须为每个函数定义使用空值调用的行为。有两个选项:
RETURNS NULL ON NULL INPUT声明如果任何输入参数为null,函数将返回null。CALLED ON NULL INPUT声明函数将总是被执行。
函数签名
签名用于区分各个功能。签名包括:
- 完全限定的函数名 - 即keyspace加函数名
- 所有参数类型的连接列表
键空间名称,函数名称和参数类型受默认命名约定和大小写敏感度规则的约束。
函数属于一个键空间。如果在<function-name>中未指定键空间,则使用当前键空间(即使用USE语句指定的键空间)。无法在系统键空间中创建用户定义的函数。删除函数
删除函数使用DROP FUNCTION语句:
drop_function_statement ::= DROP FUNCTION [ IF EXISTS ]function_name[ '('arguments_signature')' ] arguments_signature ::=cql_type( ','cql_type)*
例如:
DROP FUNCTION myfunction; DROP FUNCTION mykeyspace.afunction; DROP FUNCTION afunction ( int ); DROP FUNCTION afunction ( text );
如果存在具有相同名称但具有不同签名(重载函数)的多个函数,则必须指定要删除的函数的参数类型(arguments_signature)。
使用DROP FUNCTION和可选的IF EXISTS关键字删除一个已存在的函数,如果该函数不存在也不会抛出异常。
聚合函数
聚合函数处理一组行。 它们接收每行的值,并为整个集合返回一个值。
如果正常列,标量函数,UDT字段,writetime或ttl与聚合函数一起选择,则为它们返回的值将是与查询匹配的第一行的值。
本地聚合函数
Count
count函数可用于计算查询返回的行。例:
SELECT COUNT (*) FROM plays; SELECT COUNT (1) FROM plays;
它也可以用于计数给定列的非空值:
SELECT COUNT (scores) FROM plays;
Max and Min
max和min函数可用于计算查询对给定列返回的最大值和最小值。例如:
SELECT MIN (players), MAX (players) FROM plays WHERE game = 'quake';
Sum
sum函数可用于对给定列的查询返回的所有值进行求和。例如:
SELECT SUM (players) FROM plays;
Avg
avg函数可用于计算查询为给定列返回的所有值的平均值。例如:
SELECT AVG (players) FROM plays;
用户自定义聚合函数
用户自定义的聚合允许创建自定义聚合函数。 聚合函数的常见示例为count,min和max。
每个聚合需要类型为STYPE的初始状态(INITCOND,默认为null)。状态函数的第一个参数必须具有类型STYPE。状态函数的其余参数必须与用户定义的聚合参数的类型匹配。状态函数对每一行调用一次,状态函数返回的值变为新的状态。在处理所有行之后,以最后状态值作为其参数来执行可选的FINALFUNC。
STYPE是强制的,以便能够区分可能的状态和/或finalfunction的重载版本(因为过载可以在聚合创建之后出现)。
用户定义的聚合可以在SELECT语句中使用。
用户定义聚合的完整工作示例(假定已使用USE语句选择了键空间):
CREATE OR REPLACE FUNCTION averageState(state tuple<int,bigint>, val int) CALLED ON NULL INPUT RETURNS tuple LANGUAGE java AS $$ if (val != null) { state.setInt(0, state.getInt(0)+1); state.setLong(1, state.getLong(1)+val.intValue()); } return state; $$; CREATE OR REPLACE FUNCTION averageFinal (state tuple<int,bigint>) CALLED ON NULL INPUT RETURNS double LANGUAGE java AS $$ double r = 0; if (state.getInt(0) == 0) return null; r = state.getLong(1); r /= state.getInt(0); return Double.valueOf(r); $$; CREATE OR REPLACE AGGREGATE average(int) SFUNC averageState STYPE tuple FINALFUNC averageFinal INITCOND (0, 0); CREATE TABLE atable ( pk int PRIMARY KEY, val int ); INSERT INTO atable (pk, val) VALUES (1,1); INSERT INTO atable (pk, val) VALUES (2,2); INSERT INTO atable (pk, val) VALUES (3,3); INSERT INTO atable (pk, val) VALUES (4,4); SELECT average(val) FROM atable;
创建聚合函数
创建(或替换)用户定义的聚合函数使用CREATE AGGREGATE语句:
create_aggregate_statement ::= CREATE [ OR REPLACE ] AGGREGATE [ IF NOT EXISTS ]
function_name '(' arguments_signature ')'
SFUNC function_name
STYPE cql_type
[ FINALFUNC function_name ]
[ INITCOND term ]
参见上面的一个完整的例子。
使用可选的OR
REPLACE关键字创建AGGREGATE可创建聚合或替换具有相同签名的现有聚合。如果具有相同签名的聚合已存在,则无OR REPLACE的CREATE
AGGREGATE将失败。
使用可选的IF
NOT EXISTS关键字创建AGGREGATE可以创建聚合(如果它不存在)。
OR REPLACE和IF NOT EXISTS不能一起使用。
STYPE定义状态值的类型,必须指定。
可选的INITCOND定义聚合的初始状态值。它默认为null。必须为使用RETURNS NULL ON NULL INPUT声明的状态函数指定非空INITCOND。
SFUNC引用现有函数作为状态修改函数。状态函数的第一个参数的类型必须与STYPE匹配。状态函数的其余参数类型必须与聚合函数的参数类型相匹配。对于使用RETURNS
NULL ON NULL INPUT声明并且使用null调用的状态函数,不会更新状态。
可选的FINALFUNC在返回聚合结果之前调用。它必须只有一个类型为STYPE的参数。FINALFUNC的返回类型可以是不同类型。使用RETURNS NULL ON NULL INPUT声明的最终函数意味着如果最后一个状态为null,则聚合的返回值将为null。
如果未定义FINALFUNC,则聚合函数的返回类型总为STYPE。如果定义了FINALFUNC,则是该函数的返回类型。
删除聚合函数
删除用户定义的聚合函数使用DROP AGGREGATE语句:
drop_aggregate_statement ::= DROP AGGREGATE [ IF EXISTS ]function_name[ '('arguments_signature')' ]
例如:
DROP AGGREGATE myAggregate; DROP AGGREGATE myKeyspace.anAggregate; DROP AGGREGATE someAggregate ( int ); DROP AGGREGATE someAggregate ( text );
DROP AGGREGATE语句删除使用CREATE AGGREGATE创建的聚合。如果有多个具有相同名称但具有不同签名的聚合(重载聚合),则必须指定要删除的聚合的参数类型。
DROP AGGREGATE使用可选的IF EXISTS关键字删除聚合(如果存在),如果具有签名的函数不存在,则不执行任何操作。