简短介绍我的问题:
我正在尝试使用 stl 容器实现“某种”关系数据库。这只是出于娱乐/教育目的,因此不需要诸如“使用这个库”、“这绝对没用”之类的答案。
我知道此时标题有点令人困惑,但我们会达到目的(非常欢迎改进标题的建议)。
我采取了一些小步骤:
- 我可以将表构建为从列名称到其值的映射向量=>
std::vector<std::map<std::string, some_variant>>
。它很简单,它代表了我所需要的。
- 等等,我可以只存储列的名称一次并使用其索引访问值。 =>
std::vector<std::vector<some_variant>>
.就像第 1 点一样简单,但比这更快。
- 等等,在数据库中,表实际上是元组的序列=>
std::vector<std::tuple<args...>>
。这很酷,它准确地代表了我正在做的事情,没有变体的正确类型,甚至比其他更快。
注:“快于”是针对
1000000 条记录,用一个简单的循环,如下所示:
std::random_device dev;
std::mt19937 gen(dev());
std::uniform_int_distribution<long> rand1_1000(1, 1000);
std::uniform_real_distribution<double> rand1_10(1.0, 10.0);
void fill_1()
{
using my_variant = std::variant<long, long long, double, std::string>;
using values = std::map<std::string, my_variant>;
using table = std::vector<values>;
table t;
for (int i = 0; i < 1000000; ++i)
t.push_back({ {"col_1", rand1_1000(gen)}, {"col_2", rand1_1000(gen)}, {"col_3", rand1_10(gen)} });
std::cout << "size:" << t.size() << "\n";//just to prevent optimization
}
2234101600ns - 平均:2234
446344100ns - 平均:446
132075400ns - 平均:132
INSERT:这些解决方案中的任何一个都没有问题,插入就像示例中推回元素一样简单。
SELECT:1和2很简单,但3很棘手。
所以,最后,问题是:
-
内存使用情况:就使用的内存而言,使用解决方案 1 和 2 会产生大量开销。所以,3 似乎又是正确的选择。
对于 100 万条记录 2 的示例long
s and a double
我预计长整型需要接近 4MB*2,双打需要 8MB,再加上使用的向量、映射和变体的一些开销。相反,我们有(用 Windows 任务管理器测量,不是很准确,我知道):
1.340 MB
2.120 MB
3.31 MB
我错过了什么吗?除了提前预订合适的尺寸或shrink_to_fit
在插入循环之后?
有没有办法像 select 语句一样在运行时检索某些元组字段?
using my_tuple = std::tuple<long, long, string, double>;
std::vector<my_tuple> table;
int to_select;//this could be a vector of columns to select obviosly
std::cin>>to_select;
auto result = select (table, to_select);
您认为有机会以任何方式实现最后一行吗?我所看到的我们有两个问题:结果类型应该从起始元组中获取类型,然后实际执行所需字段的选择。
我读了很多关于这个问题的答案,他们都谈论使用连续索引make_index_sequence
或编译时已知索引。
我还发现本文 https://www.justsoftwaresolutions.co.uk/cplusplus/getting-tuple-elements-with-runtime-index.html,非常有趣,但对于本例来说并不是很有用。