我目前有一个具有以下架构的MongoDB数据库:
Image: { name: String, src: String, category: String, tags: [String] }
我想把它迁移到Postgres,为此我有4个表
image (id, src, name, category_id)
tag (id, name)
image_tag (image_id, tag_id)
category (id, name)
每个tag
插入可能会有新的image
,因此在使用CTE时我需要选择所有标记(如果不存在则只插入新标记)。我正在考虑使用缓存(redis)来存储已经插入的标签(所以我不需要从数据库中选择它们)。
所以我的问题是我应该使用insert into tags.. where not exists
语句或CTE + redis与CTE一起使用,只在缓存中找不到标签时插入标签?
答案 0 :(得分:0)
所以这里是 small 语句,用于将带有类别和多个标签的图像插入到postgres数据库的多个表中。以下表达式假定表类别和标记中的名称已定义唯一约束。为了完成,我还创建了一个没有该约束的语句(参见示例部分)。
WITH image_values(image_name, src, category) AS (
VALUES
('Goldkraut', 'goldkraut.jpg', 'logo')
),
tag_values(tag_name) AS (
VALUES
('music'), ('band')
),
category_select AS (
SELECT id, name FROM category
WHERE name IN (SELECT category FROM image_values)
),
category_insert AS (
INSERT INTO category(name)
SELECT category FROM image_values
ON CONFLICT (name) DO NOTHING
RETURNING id, name
),
category_created AS (
SELECT id, name FROM category_select
UNION ALL
SELECT id, name FROM category_insert
),
tag_select AS (
SELECT id, name FROM tag
WHERE name IN (SELECT tag_name FROM tag_values)
),
tag_insert AS (
INSERT INTO tag(name)
SELECT tag_name FROM tag_values
ON CONFLICT (name) DO NOTHING
RETURNING id, name
),
tag_created AS (
SELECT id, name FROM tag_select
UNION ALL
SELECT id, name FROM tag_insert
),
image_insert AS (
INSERT INTO image(src, name, category_id)
SELECT src, image_name, category_created.id
FROM image_values
LEFT JOIN category_created ON(image_values.category=category_created.name)
RETURNING id, src, name, category_id
),
image_tag_insert AS (
INSERT INTO image_tag(image_id, tag_id)
SELECT image_insert.id, tag_created.id FROM image_insert
CROSS JOIN tag_created
RETURNING image_id, tag_id
)
SELECT image_insert.*, category_created.name as category_name, image_tag_insert.*, tag_created.name as "tag.name"
FROM image_tag_insert
LEFT JOIN image_insert ON (image_id = image_insert.id)
LEFT JOIN category_created ON (category_created.id = image_insert.category_id)
LEFT JOIN tag_created ON (tag_created.id = tag_id)
在第一个公用表表达式(CTE)image_values
中,您将定义具有1:1关系的图像的所有值。在下一个表达式tag_values
中,定义了该图像的所有标记名称。
现在让我们从类别开始。要知道名称的类别是否已存在,请在category_select
中查询类别条目。在表达式category_insert
中,如果尚未退出,您将为该类别创建一个新条目(而不是再次从数据库中查询,我们使用cte category_select
来查明我们是否已经有一个具有此名称的类别)。要在图像表中存储类别ID,我们需要类别条目,无论现有(来自category_select
)还是插入(来自category_insert
),所以我们将这两个表达式合并到category_created
中。
现在我们对标签使用相同的模式。查询现有标记tag_select
,插入标记(如果不存在)tag_insert
并将此条目合并到tag_created
中。
接下来,我们将图片插入image_insert
。因此,我们从表达式image_values
中选择值并加入表达式category_created
以获取类别的ID。要将关系图像插入标记,我们将需要插入图像的id,因此我们将返回此值。其他返回值不是必需的,但我们将使用它们在最终查询中获得更好的结果集。
现在我们有了插入图像的主键,我们可以将图像的关联存储到标签中。在表达式image_tag_insert
中,我们选择插入图像的id,并将其与我们选择或插入的每个标记ID交叉连接。
对于最终语句,只需执行SELECT * FROM image_tag_insert
即可执行所有表达式。但是为了概述存储在数据库中的内容,我加入了所有的关系。所以结果将如下所示:
| id | src | name | category_id | category_name | image_id | tag_id | tag.name |
|----|---------------|-----------|-------------|---------------|----------|--------|----------|
| 1 | goldkraut.jpg | Goldkraut | 2 | logo | 1 | 3 | band |
| 1 | goldkraut.jpg | Goldkraut | 2 | logo | 1 | 1 | music |
在这个sqlfiddle上,您将看到给定的查询。在另一个sqlfiddle中,我在最后一个语句中添加了一些额外内容,以将所有插入的标签格式化为列表。如果您尚未在表格标签和类别中的名称列中添加唯一约束,则可以使用此example