文档库 最新最全的文档下载
当前位置:文档库 › eXtremeSQL用户指南

eXtremeSQL用户指南

eXtremeSQL用户指南
eXtremeSQL用户指南

eXtremeSQL查询优化

对于执行SQL语句建立一个最优计划是很复杂且有挑战性工作。SQL优化器分析SQL查询发送到数据库,并选择最好的搜索访问数据库的策略。

有两个SQL优化类:基于开销和基于规则。基于磁盘的数据库通常是用基于开销优化。使用基于开销优化,查询优化很依赖数据的分布。通常情况下采用优化器提供的样板和由数据库引擎提供的统计数据,并收集统计信息自身,去计算执行计划的后选开销。因此,建立最优的计划是一个非常占用CPU的操作和本质上不可预测,在优化程序所花费的时间各不相同的查询来查询和执行计划可以改变从一个调用到另一个数据分布变化,CPU使用率被认为是一个很好的权衡,由于diskaccess成本相对较高。

对于实时和嵌入式系统可预知的性能是关键,一个基于规则的优化器,像eXtremeSQL使用是更适合地。此外,eXtremeSQL优化器使得应用程序可以专注它们自己的执行计划。例如,优化器没有重新排序表中查询:查询中指定的顺序表被执行。其中一些可用于查询中使用的其他重要规则优化包括:

@如果可能,使用索引.

@每个表都被分配一个序号,代表从它在列表中的位置。

@搜索意味着分成列和一组列来排序,因此首先表达式用最小序号访问表来检查。

@子查询执行优化是通过检查子查询的依赖表达.子查询的结果被保存并且仅当子查询表达从封闭范围引用字段时重新计算。

对于eXtremeSQL中更多的关于查询优化请见“章节4:eXtremeSQL查询优化”。

章节2:SQL和eXtremeSQL

存贮分配器

对McoSqlEngine,默认动态分配器。对于多线程McoSqlEngine,McoSql::MultithreadedAllocator是默认的。它们之间的区别是McoSql::MultithreadedAllocator 为第一个线程保持独立的存贮器段。这个讨论的其它部分参考多线程动态分配器的应用。动态分配器使用标准(除非覆盖)malloc/free运行时函数分配存贮器段。段的大小可以由程序员指定,默认值为1MB。此段被视为一个堆栈,因此分配的空间是以LIFO次序。当现行段耗尽(所有空间已被安排),一个新的段被分配并且分配器使用这新的段。所有段链接成链表。当段变成空时将被释放(freed)而分配返回指向前一段。

动态分配器不使用eXtremaDB存贮池。但它可能使用mco_malloc()和mco_free()函数安排段而由动态分配器派生一个类。分配器获取的内存数量依赖每一个SQL语句。视需要可以是几K到几M字节(许多内存被用于必要的选择大量的记录处理问题,例如排序)。MemoryManager::allocator分配器指针用于eXtremeSQL所需的每个内存的分配。eXtremeSQL也实现了所谓静态分配器。

章节3:eXtremeSQL编程接口

范例放置在extremesql/samples/sql中.

eXtremeSQL提供了许多扩展的SQL - 89兼容的查询语言:

.数组

.结构

.引用

.支持所有C++内建类型

.自定义函数

也有一些与ANSI标准不兼容:

.空值分配到一些类型的列不支持。

.在运行时改变数据库模式(添加表,添加索引,...)不支持。

.标识符是大小写敏感的(SQL关键字是大小写不敏感)。

.在SELECT语句的FROM子句只能参考表,不能包括嵌套选择。

.视图不支持

.约束不是由SQL接口本身强制的,而是由底层在eXtremeDB运行时强制的。

.RIGHT OUTER JOIN和FULL OUTER JOIN没有实现。

.不支持触发,但是如果eXtremeSQL活动产生这样一个事件,eXtremeSQL的底层将引起事件通知。

类型映射

提取和存储数据

eXtremeSQL中字符串

所有操作都适用于数组也适用于字符串,这也有自己的一套运作。例如,字符串使用标准关系运算符可以相互比较。

像结构可用于匹配一个字符串中包含特殊模式通配符'%'和'_'。字符'_'匹配任何单个字符,而字符'%'匹配任何数目的字符(包括0)。扩展形式像带转义部分的操作符可用于以正常模式字符处理字符'%'和'_'只有当它们由特殊的转义字符,它是指定转义关键字之后的字符。例如

select * from T where name like '#%x%' escape '#'

将选择名字字段打头为“%x.”的所有记录。

使用in操作符搜索字串内的一个子字串,这样的表达式'blue' in color,对于color字段的所有记录中包含字串'blue'的将为真。

例如

select * from car where 'blue' in color;

将选择颜色为“暗蓝”,“蓝”,“蓝白”,“亮蓝”。。。的车。

字符串可以串联使用+或| |操作符(+和| |可以使用交替)。最后操作符只添加了符合ANSI SQL 兼容性标准。eXtremeSQL不支持隐式转换为字符串类型的表达式,所以操作符+的语义已被重新定义为字符串。换句话说,在许多SQL实现它可以编写:

1+'1'

其结果将是2(在这里进行隐式转换是从字符串到整数)。

1||'1'

其结果将是'11'.

eXtremeSQL不允许从字符串隐式转换,所以1+'1'将是'11',结果同为1||'1'。

string literal

该StringLiteral的构造器有一个大小为1的“chars”成员变量。这说明标准分配一个结构带可变的大小。当这个结构分配,它是需要添加的固定和对象可变部分的大小。在C++,它可以通过使用一个额外的new操作符。然而,这个操作符不是预定义的,你将需要自己确定它(因为它是在DynamicObject类一样):

inline void* operator new(size_t fixed, size_t var)

{

return ::new char[fixed + var];

}

现在,string literal可以以下方式创建:

char const* str = "Hello World";

size_t length = strlen(str);

Value* value = ::new (length) StringLiteral(str, length);

有两个方案选择:

1。使用StringRef类的构造函数,给出的一个指针,它是字符串程序员的责任去分配时,不需要任何字符串更长。

2。定义你自己的类扩展String类:

class UserString : public String

{

public:

virtual int size();

virtual char* body();

virtual char* cstr();

private:

const int length;

char* chars;

public:

UserString(char const* s, int l);

~UserString();

};

int UserString::size()

{

return length;

}

UserString::UserString(char const* s, int l) : length(l)

{

chars = new char[l+1];

memcpy(chars, s, l);

chars[l] = '\0';

}

UserString::~UserString()

{

delete[] chars;

}

char* UserString::body()

{

return chars;

}

char* UserString::cstr()

{

return chars;

}

eXtremeSQL引用及使用情况

在eXtremeSQL中由AUTOID快速和直接访问到记录可以使用引用。引用字段可以被索引,也可以用在ORDER BY子句中。引用可以被间接引用使用与访问结构成员相同的点符号。例如,如果有下述数据库模式:

class Address

{

string city;

autoid[1000];

hash city_index[1000];

};

class Company

{

autoid_t

address;

autoid[10000];

hash

address_index[10000];

};

class Contract

{

autoid_t company;

autoid[10000];

hash company_index[10000];

};

那么下面的查询语句:

select * from Contract where company.address.city = 'Chicago';

将检索Contract记录中引用Company,Company引用Address带city字段等于的‘Chicago’的位置。此请求的查询执行计划如下:

1。使用city_index(city = ‘Chicago’)执行一个Address表的索引搜索

2。对于所有选定的Address记录,使用address_index找到引用这些地址的Company记录3。对于所有选定的Company记录中,使用company_index找到引用这些Company记录的Contract记录

引用可以被检查为空由是空或不空判断。他们还可以相互间比较相等,以特别null关键字。当空的引用是间接引用,eXtremeSQL引发异常的。

数组和它们的使用

eXtremeSQL接受eXtremeDB数组(固定长度)和向量(可变长度)作为记录的成员。当用eXtremeDB,不支持多维数组。eXtremeSQL提供一组专门的结构来处理数组和向量(以下统称为‘array’):

1。可以在数组中使用length()函数来获取元素的大小。

2。数组元素可以使用[]操作符来检索。如果索引表达式超出了数组范围,将产生一个异常。3。操作符in可用于检查是否由左操作数指定一个数组所包含的内容。这个操作仅用于数组的原子类型:与boolean,numeric, reference或string组成部分。

4。通过数组元素的迭代操作由exists操作符实现。exists关键之后的指定变量用于在数组中的一个索引为表达式加上exists前言quantor。这个索引变量将迭代所有数组索引值,直到表达式的值为真或索引超出数组的范围。

对于例子,给出如下数据库模式:

class Company {

string location;

autoid;

};

class Contract {

autoid_t company;

unsigned<8> quantity;

date delivery;

autoid;

};

class Detail {

string name;

vector< autoid_t > contract;

};

查询:

select * from Detail where exists i:(contract[i].company.location = 'US');

选择公司在美国的所有Detail记录。

当查询:

not exists i: (contract[i].company.location = 'US')

选择公司除美国外的所有Detail记录。

向量和数组

eXtremeSQL, 不像eXtremeDB,不区分向量和数组类型。两种类型用eXtremeSQL的tpArray 类型表达。tpList类型用于在SQL中建立List的内部类型:

SELECT * from T where code in (1, 2, 10);

在这种情况下(1, 2, 10)内在表示为tpList的一个实例。

存储二进制数据与零

在eXtremeSQL中你可以存贮包含0的二进制数据。要插入二进制数据嵌入式零,并检索与利用eXtremeSQL嵌入式零,看到这个例子

class t1

{

signed<4> n;

signed<4> o;

string s;

signed<1> b[16];

list;

};

在这种情况下字段的类型将是一个数组并且你可以使用Array::getBody()方法去获取数组的内容。

函数

下表列出eXtremeSQL的内建函数:

其它eXtremeSQL支持的函数

装载和保存

McoSqlEngine::open()方法最后选择的参数是一个数据库文件的路径,从该数据库应该被加载。默认,这个参数的值是空且数据库不从磁盘装载。如果参数不为空,那么open()方法尝试从指定文件加载数据库并初始化,如果文件不存在则为空数据库。也有McoSqlDatabase::save()方法以指定文件保存一个数据库。

这样,对于从先前保存在一个文件中的映像加载一个数据库,你必须指定open方法的最后参数,对于关闭数据库之前保存数据库映像,是数据库关闭之前插入调用save()方法。基于函数的分组依据

可以SELECT语句的子句以组使用eXtremeSQL内建函数和用户定义函数。这里是使用内建函数的例子:

class events

{

autoid;

int datetimestamp; // Number of seconds since epoch自世纪始的秒数

u_char event_name;

};

"Select event_name, date_format(datetimestamp, "YYYY-MM") from events

group by date_format(datetimestamp, "YYYY-MM");

这里是定义和使用用户定义函数的例子:

// here is the body of the UDF:用户定义函数体

static Value* mod(Value* a, Value* b)

{

if (a->isNull() || b->isNull())

{

return NULL;

}

return new IntValue(a->intValue() % b->intValue());

}

// below, f1 is an instance of the SqlFunctionDeclaration class. The

// constructor links this declaration of a UDF named “mod” to the list

// of all UDFs maintained internally by eXtremeSQL:

下面,f1是一个SqlFunctionDeclaration类的一个实例,构造函数连接用户命名为“mod”的这个声明与由eXtremeSQL内部维护的所有用户定义的表

static SqlFunctionDeclaration f1(

tpInt, // tpInt is the return value of the UDF tpInt是用户定义的返回值

"mod", // the name of the function as we’ll use it in a query 我们将使用查询函数的名字(void*)mod, // the function pointer函数指针

2 // the number of arguments to the UDF用户定义函数的参数个数

);

// below, we use the UDF …mod? that was setup by the steps above:

select * from T where mod(code,3) = 0;

这个请求选择字段的值为名code被3整除。

C结构

有一个用C结构提取和存贮数据的机制。首先,对应于在eXtremeDB定义文件(MCO文件)中类的定义去定义一个C结构然后使用一个操作从数据库提取一个类或表的数据.它像这样: MyRecord r;

QueryResult result(engine.executeQuery("select * from MyRecord where

i4=%i", i));

Cursor* cursor = result->records();

Record* rec = cursor->next();

result->extract(rec, &r, sizeof(r));

可以插入整个类(表):

MyRecord r;

r.i4 = i;

... // assign the rest of MyRecord r fields

engine.executeStatement("insert into MyRecord %r", &r);

目前,模式编译器不会生成与类/表定义相应的C结构体,你必须自己做这个定义,下表指定的C结构类型为用于每个eXtremeDB数据库字段类型:

来自C应用程序初始化/关闭

此示例代码来自eXtremeSQL SDK的samples/sql/ctest目录:

{

database_t engine;

mco_db_h dbh;

mco_runtime_start();

/* Create a database*/建立数据库

mco_db_open(DATABASE_NAME, ctestdb_get_dictionary(), start_addr,DATABASE_SIZE, PAGE_SIZE));

/* connect to the database, obtain a database handle */连接数据库,获取数据库句柄

mco_db_connect(DA TABASE_NAME, &dbh);

/* Set memory allocator */设置存贮分配器

mcosql_initialize_dynamic_memory_manager(&malloc, &free,ALLOC_QUANTUM, ALLOC_RETAIN);

/* Initialize eXtremeDB SQL mapper */初始化eXtremeDB SQL变换器

mcoapi_initialize(dbh, ctestdb_get_dictionary() );

/* Open QSL engine */打开SQL引擎

mcosql_open(&engine);

/* Work with database */数据库操作

do_test(engine);

/* Close SQL engine */关闭SQL引擎

mcosql_close(engine);

/* Close eXtremeDB database */关闭eXtremeDB数据库

mco_db_close(DATABASE_NAME);

/* shutdown database engine */关闭数据库引擎

mco_runtime_stop();

}

注意,应该传递引擎句柄的地址给mcosql_open(),不是值,即。,mcosql_open (&engine);而不用mcosql_open (engine);

来自C++应用程序初始化/关闭

初始化

{

McoSqlEngine engine;

engine.open("testperfdb", testperfdb_get_dictionary(),DATABASE_SIZE, PAGE_SIZE);

}

McoSqlEngine::open原型是:

open(char const* name,mco_dictionary_h dictionary,size_t size,size_t pageSize = 128,void* mapAddress = (void*)0x20000000,size_t maxTransSize = 0,

int flags = ALLOCATE_MEMORY|SET_ERROR_HANDLER|START_MCO_RUNTIME|SET_SQL_ALL OCATOR|INITIALIZE_DATABASE,char const* databaseFile = NULL);

内在的,McoSqlEngine::open()所做如下:

1。检查本地或共享内存的支持。

2。如果是本内存且(标志与ALLOCA TE_MEMORY)是真,那么调用malloc()去分配DA TABASE_SIZE字节。

3。设置eXtremeDB错误处理到McoDatabase::checkStatus.

4。初始化eXtremeDB运行时。

5。设置McoSQL存贮分配器。

6。设置最大事务大小。

7。连接或操作eXtremeDB数据库。

8。打开SQL引擎。

附注内存分配器

。对于McoSqlEngine,默认使用类的DynamicAllocator。对于McoMultithreadedSqlEngine,默认使用McoSql::MultithreadedAllocator。它们二者的不同是McoSql::MultithreadedAllocator为每一线程维护独立的存贮器段.对于这个讨论的其它部分,涉及到DynamicAllocator的地方也适用于MultiThreadedAllocator.

。DynamicAllocator使用标准(除非被覆盖)的malloc/free运行时函数来分配存贮器段。这个段的大小可以由编程人员指定;默认为1MB。这个段被处理为堆,因此SQL分配空间是以LIFO次序。当现行段被耗尽(所有的空间已分配完),新的一个段被分配并且分配器使用这新的段.所有的段连接成链表。当段变空时它们被释放(freed)并且分配器返回到前一个段。

DynamicAllocator不使用eXtremeDB存贮池。但它可能从DynamicAllocator得到一个类,这个是用mco_malloc() 和mco_free()函数去分配的段。

分配器抢占的存贮器数量依每个SQL语句而定。要求可能从几千字节到几兆字节不同(那么多内存可以被需要处理的查询选择大量的记录,并举例来说,排序)。MemoryManager::allocator分配器指针用于eXtremeSQL所需的每一个存贮器分配。eXtremeSQL库也实现一个StaticAllocator调用。对比DynamicAllocator抢占新的段,当必须并维护它们的列表时,StaticAllocator是给出永远不会延长的存贮池。

通过调用MemoryManager::setAllocator()应用程序安装这个分配器:MemoryManager::setAllocator(new StaticAllocator(mem, memSize));

然后调用不带SET_SQL_ALLOCATOR标志的McoSqlEngine.open() MemoryManager::setAllocator()是静态方法,你不需要有这个类的实例。可以如上例调用它。关闭

{

engine.close();

}

内在的,McoSqlEngine::close()所做如下:

1。释放存贮器分配器。

2。关闭数据库(mco_db_disconnect() & mco_db_close()).

3。如果是初始化分配的存贮器就释放它。

4。结束eXtremeDB运行时。

第6章SQL元数据

数据库模式定义使用eXtremeDB私有数据定义语言(DDL)或eXtremeSQL‘建立表’句法两者之一处理经由一个模式编译器(各自为mcocomp或sql2mco),除其他事项外,在产生的dbname.c文件中建立一个二进制形式的模式。这个二进制形式的模式被称为数据库字典。eXtremeSQL的3.1版开始,数据库字典可以通过一个称元数据表的系统表来质询,这个表的定

tpNull,

tpBool,

tpInt1,

tpUInt1,

tpInt2,

tpUInt2,

tpInt4,

tpUInt4,

tpInt8,

tpUInt8,

tpReal4,

tpReal8,

tpDateTime, // time_t

tpUnicode,

tpString,

tpRaw,

tpReference,

tpArray,

tpStruct,

tpBlob,

tpDataSource,

tpList,

tpInt = tpInt8,

tpReal = tpReal8,

tpLast

};

下面从Metatable提取数据库字典信息的质询例子。

1。列出所有包含字段"foo"的所有表:

SELECT DISTINCT TableName FROM Metatable WHERE FieldName='foo';

2.获得一个类(表)所有字段(列)名的列表:

SELECT FieldName FROM Metatable WHERE TableName='abc' ORDER BY FieldNo; 3.获得数据库中所有类(表)的列表

SELECT DISTINCT TableName FROM Metatable;

4.获得指定表不包括自动生成字段的的所有字段的列表:

SELECT * FROM Metatable WHERE TableName='abc' AND NOT AutoGenerated ORDER BY FieldNo;

5.提取所有表和字段的引用指定表:

SELECT TableName, FieldName FROM Metatable WHERE ReferencedTable='xyz';

相关文档