我知道通常最好的方法是将INTEGER字段用作主键,但是不幸的是,由于要使用的API,我只能使用以下格式的主键:CHAR_INT(例如:ABC_12345) )。
我将拥有大量数据(10亿条记录),并且优先考虑查询和插入速度,使用CHAR_INT主键是否会对速度产生较大影响?还是相对可以忽略不计?
此外,为字符串的CHAR部分创建数字ID会更有效吗?因此,使用前面的示例:ABC_12345将变成类似于1_12345。我知道它们都是字符串,只是想知道仅使用数字是否有效率。
我正在使用SQLite。
谢谢!
答案 0 :(得分:4)
据我所知,没有内置数据类型“ CHAR_INT”。
但是,SQLite在键入方面非常灵活,并且允许任何字符串成为类型的名称。 SQLite不是强类型的,因此该值似乎存储为字符串。
数字索引更有效。一个重要原因是数字是固定长度的。字符串是可变长度的,当在索引中存储键值时会增加开销。另一个原因是硬件在支持数字比较方面做得更好。当考虑字符集和排序规则时,字符串比较变得更加复杂。
也就是说,与使用索引的好处相比,搜索和维护索引的开销实际上很小。因此,我不必担心索引仅包含字符串。但是,我将更加担心施加此类限制的工具。您应该能够在表中选择所需的键。
答案 1 :(得分:3)
类型(类型相似性)只有一个例外,并没有真正的区别。
特别是the_column_name INTEGER PRIMARY KEY
(带有或不带有AUTOINCREMENT)的例外,它将列定义为 rowid 列的别名。 INT PRIMARY KEY没有。
因此the_column_name CHAR_INT PRIMARY KEY
,the_column_name INT CHAR PRIMARY KEY
甚至是the_column_name INT PRIMARY KEY
实际上是相同的,甚至可以使用the_column_name RUMPLESTILTSKIN PRIMARY KEY
(尽管后者的类型相似性不同)。
是确定类型相似性的规则。有5个。具有最高优先级的规则是:如果类型具有INT,则类型相似性为INTEGER。作为类型的RUMPLESTILTSKIN会通过所有规则(除了最后一个规则)删除,如果最后一个规则不适用,那么如果先前的规则都不适用,则类型相似性为NUMERIC。
3.1。列亲和力的确定
列的亲和力由列的声明类型决定,具体取决于 如下所示的规则:
如果声明的类型包含字符串“ INT”,则将其分配 整数亲和力。
如果列的声明类型包含任何字符串“ CHAR”, “ CLOB”或“ TEXT”,则该列具有TEXT关联性。请注意 类型VARCHAR包含字符串“ CHAR”,因此被分配为TEXT 亲和力。
如果列的声明类型包含字符串“ BLOB”,或者没有 指定类型,则该列具有关联BLOB。
如果列的声明类型包含任何字符串“ REAL”, “ FLOA”或“ DOUB”,则该列具有REAL亲和力。
否则,关联性为NUMERIC。
请注意,确定列亲和力的规则顺序为 重要。声明类型为“ CHARINT”的列将同时匹配 规则1和2,但第一个规则优先,因此该列 亲和力将为INTEGER。
说类型相似性并不能决定数据的存储方式。每列均根据取决于要存储的数据的存储类进行存储。
空值存储为空值,一串数字(作为字符串包含或不作为字符串)存储为整数。简而言之,数据将按照SQLite的决定进行存储,SQlite会尝试尽可能高效地存储数据,并在尽可能小的空间内存储到最小的字节。
请考虑以下内容:-
DROP TABLE IF EXISTS mytable1;
DROP TABLE IF EXISTS mytable2;
DROP TABLE IF EXISTS mytable3;
CREATE TABLE IF NOT EXISTS mytable1 (c1 CHAR_INT PRIMARY KEY);
CREATE TABLE IF NOT EXISTS mytable2 (c1 INT PRIMARY KEY);
CREATE TABLE IF NOT EXISTS mytable3 (c1 RUMPLEstiltSkin PRIMARY KEY);
-- INSERT INTO mytable1 VALUES(12345),('12345'),('a_12345'),('1_12345'),(x'0102030405'); -- fails due to unique constraint 12345 and '12345' are the same
-- INSERT INTO mytable2 VALUES(12345),('12345'),('a_12345'),('1_12345'),(x'0102030405'); -- fails due to unique constraint 12345 and '12345' are the same
-- INSERT INTO mytable3 VALUES(12345),('12345'),('a_12345'),('1_12345'),(x'0102030405'); -- fails due to unique constraint 12345 and '12345' are the same
INSERT INTO mytable1 VALUES(12345),('54321'),('a_12345'),('1_12345'),(x'0102030405');
INSERT INTO mytable2 VALUES(12345),('54321'),('a_12345'),('1_12345'),(x'0102030405');
INSERT INTO mytable3 VALUES(12345),('54321'),('a_12345'),('1_12345'),(x'0102030405');
SELECT c1, typeof(c1) FROM mytable1;
SELECT c1, typeof(c1) FROM mytable2;
SELECT c1, typeof(c1) FROM mytable3;
typeof 函数返回列的类型(存储类型不是列亲和力)
结果显示为:-
使用除INTEGER PRIMARY KEY之外的任何内容(有一些派生),因此rowid的别名为
有两个索引,rowid(除非TABLE被定义为WITHOUT ROWID)和PRIMARY KEY。
搜索具有特定rowid的记录,或搜索具有指定范围内的rowid的所有记录的速度大约是通过指定任何其他PRIMARY KEY或索引值进行的类似搜索的两倍。 ROWIDs and the INTEGER PRIMARY KEY
处理数字而不是字符串会占用更多空间,因此会减少可以在缓冲区中保存的数据,因此会产生一些影响。
搜索索引相对较快,与数据本身相比,数据量相对较少,并且仅读取数据本身。
也许考虑以下几点:-
DROP TABLE IF EXISTS mytable1;
DROP TABLE IF EXISTS mytable2;
DROP TABLE IF EXISTS mytable3;
CREATE TABLE IF NOT EXISTS mytable1 (pk INT PRIMARY KEY, name TEXT);
CREATE TABLE IF NOT EXISTS mytable2 (pk CHAR_INT PRIMARY KEY, name TEXT);
CREATE TABLE IF NOT EXISTS mytable3 (pk INT PRIMARY KEY, name TEXT) WITHOUT ROWID;
INSERT INTO mytable1
WITH RECURSIVE cte1(a,b) AS (
SELECT 'ABC_'||CAST(abs(random()) AS TEXT),'some data' UNION ALL
SELECT DISTINCT (substr(a,1,4))||CAST(abs(random()) AS TEXT),'some data' FROM cte1 LIMIT 1000000
)
SELECT * FROM cte1
;
INSERT INTO mytable2
WITH RECURSIVE cte1(a,b) AS (
SELECT '1_'||CAST(abs(random()) AS TEXT),'some data' UNION ALL
SELECT DISTINCT (abs(random()) % 100)||'_'||CAST(abs(random()) AS TEXT),'some data' FROM cte1 LIMIT 1000000
)
SELECT * FROM cte1
;
INSERT INTO mytable3 SELECT * FROM mytable1;
SELECT * FROM mytable1 WHERE name LIKE('%me data%');
SELECT * FROM mytable2 WHERE name LIKE('%me data%');
SELECT * FROM mytable3 WHERE name LIKE('%me data%');
SELECT * FROM mytable3 WHERE name LIKE('%me data%');
SELECT * FROM mytable1 WHERE name LIKE('%me data%');
SELECT * FROM mytable2 WHERE name LIKE('%me data%');
SELECT * FROM mytable2 WHERE name LIKE('%me data%');
SELECT * FROM mytable3 WHERE name LIKE('%me data%');
SELECT * FROM mytable1 WHERE name LIKE('%me data%');
这将创建3个排列,全部包含1,000,000行
mytable1 的主键为ABC _ ????例如:-
mytable2 的主键为?? _ ????例如:-
mytable3 是 mytable1 的副本,但该表已使用WITHOUT ROWID定义
时间
所有3个的SELECTS都非常接近(完成了多个选择,并且以不同的顺序使缓存均匀)。包含计时的消息是:-
SELECT * FROM mytable1 WHERE name LIKE('%me data%')
> OK
> Time: 0.672s
SELECT * FROM mytable2 WHERE name LIKE('%me data%')
> OK
> Time: 0.667s
SELECT * FROM mytable3 WHERE name LIKE('%me data%')
> OK
> Time: 0.702s
SELECT * FROM mytable3 WHERE name LIKE('%me data%')
> OK
> Time: 0.7s
SELECT * FROM mytable1 WHERE name LIKE('%me data%')
> OK
> Time: 0.675s
SELECT * FROM mytable2 WHERE name LIKE('%me data%')
> OK
> Time: 0.673s
SELECT * FROM mytable2 WHERE name LIKE('%me data%')
> OK
> Time: 0.676s
SELECT * FROM mytable3 WHERE name LIKE('%me data%')
> OK
> Time: 0.709s
SELECT * FROM mytable1 WHERE name LIKE('%me data%')
> OK
> Time: 0.676s
除了前面的链接之外,您可能还希望查看:-
答案 2 :(得分:1)
Sqlite有两种类型的表。
默认ROWID table。 Rowid表是B*-Trees,带符号的64位整数作为其主键(rowid
)。如果该列只有一个INTEGER PRIMARY KEY列,则该列将用作rowid的别名。任何其他PRIMARY KEY
类型,或两列或更多列的复合主键,都只是唯一索引。
因此,您的CHAR_INT
列(Sqlite非常 宽容列类型需要什么;这只是hint关于如何尝试存储和比较存储在其中的值的WITHOUT ROWID该列(不是实际类型),根据Sqlite规则,具有整数 affinity ,但是由于ABC_123
之类的内容无法无损地转换为整数,因此它们将以字符串形式存储。插入一行意味着更新主表和主键索引(当然还有其他索引)。通过键查找一行涉及到首先在索引中查找对应的rowid
,然后查找主表的该行。从正面来看,这两个查询都使用O(log N)
二进制搜索。
其他表类型为pip download
。这些表使用与索引相同的普通B-Tree数据结构,并使用表的主键(无论其类型或多少列)作为真正的主键。插入只需要更新一个表(当然,还要加上其他索引),查找只需要搜索一个表,因此当您的主键不是INTEGER
时,它可以更快并且占用更少的磁盘空间。
最终哪个更好取决于一系列因素,例如表中使用了多少其他索引,行中存储了多少数据,在表上运行的查询等等。该文档建议,除其他建议外,还可以构建带有WITHOUT ROWID
表和不带有#I load df and make some changes with it
movies <- read_csv("~/shared/mi_2018/data/movies.csv") %>%
select(movie_id, production_countries)
movies_with_countries = extract_json2(df = movies, col = "production_countries")
a = spread(movies_with_countries, key = production_countries_sep,
value = production_countries_v)
row.names(a) <- a$movie_id
a = a %>% select(-movie_id, -production_countries)
a[is.na(a)] <- 0
#Here I make a bipartite graph, but the result is reverse compared to what I expected
bg = graph.incidence(a)
V(bg)$type
pr = bipartite.projection(bg)
q = get.adjacency(pr$proj1,sparse=FALSE,attr="weight")
plot(pr$proj1,edge.width=E(pr$proj1)$weight^2,edge.color="black",vertex.label=V(pr$proj1)$name)
表的数据库,并进行基准测试,以了解更适合特定用途的数据。