导入和导出操作
xSQL 提供了多种备份选项,以特定命令的形式来备份数据库或保存快照,以及保存数据库元数据和字典。这些操作也可以通过以下 SQL 选择语句中的函数调用执行:
select backup( <dbsFile-path>, <logFile-path>, nTreads );
select save_snapshot( <imageFile-path> );
select save_metadata( <configFile-path> );
select save_class( <file-path>, <table-name> );
select load_class( <file-path>, <table-name> );
select save_dictionary( <schemaFile-path> );
还请注意,如果这些选择语句中的某一个由远程客户端执行,那么所有的文件名都指的是服务器的文件系统。
从外部文件导入数据
正如在“xSQL 数据导出和导入操作”页面中所解释的那样,使用 xSQL 导入命令从 CSV 文件导入外部数据可能很有用。也可以使用嵌入式 SmartESQL API 导入数据。以下各节描述了 SQL 语句的语法和实现。
数据文件格式
SmartESQL 支持从具有多种格式的外部文件中导入数据:
- 具有固定长度记录的文本文件——例如(来自历史纽约证券交易所报价):
N12182013 Record Count : 728040656
040000459PA 000000000000000000000000000000000000R PP000000000000001122 C
070109890TA 000003600000000001000000000000000000R TT000000000007250312 C
080000362KA 000005480000000002000006000000000002R KK000000000008758212 C
...
- 逗号分隔的可变长度记录 - 例如(来自 CME 交易):
实施
数据通过使用“外部表”概念加载到数据库中:文件中的数据被视为 SQL 数据集,并在 SQL 语句中使用。支持可选的数据转换,例如重新映射列,并且数据库架构不必精确反映 CSV 数据布局。SmartESQL 允许在运行时指定数据库布局(请参阅动态 DDL)。从 CSV 文件加载数据并将其转换为各种内部布局的能力在从不同的市场数据提供商(如纽约证券交易所 TAQ、路透社等)导入历史市场数据时特别有用。
SmartESQL 引擎将 CSV 文件表示为外部虚拟(或外键)表。由文件内容填充的数据库表被称为目标表。用于读取目标表布局的数据库表被称为模式表。需要注意的是,外键表并非实体化,而只是创建了一个通道,通过该通道数据从输入文本文件流入数据库,并在运行时按照应用程序所需格式进行格式化。外键表可以通过提供输入文件名作为表名以及通过现有的模式表提供目标表布局的 SQL 语句隐式定义,例如:
外部表参数如下:
- 路径:输入文件路径
- 分隔符:可以用来替代逗号的字符分隔符。*
- 跳过:可选地从文件开头跳过 N 条记录(默认值为 0)
- 忽略错误:可选地忽略错误,包括嵌入的“\n”(默认值为 0)
- 空值表示:空值的表示形式
注意,如果未显式指定分隔符字符,则当跳过参数设置为零(意味着 CSV 文件没有标题)时,分隔符将是逗号;否则(即跳过参数大于零),数据库运行时会读取输入文件的第一行,并查找以下字符串中的第一个字符用作分隔符字符:“; , | \t”。在输入流中遇到的第一个此类字符将用作后续数据的分隔符。以下是一些示例输入字符串,解释它们将如何被解释:
A;B,C;D|E - the ';' will be used as delimiter, there are three fields: 'A', 'B,C' and 'D|E'
A,B,C;D|E - the ';' will be used as delimiter, there are three fields: 'A', 'B' and 'C;D|E'
A|B,C;D|E - the '|' will be used as delimiter, there are three fields: 'A', 'B,C;D' and 'E'
在上述示例插入语句中,数据从 /some_directory/file.csv 文件(外部表)加载,并插入到目标表 TargetTable 中。模式表 PatternTable 用于指定数据库目标表的布局,即其列名和数据类型。在导入操作之前,目标表 TargetTable 和模式表 PatternTable 都必须存在于数据库中。该语句不会修改模式表。 如果需要,目标表和模式表都可以引用同一个数据库表(但并非必须如此)。例如:
insert into TAQTrade select * from foreign table (path='taqtrade20141030', skip=1, nostop=1)
as TAQTrade;
这里“taqtrade20141030”是输入文件,而 TAQTrade 用于定义数据布局和目标数据库表。
导入具有固定大小记录的文件
为了导入具有固定长度记录的文件,模式表字段必须定义为固定长度字符类型(CHAR (N) ),其大小由输入文件格式定义。例如:
create table TAQTradeIN (
tTime char(9), -- Time of trade with milliseconds (HHMMSSXXX)
Exchange char(1), -- The Exchange where the trade took place
Symbol char(16), -- Security symbol (six-character root, ten- character suffix)
SaleCondition char(4), -- A Sale Condition (applies to all exchanges)
TradeVolume char(9), -- The total volume of shares traded in the trade line
TradePrice char(11), -- The price of the trade
TradeStopStock char(1), -- This value indicates that this Trade (NYSE only)
TradeCorrectionIndicator char(2), -- Trade Correction Indications
TradeSequenceNumber char(16),
SourceofTrade char(1),
TradeReportingFacility char(1)
);
如果应用程序需要进行转换,例如添加用户定义的字段、删除输入文件中存在的某些字段等,则应明确指定目标表的列。例如,对于上述的 TAQTradeIN 固定大小的外部表,我们可以将输入数据转换为如下定义的 TAQTrade 表:
create table TAQTrade (
tTime int, -- Time of trade with milliseconds (HHMMSSXXX)
Exchange char(1), -- The Exchange where the trade took place
Symbol char(16), -- Security symbol
SaleCondition char(4), -- A Sale Condition
TradeVolume int, -- The total volume of shares traded
TradePrice DECIMAL(11,4), -- The price of the trade
TradeStopStock char(1), -- Indicates a Stop Stock
TradeCorrectionIndicator char(2), -- Trade Correction Indications
TradeSequenceNumber int,
SourceofTrade char(1),
TradeReportingFacility char(1)
);
执行此导入和转换的插入语句将如下所示:
insert into TAQTrade(tTime, Exchange, Symbol, SaleCondition,
TradeVolume, TradePrice, TradeStopStock,
TradeCorrectionIndicator,
TradeSequenceNumber, SourceofTrade,
TradeReportingFacility )
select int(tTime), Exchange, Symbol, SaleCondition, int(TradeVolume),
cast((substr(TradePrice, 1, 7) || '.' ||
substr(TradePrice, 8,4)) as decimal),
TradeStopStock, TradeCorrectionIndicator,
int(TradeSequenceNumber),
SourceofTrade, TradeReportingFacility
from foreign table (path='taqtrade20141030', skip=2,nostop=1)
as TAQTradeIN;
将文件导入垂直(序列)表示形式
如果要导入的数据要插入到序列类型的字段中,那么必须使用 INSERT OR UPDATE 语句。例如:
INSERT OR UPDATE INTO TOBSeq (Book, SecurityID,EntryType,
TradeDateTime, SeqNum, GroupCode, ProductCode, I
nstrumentName, EntryPrice,
EntryQuantity, AggressorSide,TickDirection)
SELECT Book, SecurityID, EntryType, cnvtime(TradeDate, TradeTime),
SeqNum, GroupCode, ProductCode, InstrumentName,
EntryPrice, EntryQuantity, AggressorSide, TickDirection
FROM foreign table(path='tob.csv', delimiter=',', skip=2,nostop=1)
as FlatTOB;
导入自动编号字段
根据定义,自动编号是由数据库生成的计数器,无法导入。如果源外键表(CSV 文件等)包含自动编号列,则其值将被忽略。目标表的自动编号值由数据库运行时生成。
一些注意事项
以下几点需要留意:
- 请记住,输入文件是在单个事务中加载的,而且输入文件可能相当大。REDO_LOG 事务模式(通常是默认模式)要求整个事务都保存在页面池中。这使得在 REDO_LOG 模式下无法加载超过页面池大小的文件。
- 如果使用 UNDO_LOG 模式,则数据库内存设备应足够大以保存事务列表。
- 在 UNDO_LOG 模式下,I/O 量相当大——大约是 REDO_LOG 模式下 I/O 量的两倍。为了缓解这一问题,建议使用以下 xSQL 配置文件定义:
db_params: {
db_log_type: undo,
log_params: {
default_commit_policy : no_sync
},
mode_mask : bulk_write_modified_pages
}
批量插入
批量插入可以显著提高具有单个索引的磁盘对象的插入和查询速度。通过部分排序插入对象,B 树页的引用局部性在插入期间会显著增加,从而使搜索操作更快,因为数据大多按索引顺序存储。
当从 CSV 文件或另一表插入数据时,行的物理存储顺序通常与索引定义的逻辑顺序不匹配。这导致数据库运行时在插入过程中需要频繁读取和重新排序数据,影响插入速度,并且检索时需要访问分散的页面。
批量插入通过按索引顺序存储数据,提高了引用局部性,使搜索结果集中的记录在磁盘上相邻存储,从而加快了查找速度。
C++ API 中,SqlOptimizerParameters 类的 batchInsertBufferSize 字段控制批量插入缓冲区大小,需在创建 SQL 引擎前设置(通过 engine::open(database) 方法)。将此值设为零可禁用批量插入。xSQL 也支持通过 sql_optimizer_params 配置文件选项进行批量插入。
要使用批量插入,需从 CSV 文件或其他源表通过“插入选择”语句将数据插入到持久表中。
insert into Quote
select * from foreign table (path='IBM-q1-2013.csv', skip=1 )
as Quote;
或
insert into Quote (symbol,date, time, price )
select symbol, date, time, price
from foreign table (path='IBM-q1-2013.csv', skip=1)
as Quote;
请注意,批处理插入算法所使用的索引必须是 B 树索引。另外,批处理插入算法会利用为目标表创建的所有索引列表中的第一个 B 树索引来预先对输入数据流进行排序。例如,如果创建了多个索引,如下所示:
create table Quote (symbol varchar, date int, time int, price numeric);
create index Quote.MyIndex on Quote (symbol, data, time);
create index Quote.MyOtherIndex on Quote (symbol, price);
这里使用的是 MyIndex 索引。使用以下模式文件也会得到相同的结果:
class Quote {
string symbol;
uint4 date;
uint4 time;
float price;
tree<symbol, date, time> MyIndex;
tree<symbol, price> MyOtherIndex;
};
请注意,批量插入算法不会考虑表中定义的任何自动标识符、列表索引或任何哈希索引。根据定义,自动标识符是由数据库生成的计数器,无法导入。如果源外部表(CSV 文件等)包含自动标识符列,则其值将被忽略。目标表的自动标识符值由数据库运行时生成。
空值
在从 CSV 文件加载数据时,有时需要定义空字符串。空值的字符串表示形式为 nullstr。默认情况下,空字符串由空字符串表示,即 ""。但应用程序可以显式定义空值。例如:
INSERT INTO T select * from foreign table (path=/my.csv',nullstr='NULL',skip=0) as T;
示例
请参阅 SDK 示例 xSQL 导入,以获取从外部文件导入证券交易数据的实际示例。