【问题标题】:SQL performance: Inserting one table into twoSQL 性能:将一张表插入两张
【发布时间】:2020-06-24 11:14:53
【问题描述】:

在我的职业生涯中,我遇到过许多必须将平面、非规范化数据插入规范化结构的实例。

为此,我经常使用 CTE 插件。例如

CREATE TABLE raw_data (
    foo varchar,
    bar_1 varchar,
    bar_2 varchar
);

INSERT INTO raw_data VALUES ('A', 'A1', 'A2');
INSERT INTO raw_data VALUES ('B', 'B1', 'B2');

CREATE TABLE foo (
    id int PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
    value varchar NOT NULL
);

CREATE TABLE bar (
    id int PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
    value varchar NOT NULL,
    foo_id int NOT NULL,
    CONSTRAINT fk_bar_foo FOREIGN KEY (foo_id) REFERENCES foo(id)
);

WITH new_foos AS (
    INSERT INTO foo (value)
    SELECT foo FROM raw_data
    RETURNING *
)
INSERT INTO bar (foo_id, value)
SELECT
    f.id,
    unnest(ARRAY[r.bar_1, r.bar_2])
FROM new_foos f
JOIN raw_data r
    ON r.foo = f.value;

它工作正常,但是从性能的角度来看,不得不返回并重新扫描原始数据表似乎很可惜。 IE。一次插入foo,然后再次插入bar

我很想知道这是否是一种最佳方法,或者如果不是,可以做些什么来改进它。

【问题讨论】:

  • 我认为没有办法绕过join——也就是说,您不能返回表中没有的值。如果您在raw_data(foo) 上有索引,那么性能应该不错。
  • 我想这取决于你对好的定义,有时像这样的 raw_data 导入会变得非常大。我内心有一个小声音说:“除非你真的必须这样做,否则不要多次访问你的大桌子”。您可能是对的,但我从未停止对 SQL 社区提出的一些解决方案感到惊讶,所以我只是想把这个扔出去!

标签: sql postgresql performance


【解决方案1】:

嗯,是的,但想想看,如果你有足够的内存来保存 Json,那么你就有足够的内存来保存表格。因此两次传递数据可能会更快。 1 次用于磁盘,1 次来自内存。出于这个原因,DBMS 倾向于在内存中保留最不常用的数据。免责声明:我的主要经验是使用 Oracle,所以我可能会在这里投射到 Postgres,但我认为它会起到缓冲作用。

【讨论】:

    【解决方案2】:

    我已经玩了一段时间了,我想把它作为一个可能的建议扔出去。如果您使用 CTE 将数据放入 JSON 结构(手动生成 PK),然后从中插入每个表会怎样?

    像这样:

    WITH raw_as_json AS (
        SELECT
            jsonb_build_object(
                'id', NEXTVAL('foo_seq'),
                'value', foo,
                'bars', json_build_array(bar_1, bar_2)
            ) AS foobar
        FROM raw_data
    ), foos AS (
        INSERT INTO foo (id, value)
        SELECT
            (foobar -> 'id')::int,
            foobar -> 'value'
        FROM raw_as_json
    )
    INSERT INTO bar (id, foo_id, value)
    SELECT
        NEXTVAL('bar_seq'),
        (foobar -> 'id')::int,
        jsonb_array_elements_text(foobar -> 'bars')
    FROM raw_as_json;
    

    它只需要扫描一次原始数据。我还没有完全权衡 CTE、json 等的性能问题。我很感激对这种方法的任何批评,我仍然会坚持一个更好(或更不奇怪)的答案。

    【讨论】:

    • 我会说您正在扫描数据 3 次:一次用于磁盘(显然)以生成 json,两次扫描 json。注意:json 较大,因此如果原始数据很大,Postgres 很有可能需要使用磁盘来保存 json。
    • 是的,我想可能是这样。因此,如果 json 适合内存,则速度会更快,否则可能会更慢。我仍然有兴趣看看是否会出现任何替代方案。理论上你只需要读一行写2,但实际上可能不存在这样的机制。
    • 这是一个公平的观点。想要将其表述为答案,我会将其标记为正确吗?
    • 好的,完成并作为评论删除。
    猜你喜欢
    • 1970-01-01
    • 2014-01-06
    • 1970-01-01
    • 2023-03-16
    • 1970-01-01
    • 2014-05-20
    • 1970-01-01
    • 1970-01-01
    • 2013-12-15
    相关资源
    最近更新 更多