CQL(Cassandra Query Language)数据类型

CQL是一种类型化语言,支持丰富的数据类型集,包括本地类型(native_types),集合类型(collection_type),用户定义类型(user_defined_type),元组类型(tuple_type)和自定义类型(custon_type):
cql_type ::=  native_type | collection_type | user_defined_type | tuple_type | custom_type


1.本地类型

CQL支持的本地类型有:

native_type ::=  ASCII
                 | BIGINT
                 | BLOB
                 | BOOLEAN
                 | COUNTER
                 | DATE
                 | DECIMAL
                 | DOUBLE
                 | FLOAT
                 | INET
                 | INT
                 | SMALLINT
                 | TEXT
                 | TIME
                 | TIMESTAMP
                 | TIMEUUID
                 | TINYINT
                 | UUID
                 | VARCHAR
                 | VARINT
下表提供了本地数据类型的其他信息,以及每种类型支持的常量类型:
type constants supported description
ascii string ASCII字符串
bigint integer 64位有符号长
blob blob 任意字节(无验证)
boolean boolean true或false
counter integer 计数器列(64位有符号值)
date integer,string 日期(没有相应的时间值)
decimal integer,float 可变精度十进制
double integerfloat 64位IEEE-754浮点
float integer,float 32位IEEE-754浮点
inet string IP地址,IPv4(4字节长)或IPv6(16字节长)。 注意,没有inet常量,IP地址应该作为字符串输入
int integer 32位有符号长
smallint integer 16位有符号长
text string UTF8编码字符串
time integer,string 一个具有纳秒精度的时间(没有相应的日期值)
timestamp integer,string 时间戳(日期和时间),精度为毫秒
timeuuid uuid 版本1 UUID,通常用作“无冲突”时间戳
tinyint integer 8位有符号长int
uuid uuid UUID(任何版本)
varchar string UTF8编码字符串
varint integer 任意精度整数


2.计数器

计数器类型用于定义计数器列。 计数器列是其值为64位有符号整数且支持2个操作的列:递增和递减(请参见语法的UPDATE语句)。 注意,不能设置计数器的值:在第一次递增/递减之前不存在计数器,并且第一次递增/递减被假定为先前值为0。
计数器有一些重要的限制

  • 不能够用于表中PRIMARY KEY的列部分
  • 包含计数器的表只能包含计数器数。 换句话说,表中处PRIMARY KEY外所有列都有计数器类型,或者没有计数器类型
  • 计数器不支持到期
  • 支持删除计数器,但只能保证在您第一次删除计数器时工作。 换句话说,您不应该重新更新您已删除的计数器(如果您这样做,不能保证正确的行为)
  • 计数器更新本质上不是同义的。一个重要的后果是,如果计数器更新意外失败(超时或失去与协调器节点的连接),客户端无法知道是否已应用更新。尤其是在重复更新操作可能导致过度计数

3.使用时间戳

时间戳类型的值被编码为64位有符号整数,表示标准基本时间(1970年1月1日00:00:00 GMT)以来的毫秒数。可以把时间戳的值作为整数或使用表示ISO 8601日期的字符串输入CQL使用。 例如,以下所有值都是2011年3月2日上午04:05:00 GMT的有效时间戳值:

  • 1299038700000
  • '2011-02-03 04:05+0000'
  • '2011-02-03 04:05:00+0000'
  • '2011-02-03 04:05:00.000+0000'
  • '2011-02-03T04:05+0000'
  • '2011-02-03T04:05:00+0000'
  • '2011-02-03T04:05:00.000+0000'
上面的+0000是RFC 822 4位时区规范; +0000指GMT。美国太平洋标准时间为-0800。当要需要省略时区('2011-02-03 04:05:00')的形式显示时间时,日期的默认时区将被解释为所处的Cassandra节点所对应的时区。然而依赖于时区配置的写法难度是固定而且可预期的,因此建议在可能的情况下始终为时间戳指定时区


4.使用日期

日期类型的值被编码为表示距离1970年1月1日的天数的32位无符号整数,其范围为2 ^ 31。
对于时间戳,日期可以作为整数或使用日期字符串输入。 在后一种情况下,格式应为yyyy-mm-dd(例如“2011-02-03”)。


5.使用时间

时间类型的值被编码为表示自午夜以来纳秒数的64位有符号整数。
对于时间戳,时间可以作为整数输入或使用表示时间的字符串输入。 在后一种情况下,格式应为hh:mm:ss [.fffffffff](其中亚秒精度是可选的,如果提供,可以小于纳秒)。 例如,以下是一段时间的有效输入:
  • '08:12:54'
  • '08:12:54.123'
  • '08:12:54.123456'
  • '08:12:54.123456789'


6.集合

CQL支持3种集合:Map,Set和List。这些集合的类型由以下定义:
collection_type ::=  MAP '<' cql_type ',' cql_type '>'
                     | SET '<' cql_type '>'
                     | LIST '<' cql_type '>'
并且可以使用字面量集合作为它们的输入值:
collection_literal ::=  map_literal | set_literal | list_literal
map_literal        ::=  '{' [ term ':' term (',' term : term)* ] '}'
set_literal        ::=  '{' [ term (',' term)* ] '}'
list_literal       ::=  '[' [ term (',' term)* ] ']'
请注意,在字面量集合中不支持bind_marker和NULL。


6-1.值得注意的特点

集合用于存储/反规范化数量相对较少的数据。 它们对于诸如“给定用户的电话号码”,“应用于电子邮件的标签”等的事物工作良好。但是当存储的数据无限增长的时候(“由用户发送的所有消息”,“由传感器注册的事件 “...),那么集合是不合适的,并且应该使用特定的表(具有聚类列)。 具体来说,(非冻结)集合具有以下值得注意的特征和限制:

  • 单个集合不在内部编制索引。 这意味着即使访问集合的单个元素,也必须被读取整个集合(并且读取一个元素不在内部被分页)。
  • 虽然对集合和映射的插入操作从来不会在内部产生先读后写,但是对列表的某些操作会执行。此外,一些列表操作本质上不是幂等性的,使得它们在超时问题的情况下重试。因此建议在可能的情况下选择使用集合而不使用列表。


6-2.Maps

映射是一组(排序)的键/值对,其中键是唯一的,并且按其键排序映射。您可以定义和插入map:

CREATE TABLE users (
    id text PRIMARY KEY,
    name text,
    favs map<text, text> // A map of text keys, and text values
);

INSERT INTO users (id, name, favs)
           VALUES ('jsmith', 'John Smith', { 'fruit' : 'Apple', 'band' : 'Beatles' });

// Replace the existing map entirely.
UPDATE users SET favs = { 'fruit' : 'Banana' } WHERE id = 'jsmith';

此外,map支持

  • 更新或插入一个或多个元素:       

UPDATE users SET favs['author'] = 'Ed Poe' WHERE id = 'jsmith';
UPDATE users SET favs = favs + { 'movie' : 'Cassablanca', 'band' : 'ZZ Top' } WHERE id = 'jsmith';

  • 删除一个或多个元素(如果元素不存在,则删除它是一个无操作,但不会抛出错误):

DELETE favs['author'] FROM users WHERE id = 'jsmith';
UPDATE users SET favs = favs - { 'movie', 'band'} WHERE id = 'jsmith';
最后,需要说明的是对于插入和更新操作都允许TTL,但是在这两种情况下,TTL集合仅适用于新插入/更新的元素。换一种说法下面的语句将仅仅只是把TTL应用于{'color':'green'}记录,其余的map不受影响:

UPDATE users USING TTL 10 SET favs['color'] = 'green' WHERE id = 'jsmith';

6-3.Sets

集合是唯一值的(排序)集合。您可以定义和插入set:

CREATE TABLE images (
    name text PRIMARY KEY,
    owner text,
    tags set<text> // A set of text values
);

INSERT INTO images (name, owner, tags)
            VALUES ('cat.jpg', 'jsmith', { 'pet', 'cute' });

// Replace the existing set entirely
UPDATE images SET tags = { 'kitten', 'cat', 'lol' } WHERE name = 'cat.jpg';

此外,set支持

  • 添加一个或多个元素(因为这是一个set,插入已经存在的元素是一个无操作):      
UPDATE images SET tags = tags + { 'gray', 'cuddly' } WHERE name = 'cat.jpg';
  • 删除一个或多个元素(如果元素不存在,则删除它是一个无操作,但不会抛出错误):
UPDATE images SET tags = tags - { 'cat' } WHERE name = 'cat.jpg';
最后,和map相同,TTL只适用于新插入的值。


6-4.Lists

注:由于列表具有限制和特定的性能注意事项,所以我们在使用列表之前需要先考虑这些影响。一般来说,如果集合和列表可以同时使用的情况下,总是喜欢一个集合。

列表是非唯一值的(排序)集合,其中元素按列表中的位置排序。您可以定义和插入一个list:

CREATE TABLE plays (
    id text PRIMARY KEY,
    game text,
    players int,
    scores list<int> // A list of integers
)

INSERT INTO plays (id, game, players, scores)
           VALUES ('123-afde', 'quake', 3, [17, 4, 2]);

// Replace the existing list entirely
UPDATE plays SET scores = [ 3, 9, 4] WHERE id = '123-afde';

此外,list支持

  • 将新值附加或者前置到list中:      
UPDATE plays SET players = 5, scores = scores + [ 14, 21 ] WHERE id = '123-afde';
UPDATE plays SET players = 6, scores = [ 3 ] + scores WHERE id = '123-afde';
  • 设置列表中特定位置的值。这意味着该列表具有用于该位置的预先存在的元素,否则将抛出该列表太小的错误:
UPDATE plays SET scores[1] = 7 WHERE id = '123-afde';
  • 删除元素在列表中的位置。这意味着列表具有用于该位置的预先存在的元素,否则将抛出该列表太小的错误。此外,当操作从列表中移除元素时,列表大小将减小1,移动在删除的元素之后的所有元素的位置:

DELETE scores[1] FROM plays WHERE id = '123-afde';
  • 删除列表中所有出现的特定值(如果某个元素在列表中根本不出现,则会被忽略,并且不会抛出任何错误):

UPDATE plays SET scores = scores - [ 12, 21 ] WHERE id = '123-afde';
警告:附加和前置操作本质上不是幂等的。 因此,如果操作超时,则重试操作是不安全并且它有可能导致附加/前置值两次。

警告:通过位置设置和移除元素并移除特定值会在内部产生先读后写。因此,它们将比常规的更新耗费更多的资源,运行得更慢。

最后,和map相同,TTL只适用于新插入的值。

7.用户自定义类型

CQL支持用户定义类型的定义(简称UDT)。可以使用下面描述的create_type_statement,alter_type_statement和drop_type_statement创建,修改和删除此类型。但是一旦创建,UDT就简单地用它的名字来引用:

user_defined_type ::=  udt_name
udt_name          ::=  [ keyspace_name '.' ] identifier
UDT有一个名称(用于该类型的声明列),并且是一组命名和类型的字段。字段名称可以是任何类型,包括集合或其他UDT。例如:

CREATE TYPE phone (
    country_code int,
    number text,
)

CREATE TYPE address (
    street text,
    city text,
    zip text,
    phones map<text, phone>
)

CREATE TABLE user (
    name text PRIMARY KEY,
    addresses map<text, frozen<address>>
)
注意:

  • 尝试创建已存在的类型将导致错误,除非使用IF NOT EXISTS选项。 如果使用它,如果类型已经存在,语句将是一个无操作。
  • 类型本质上绑定到创建它的键空间,并且只能在该键空间中使用。在创建时,如果类型名称前缀为键空间名称,则在该键空间中创建。否则,将在当前键空间中创建。
  • 从Cassandra 4.0开始,UDT必须在大多数情况下被冻结,因此在上面的表定义中冻结了

7-1.创建UDT

创建新的用户定义类型是使用以下定义的CREATE TYPE语句完成的:

create_type_statement ::=  CREATE TYPE [ IF NOT EXISTS ] udt_name
                               '(' field_definition ( ',' field_definition )* ')'
field_definition      ::=  identifier cql_type


7-2.UDT字面量

一旦创建了已定义的类型,可以使用UDT字面量作为输入值:

udt_literal ::=  '{' identifier ':' term ( ',' identifier ':' term )* '}'
换句话说,一个UDT字面量就像一个map字面量,但它的键是类型的字段的名称。例如,可以使用以下代码插入到上一节中的表定义中:

INSERT INTO user (name, addresses)
          VALUES ('z3 Pr3z1den7', {
              'home' : {
                  street: '1600 Pennsylvania Ave NW',
                  city: 'Washington',
                  zip: '20500',
                  phones: { 'cell' : { country_code: 1, number: '202 456-1111' },
                            'landline' : { country_code: 1, number: '...' } }
              },
              'work' : {
                  street: '1600 Pennsylvania Ave NW',
                  city: 'Washington',
                  zip: '20500',
                  phones: { 'fax' : { country_code: 1, number: '...' } }
              }
          })
为了有效,UDT字面量应该只包括由类型定义的字段,它是一个字面量,但它可以省略一些字段(在这种情况下,这些字段将为null)。


7-3.更改UDT

可以使用ALTER TYPE语句修改现有的用户定义类型

alter_type_statement    ::=  ALTER TYPE udt_name alter_type_modification
alter_type_modification ::=  ALTER identifier TYPE cql_type
                             | ADD field_definition
                             | RENAME identifier TO identifier ( identifier TO identifier )*
您可以:

  • 修改特定字段的类型(ALTER TYPE address ALTER zip TYPE bigint)。 此类更改的限制与更改列的类型相同。
  • 添加一个新字段到类型(ALTER TYPE address ADD country text)。对于在添加之前创建的类型的任何值,该新字段将为null。
  • 重命名类型的字段ALTER TYPE address RENAME zip TOzipcode


7-4.删除UDT

您可以使用DROP TYPE删除已存在的用户定义类型:

drop_type_statement ::=  DROP TYPE [ IF EXISTS ] udt_name
删除类型会立即产生结果,不可逆地删除该类型。 但是,尝试删除仍在由另一个类型,表或函数使用的类型将导致错误
如果类型丢失不存在,将返回错误,除非使用IF EXISTS,在这种情况下,操作是无操作。


8.元组

CQL还支持元组和元组类型(其中元素可以是不同类型的)。在功能上,元组可以作为匿名UDT与匿名字段。元组类型和元组文字定义如下:

tuple_type    ::=  TUPLE '<' cql_type ( ',' cql_type )* '>'
tuple_literal ::=  '(' term ( ',' term )* ')'
因此可以使用:

CREATE TABLE durations (
    event text,
    duration tuple<int, text>,
)

INSERT INTO durations (event, duration) VALUES ('ev1', (3, 'hours'));
与其他“组合”类型(集合和UDT)不同,元组总是被冻结(不需要freeze关键字),并且不可能只更新元组的一些元素(不更新整个元组)。另外,元组字面量应该总是具有与在类型中声明的值相同的值,其中的一些值可以是null,但是它们需要被明确声明为这样。


9.自定义类型

注:自定义类型主要用于向后兼容目的,并且不建议使用它们。它们的使用复杂的而且不友好,此外已提供的其他类型,特别是用户定义的类型,已经足够应付几乎所有的场景。

自定义类型定义为:

custom_type ::=  string
自定义类型是一个字符串,它包含继承自服务器端抽象类类的Java类的名称,并且可以由Cassandra加载(因此应该在运行Cassandra的每个节点的CLASSPATH中)。该类将定义什么值对类型有效,以及当用于聚类列时如何排序。处于一些其它目的,自定义类型的值与blob的值相同,并且可以使用blob字面量语法输入。



你可能感兴趣的:(--Cassandra,cassandra,Cassandra,Query,Lang,CQL数据类型)