sqlite - 使用 Sqlite 设计基于标签的数据表的最佳方法是什么?

标签 sqlite database-design full-text-search android-sqlite

从服务器收到的 Json 有这种形式。

[
 {
  "id": 1103333,
  "name": "James",
  "tagA": [
    "apple",
    "orange",
    "grape"
  ],
  "tagB": [
    "red",
    "green",
    "blue"
  ],
  "tagC": null
  },

  {
  "id": 1103336,
  "name": "John",
  "tagA": [
    "apple",
    "pinapple",
    "melon"
  ],
  "tagB": [
    "black",
    "white",
    "blue"
  ],
  "tagC": [
    "London",
    "New York"
    ]
  }
]

一个对象可以有多个标签,一个标签可以关联多个对象。

在这个列表中,我想找到一个对象,它的 tagA 是苹果或葡萄,tagB 是黑色的。

这是我用来写的第一个表。
create table response(id integer primary key, name text not null, tagA text, 
tagB text, tagC text)

select * from response where (tagA like '%apple%' or tagA like '%grape%') and (tagB like '%black%')

这种类型的表设计有一个问题就是搜索速度很慢,因为在使用Room等ORM库时不支持fts函数的surface函数。

我想到的下一件事是为每个标签创建一个表。
create table response(id integer primary key, name text not null)

create table tagA(objectID integer, value text, primary key(objectID, value))

create table tagB(objectID integer, value text, primary key(objectID, value))

create table tagC(objectID integer, value text, primary key(objectID, value))

select * from response where id in ((select objectId from tagA where value in ('apple','grape')) 
intersect
(select objectId from tagB where value in 'black'))

这大大增加了APK的插入时间和容量(每增加一张表大约增加两倍),但搜索速度远远落后于FTS虚拟表。

我想避免这种情况,就像我使用 FTS 表一样,因为我需要自己管理更多事情。

我错过了很多东西(索引等),但我不知道它是什么。

如何在不使用 FTS 方法的情况下优化数据库?

最佳答案

您可以使用引用表(又名映射表以及许多其他名称)来允许标记(所有单个表)和对象(再次单个表)之间的多对多关系。

所以你有 对象 表每个对象具有 id 你有 标签 再次使用 id 对于每个对象。所以像:-

DROP TABLE IF EXISTS object_table;
CREATE TABLE IF NOT EXISTS object_table (id INTEGER PRIMARY KEY, object_name);
DROP TABLE IF EXISTS tag_table;
CREATE TABLE IF NOT EXISTS tag_table (id INTEGER PRIMARY KEY, tag_name);

你会同时填充两个,例如
INSERT INTO object_table (object_name) VALUES
    ('Object1'),('Object2'),('Object3'),('Object4');
INSERT INTO tag_table (tag_name) VALUES
    ('Apple'),('Orange'),('Grape'),('Pineapple'),('Melon'),
    ('London'),('New York'),('Paris'),
    ('Red'),('Green'),('Blue'); -- and so on

您将拥有类似于以下内容的映射表:-
DROP TABLE IF EXISTS object_tag_mapping;
CREATE TABLE IF NOT EXISTS object_tag_mapping (object_reference INTEGER, tag_reference INTEGER);

随着标签被分配给对象而超时,反之亦然,您添加映射,例如:-
INSERT INTO object_tag_mapping VALUES
    (1,4), -- obj1 has tag Pineapple
    (1,1),  -- obj1 has Apple
    (1,8), -- obj1 has Paris
    (1,10), -- obj1 has green
    (4,1),(4,3),(4,11), -- some tags for object 4
    (2,8),(2,7),(2,4), -- some tags for object 2
    (3,1),(3,2),(3,3),(3,4),(3,5),(3,6),(3,7),(3,8),(3,9),(3,10),(3,11); -- all tags for object 3

然后你可以有这样的查询:-
SELECT object_name, 
    group_concat(tag_name,' ~ ') AS tags_for_this_object 
FROM object_tag_mapping 
JOIN object_table ON object_reference = object_table.id
JOIN tag_table ON tag_reference = tag_table.id
GROUP BY object_name
;
  • group_concat 是一个聚合函数(适用于每个 GROUP),它将为指定列找到的所有值与(可选)分隔符连接起来。

  • 查询的结果是:-

    enter image description here

    以下可能是基于标签的搜索(不是您可能同时使用 tag_name 和 tag_reference):-
    SELECT object_name, tag_name 
    FROM object_tag_mapping 
    JOIN object_table ON object_reference = object_table.id
    JOIN tag_table ON tag_reference = tag_table.id
    WHERE tag_name = 'Pineapple' OR tag_reference = 9
    ;
    

    这将导致:-

    enter image description here

  • 请注意,这是一个简单的概述,例如您可能需要考虑将映射表作为 WITHOUT ROWID 表,或者具有复合 UNIQUE 约束。

  • 额外的重新评论:-

    How do I implement a query that contains two or more tags at the same time?



    如果您想要特定的标签但仍然可行,这会稍微复杂一些。这是一个使用 CTE(公用表表达式)和 HAVING 子句(在生成输出后应用的 where 子句,因此可以应用于聚合)的示例:-
    WITH cte1(otm_oref,otm_tref,tt_id,tt_name, ot_id, ot_name) AS 
        (
            SELECT * FROM object_tag_mapping 
            JOIN tag_table ON tag_reference = tag_table.id 
            JOIN object_table ON object_reference = object_table.id
            WHERE tag_name = 'Pineapple' OR tag_name = 'Apple'
        )
    SELECT ot_name, group_concat(tt_name), count() AS cnt FROM CTE1 
    GROUP BY otm_oref
    HAVING cnt = 2
    ;
    

    这导致:-

    enter image description here

    关于sqlite - 使用 Sqlite 设计基于标签的数据表的最佳方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51128832/

    相关文章:

    mysql - 我应该如何构建我的表格?

    database-design - EAV 替代用户定义的字段?

    mysql - 区分重音的全文搜索 (MySQL)

    objective-c - 如何使用 SQLite 存储在 Core Data 中查询未保存的数据?

    iphone - objective-c 中的sqlite3多线程

    java - hibernate : Opinions in Composite PK vs Surrogate PK

    git - git中如何搜索标签注释的内容?

    sql - FreeText 查询很慢 - 包括 TOP 和 Order By

    java - Maven 不获取依赖 jar

    ios - Sqlite_prepare_v2返回SQLite_ERROR