【问题标题】:SQLite : how to achieve ORM "eager loading" in plain SQL?SQLite:如何在纯 SQL 中实现 ORM“急切加载”?
【发布时间】:2017-09-28 04:55:40
【问题描述】:

我有一个包含电影、演员和标签的 SQLite 数据库。

电影和演员、电影和标签之间是多对多的关系。

在我的应用程序中,我想列出所有电影及其对应的演员和标签,例如:

  • 先生。与史密斯夫人:演员:布拉德皮特、安吉丽娜朱莉,标签:动作、喜剧、犯罪

  • 乘客:演员:詹妮弗·劳伦斯、克里斯·帕拉特、标签:冒险、戏剧、浪漫

我想知道实现这一目标的正确 SQL 语句是什么。

我的数据库中的表定义如下:

CREATE TABLE "Movie" 
(
    id INTEGER PRIMARY KEY, 
    name VARCHAR
)

CREATE TABLE "Actor"  
(
    id INTEGER PRIMARY KEY, 
    name VARCHAR
)

CREATE TABLE "Tag" 
( 
    id INTEGER PRIMARY KEY, 
    name VARCHAR
)

CREATE TABLE "Movie_Actor" 
( 
    movie_id INTEGER,
    actor_id INTEGER,
    FOREIGN KEY(movie_id) REFERENCES "Movie" (id), 
    FOREIGN KEY(actor_id) REFERENCES "Actor" (id),
    UNIQUE(movie_id, actor_id)
);

CREATE TABLE "Movie_Tag" 
( 
    movie_id INTEGER,
    tag_id INTEGER,
    FOREIGN KEY(movie_id) REFERENCES "Movie" (id), 
    FOREIGN KEY(tag_id) REFERENCES "Tag" (id),
    UNIQUE(movie_id, tag_id)
);

要获得带有演员和标签的单部电影,我使用以下 3 个查询(例如 Movie.id = 1):

获取电影行:

SELECT * 
FROM Movie 
WHERE id = 1

要获取演员:

SELECT *
FROM 
    (SELECT * FROM Actor) AS T1
JOIN 
    (SELECT * FROM Movie_Actor WHERE Movie_Actor.movie_id = 1) AS T2 ON T1.id = T2.actor_id

获取标签:

SELECT *
FROM 
    (SELECT * FROM Tag) AS T1
JOIN 
    (SELECT * FROM Movie_Tag WHERE Movie_Tag.movie_id = 1) AS T2 ON T1.id = T2.tag_id

我的问题是,当我获得诸如SELECT * FROM Movie 之类的电影列表时,我应该如何检索标签和演员?

许多 ORM 都有“急切加载”关系的选项,我想知道如何在纯 SQL 中做到这一点?

我是否需要对从SELECT * FROM Movie 获得的每一行执行额外的 2 个查询?

谢谢。

【问题讨论】:

    标签: sql database sqlite


    【解决方案1】:

    要获取带有id = 1 的电影以及与该电影相关的所有演员,请执行以下操作:

    SELECT * FROM Movie
        LEFT JOIN Movie_Actor ON Movie_Actor.movie_id = Movie.id
        LEFT JOIN Actor ON Actor.id = Movie_Actor.actor_id
        WHERE id = 1 
    

    要同时获取所有标签,请继续加入关联的表 Movie_TagTag

    您可能会认为这会非常低效,因为很多信息会被复制,例如电影的名称不仅会被提取一次,而且会被提取 NA * NT 次,其中 NA 是数字of fetched actor 和 NT 是获取标签的数量。

    实际上,数据库在这一点上往往很聪明,(正是因为这是一种非常流行的机制,可以通过尽可能少的往返数据库来检索数据),因此在它们的通信协议中,它们包含避免传输字段值的特殊措施从行到行都是相同的。因此,实际传输的数据量非常接近于分别查询每个表时将传输的数据量。

    当然,这样做的好处是您将遭受一次往返数据库的惩罚,而不是多次往返,每个表一次。

    【讨论】:

    • 谢谢!这很有帮助。我现在正试图弄清楚如何解析我得到的行。在我将行放入 之前: Hashmap。因此,对于每部返回的电影,我都有一个包含行内容的 Hashmap。这不会再飞了。
    • 是的,解码结果的算法有点复杂。除了Movie 的列之外,您的地图还需要有两个伪列,一个用于Actors,一个用于Tags,其对应的值将是集合。
    • 是的,我可能需要为地图创建一个特定的对象,该对象既可以包含单个值,也可以包含集合。我现在面临的另一个问题是如何限制结果。假设我只想获得 5 部电影,我能看到的解决此问题的唯一方法是制作像 SELECT * FROM Movies LIMIT 5 这样的子查询,然后才使用标签和演员 JOIN...
    • 这不是有效的 SQL。而且 SQLite 不是客户端/服务器数据库,所以 using multiple queries is not any slower.
    • @CL。因此,您的建议是为每个 Movie 进行 2 个单独的查询?例如,当迭代 SELECT * FROM Movie 查询每行的标签和参与者的结果时?
    猜你喜欢
    • 2018-10-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-11
    • 2014-10-13
    • 1970-01-01
    相关资源
    最近更新 更多