第一部分
我们继续创建简单的证券交易所模拟器。这是我们要做的:
- 让我们创建一个数据库组织图。
- 我们将描述它存储的内容、方式和位置。
- 让我们看看数据是如何相互关联的。
- 让我们以 SQL 表创建命令CREATE TABLE(SQL 语言的数据定义语言 ( DDL ))为例开始学习 SQL 的基础知识 。
- 让我们继续编写Java程序。我们使用 JDBC 和三层架构以编程方式创建数据库的 java.sql 来实现 DBMS 的主要功能。
这两部分内容更加冗长,因为我们需要从内部熟悉 SQL 的基础知识和 DBMS 的组织,并与 Java 进行类比。为了不让代码清单让您感到厌烦,最后有该程序对应的提交 github 存储库的链接。
数据库管理系统设计
应用说明
您已经听说组织数据存储是编程的一个组成部分。让我提醒您,我们应用程序的目的是最简单的交换模拟:
- 股票的价值在交易日内可以按照规定的规则发生变化;
- 有初始资金的交易者;
- 交易者可以根据他们的算法买卖股票。
交易
所以固定时间段(在我们的例子中为 1 分钟)运行。在价格变动期间,股票价格可能会发生变化,然后交易者可以买入或卖出股票。
交换仿真数据结构
我们将其称为单独的交换实体模型。为了避免舍入错误,我们将通过一个类来处理财务金额
BigDecimal
(详细信息可以在文章末尾的链接中找到)。让我们更详细地描述每个模型的结构:
促销:
属性 |
类型 |
描述 |
name |
斯丁 |
姓名 |
changeProbability |
整数 |
每个价格变动的利率变化概率(百分比) |
startPrice |
大十进制 |
初始投资成本 |
delta |
整数 |
当前值可以更改的最大百分比量 |
分享价格:
属性 |
类型 |
描述 |
operDate |
本地日期时间 |
设置费率的时间(tick) |
share |
晋升 |
促销链接 |
rate |
大十进制 |
分享价格 |
商人:
属性 |
类型 |
描述 |
name |
细绳 |
设置费率的时间(tick) |
sfreqTick |
整数 |
交易频率。由交易者执行操作之前的时间段(以跳动点为单位)指定 |
cash |
大十进制 |
除股票以外的金额 |
traidingMethod |
整数 |
交易者使用的算法。让我们将其设置为一个常数,算法的实现将在Java代码中(在下面的部分中) |
changeProbability |
整数 |
完成操作的概率,百分比 |
about |
细绳 |
每个价格变动的利率变化概率(以百分比表示) |
交易者行动:
属性 |
类型 |
描述 |
operation |
整数 |
交易类型(买入或卖出) |
traider |
商人 |
交易者链接 |
shareRate |
分享价格 |
与股票价格的链接(分别是股票本身、其利率和发行时间) |
amount |
长的 |
本次交易涉及的股份数量 |
为了确保每个模型的唯一性,我们将添加一个
longid
类型的属性。该属性在模型实例中是
唯一的,并且可以唯一地标识它。引用其他模型(交易者、股票、股票价格)的属性可以使用此属性来唯一标识相应的模型。我立刻想到我们可以用来 存储这样的数据,相应的模型在哪里。但是,请尝试在以下条件下在代码中实现此功能:
id
Map<Long, Object>
Object
- 数据大小明显超过可用 RAM 的大小;
- 预计可以从十几个不同的地方访问数据;
- 需要同时修改和读取数据的能力;
- 有必要确保数据的形成和完整性规则;
...您将面临需要适当资格和时间来实施的任务。没有必要“重新发明轮子”。很多事情已经为我们想好了并写好了。所以我们将使用多年来已经测试过的东西。
在 Java 中存储数据
让我们考虑一下行动。在 Java 中,我们为此模型创建了一个特定的类,
Share
其中包含字段
name
,
changeProbability
,
startPrice
,
delta
。许多共享存储为
Map<Long, Share>
,其中密钥是每个共享的唯一标识符。
public class Share {
private String name;
private BigDecimal startPrice;
private int changeProbability;
private int delta;
}
Map<Long, Share> shares = new HashMap<>();
shares.put(1L, new Share("ibm", BigDecimal.valueOf(20.0), 15, 10));
shares.put(2L, new Share("apple", BigDecimal.valueOf(14.0), 25, 15));
shares.put(3L, new Share("google", BigDecimal.valueOf(12.0), 20, 8));
...
shares.put(50L, new Share("microsoft", BigDecimal.valueOf(17.5), 10,4 ));
要通过 ID 访问所需的促销活动,请使用方法
shares.get(id)
。对于按名称或价格查找股票的任务,我们将循环遍历所有记录以查找我们需要的记录,依此类推。但我们会采取相反的方式,将值存储在 DBMS 中。
DBMS 中的数据存储
让我们为 DBMS 制定一组初始的数据存储规则:
- DBMS 中的数据被组织成表 ( TABLE ),表是一组记录。
- 所有记录都具有相同的字段集。它们是在创建表时设置的。
- 该字段可以设置为默认值 ( DEFAULT )。
- 对于表,您可以设置约束 ( CONSTRAINT ) 来描述其数据的要求,以确保其完整性。这可以在表创建阶段( CREATE TABLE )完成或稍后添加(ALTER TABLE ... ADD CONSTRAINT)。
- 最常见的约束:
- 主键是 PRIMARY (在我们的例子中是 Id )。
- 唯一值字段UNIQUE(车辆表的VIN)。
- 检查CHECK字段(百分比值不能大于100)。字段的私有限制之一是NOT NULL或NULL,它禁止/允许在表字段中存储 NULL。
- 链接到第三方表FOREIGN KEY(链接到股价表中的股票)。
- 索引INDEX(对字段建立索引以加快在其中搜索值的速度)。
- 如果记录的字段值与限制(CONSTRAINT)相矛盾,则不会发生记录的修改( INSERT、UPDATE )。
- 每个表可以有一个(或多个)关键字段,可用于唯一标识一条记录。这样的字段(或多个字段,如果它们形成组合键)形成表的主键 - PRIMARY KEY。
- 主键保证了表中记录的唯一性;在主键上创建索引,可以根据键值快速访问整个记录。
- 拥有主键可以更轻松地在表之间创建链接。接下来,我们将使用人工主键:对于第一条记录
id = 1
,后续的每条记录都将插入到表中,并且 id 值加一。此键通常称为AutoIncrement或AutoIdentity。
实际上是一个股票表:
在这种情况下是否可以使用股票名称作为键?总的来说 - 是的,但有可能某些公司发行不同的股票并仅以自己的名称命名。在这种情况下,将不再有唯一性。在实践中,经常使用人工主键。同意,在包含人员记录的表中使用全名作为唯一键并不能确保唯一性。以及使用全名和出生日期的组合。
DBMS 中的数据类型
与任何其他编程语言一样,SQL 也具有数据类型。以下是最常见的 SQL 数据类型:
整数类型
SQL类型 |
SQL 同义词 |
Java中的匹配 |
描述 |
INT |
INT4,整数 |
java.lang.Integer |
4 字节整数,-2147483648 … 2147483647 |
布尔值 |
布尔、位 |
java.lang.布尔值 |
真假 |
天音 |
|
java.lang.Byte |
1 字节整数,-128 … 127 |
小智 |
INT2 |
java.lang.Short |
2 字节整数,-32768 … 32767 |
BIGINT |
INT8 |
java.lang.Long |
8 字节整数,-9223372036854775808 … 9223372036854775807 |
自动递增 |
增量 |
java.lang.Long |
表独有的增量计数器。如果插入一个新值,它就会加一,生成的值永远不会重复。 |
真实的
SQL类型 |
SQL 同义词 |
Java中的匹配 |
描述 |
小数(N,M) |
十二月,数字 |
java.math.BigDecimal |
固定精度小数(N 个整数位和 M 个小数位)。主要设计用于处理财务数据。 |
双倍的 |
浮点8 |
java.lang.Double |
双精度实数(8 字节)。 |
真实的 |
浮点数4 |
java.lang.Real |
单精度实数(4 字节)。 |
细绳
SQL类型 |
SQL 同义词 |
Java中的匹配 |
描述 |
VARCHAR(N) |
NVARCHAR |
java.lang.String |
长度为 N 的 UNICODE 字符串。长度限制为 2147483647 将字符串的全部内容加载到内存中。 |
日期和时间
SQL类型 |
SQL 同义词 |
Java中的匹配 |
描述 |
时间 |
|
java.time.LocalTime、java.sql.Time |
存储时间(最多纳秒),转换为DATETIME时,日期设置为1970年1月1日。 |
日期 |
|
java.time.LocalDate、java.sql.Timestamp |
以 yyyy-mm-dd 格式存储日期,时间设置为 00:00 |
约会时间 |
时间戳 |
java.time.LocalDateTime、java.sql.Timestamp |
存储日期+时间(不考虑时区)。 |
大量数据的存储
SQL类型 |
Java中的匹配 |
描述 |
BLOB |
java.io.InputStream、java.sql.Blob |
存储二进制数据(图片、文件...)。 |
CLOB |
java.io.Reader、java.sql.Clob |
与 VARCHAR 不同,存储大型文本数据(书籍、文章...)是将数据分部分加载到内存中。 |
SQL 编写风格
对于许多语言,都有代码格式指南。通常,此类文档包含变量、常量、方法和其他语言结构的命名规则。因此,对于 Python,有 PEP8,对于
Java - Oracle Code Conventions for Java。已经为 SQL 创建了几个不同的集,它们之间略有不同。无论如何,您应该养成在格式化代码时遵循规则的习惯,尤其是在团队中工作时。例如,规则可以是以下(当然,您可以为自己制定一套不同的规则,主要是将来坚持它们):
- 关键字和保留字,包括命令和运算符,必须用大写字母书写:CREATE TABLE、CONSTRAINT...
- 表、字段和其他对象的名称不应与 SQL 语言关键字重合(请参阅文章末尾的链接),但可以包含它们。
- 表名称应反映其用途。它们以小写字母书写。名称中的单词之间用下划线分隔。末尾的单词必须是复数:traders(交易者)、share_rates(份额率)。
- 表字段名称应反映其用途。它们必须以小写字母书写,名称中的单词必须采用驼峰式格式,并且末尾的单词必须使用单数:name(名称)、share_rates(分享率)。
- 人工关键字段必须包含单词 id。
- CONSTRAINT 名称必须遵循表命名约定。它们还必须包括其中涉及的字段和表,以语义前缀开头:check_(检查字段值)、pk_(主键)、fk_(外键)、uniq_(字段唯一性)、idx_(索引)。示例:pk_traider_share_actions_id(trader_share_actions 表的 id 字段上的主键)。
- 依此类推,当你学习 SQL 时,规则列表将会被补充/更改。
数据库管理系统设计
在创建 DBMS 之前,需要对其进行设计。最终模式包含表、一组字段、约束、键、字段的默认条件、表和其他数据库实体之间的关系。在 Internet 上,您可以找到许多免费的在线/离线设计器来设计小型 DBMS。尝试在搜索引擎中输入“免费数据库设计器”之类的内容。此类应用程序具有有用的附加属性:
- 可以生成SQL命令来创建DBMS。
- 在图表上直观地显示设置。
- 允许您移动表格以获得更好的可视化效果。
- 在图表上显示键、索引、关系、默认值等。
- 他们可以远程存储 DBMS 模式。
例如,
dbdiffo.com突出显示键、显示非空字段和带有 NN 标签的 AI(自动增量)计数器:
在 DBMS 中创建表
所以我们有一个图表。现在让我们继续创建表 (CREATE TABLE)。为此,我们建议获得初步数据:
- 表名
- 字段名称和类型
- 字段的限制(CONSTRAINTS)
- 字段的默认值(如果可用)
- 主键 (PRIMARY KEY) 如果可用
- 表之间的连接(外键)
我们不会详细研究 CREATE TABLE 命令的所有选项;我们将使用为交易者创建表的示例来了解 SQL 基础知识:
CREATE TABLE traiders(
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
freqTiсk INTEGER NOT NULL,
cash DECIMAL(15,2) NOT NULL DEFAULT 1000,
tradingMethod INTEGER NOT NULL,
changeProbability INTEGER NOT NULL DEFAULT 50,
about VARCHAR(255) NULL
);
ALTER TABLE traiders ADD CONSTRAINT check_traiders_tradingMethod
CHECK(tradingMethod IN (1,2,3));
ALTER TABLE traiders ADD CONSTRAINT check_traiders_changeProbability
CHECK(changeProbability <= 100 AND changeProbability > 0)
让我们仔细看看:
CREATE TABLE traiders
(字段描述)- 创建具有指定名称的表;在描述中,字段以逗号分隔。任何命令都以分号结尾。
- 字段描述以其名称开头,后跟其类型、CONSTRAINT 和默认值。
id BIGINT AUTO_INCREMENT PRIMARY KEY
– 整数类型的 id 字段是一个主键和一个增量计数器(对于 id 字段的每条新记录,都会生成一个值,该值比该表之前创建的值大 1)。
cash DECIMAL(15,2) NOT NULL DEFAULT 1000
– 现金字段,小数,小数点前 15 位,小数点后两位(财务数据,例如美元和美分)。不能接受 NULL 值。如果没有给定值,它将获得值 1000。
about VARCHAR(255) NULL
– about 字段,一个最长 255 个字符的字符串,可以接受空值。
注意,我们可以在建表后设置部分
CONSTRAINT条件。让我们考虑一下修改表结构及其字段的构造:
ALTER TABLE table_name ADD CONSTRAINT constrain_name CHECK (condition)使用示例:
CHECK(tradingMethod IN (1,2,3))
– TradingMethod字段只能取值1,2,3
CHECK(changeProbability <= 100 AND changeProbability > 0)
–changeProbability字段可以取1到100范围内的整数值
表之间的关系
为了分析表之间关系的描述,我们看一下share_rates的创建:
CREATE TABLE share_rates(
id BIGINT AUTO_INCREMENT PRIMARY KEY,
operDate datetime NOT NULL,
share BIGINT NOT NULL,
rate DECIMAL(15,2) NOT NULL
);
ALTER TABLE share_rates ADD FOREIGN KEY (share) REFERENCES shares(id)
可以按如下方式设置到另一个表的值的链接:
ALTER TABLE
table_from_which_is_referred
ADD FOREIGN KEY
(field_which_referred)
REFERENCES
table_to_which_is_referenced (field_which_is_referenced) 让我们有关于
股票的记录,例如,对于 id=50,我们存储初始价格为 17.5 的 Microsoft 股票,增量为 20,变化机会为 4%。对于
share_rates表,我们得到三个主要属性:
- 我们只需将shares表中的id键的值存储在share字段中,以便使用它从shares表中获取其余信息(名称等)。
- 我们无法为不存在的促销活动制定费率。您不能在 share 字段中插入不存在的值(shares 表中没有具有此 id 的记录),因为表之间不会存在对应关系。
- 我们无法删除在 share_rates 中设置费率的股票中的股票条目。
最后两点用于确保存储数据的完整性。您可以使用本文末尾的 github 存储库链接来查看我们模拟的 SQL 表的创建以及相应类方法的 Java 实现中的 SQL 查询示例。
第三部分
GO TO FULL VERSION