【问题标题】:How Postgresql COPY TO STDIN With CSV do on conflic do update?Postgresql COPY TO STDIN With CSV 如何在冲突中进行更新?
【发布时间】:2018-06-09 16:54:11
【问题描述】:

我想做

 " on conflict (time) do update set name , description "

但我不知道什么时候将 stdin 与 csv 一起使用,我不知道什么名称等于什么?和描述等于什么......

table_a:

xxx.csv:

with open('xxx/xxx.csv', 'r', encoding='utf8') as f:
    sql = """
    COPY table_a FROM STDIN With CSV on conflict (time) 
    do update set name=??, description=??;
    """
    cur.copy_expert(sql, f)
    conn.commit()

【问题讨论】:

  • 创建一个临时表,将您的 csv 复制到那里,然后从所述临时表插入到您的主表中并解决所需的冲突。

标签: python postgresql csv stdin psycopg2


【解决方案1】:

我已经成功完成了批量upsert,功能如下(欢迎提出建议):

import io
from sqlalchemy.engine import Engine
from sqlalchemy.ext import declarative_base

BaseModel = declarative_base()


def upsert_bulk(engine: Engine, model: BaseModel, data: io.StringIO) -> None:
    """
    Fast way to upsert multiple entries at once

    :param `db`: DB Session
    :param `data`: CSV in a stream object
    """
    table_name = model.__tablename__
    temp_table_name = f"temp_{table_name}"

    columns = [c.key for c in model.__table__.columns]

    # Select only columns to be updated (in my case, all non-id columns)
    variable_columns = [c for c in columns if c != "id"]

    # Create string with set of columns to be updated
    update_set = ", ".join([f"{v}=EXCLUDED.{v}" for v in variable_columns])

    # Rewind data and prepare it for `copy_from`
    data.seek(0)

    with conn.cursor() as cur:
        # Creates temporary empty table with same columns and types as
        # the final table
        cur.execute(
            f"""
            CREATE TEMPORARY TABLE {temp_table_name} (LIKE {table_name})
            ON COMMIT DROP
            """
        )

        # Copy stream data to the created temporary table in DB
        cur.copy_from(data, temp_table_name)

        # Inserts copied data from the temporary table to the final table
        # updating existing values at each new conflict
        cur.execute(
            f"""
            INSERT INTO {table_name}({', '.join(columns)})
            SELECT * FROM {temp_table_name}
            ON CONFLICT (id) DO UPDATE SET {update_set}
            """
        )

        # Drops temporary table (I believe this step is unnecessary,
        # but tables sizes where growing without any new data modifications
        # if this command isn't executed)
        cur.execute(f"DROP TABLE {temp_table_name}")

        # Commit everything through cursor
        conn.commit()

    conn.close()

【讨论】:

    【解决方案2】:

    this SO post 中,有两个答案——结合在一起——为成功使用ON CONFLICT 提供了一个很好的解决方案。下面的例子,使用ON CONFLICT DO NOTHING;

    CREATE TEMP TABLE tmp_table 
    (LIKE label INCLUDING DEFAULTS)
    ON COMMIT DROP;
    
    COPY tmp_table FROM 'full/file/name/here';
    
    INSERT INTO main_table
    SELECT *
    FROM tmp_table
    ON CONFLICT DO NOTHING;
    

    【讨论】:

      【解决方案3】:

      感谢各位大师的解答。

      这是我的解决方案。

      sql = """
      CREATE TABLE temp_h (
          time ,
          name,
          description
      );
      COPY temp_h FROM STDIN With CSV;
      
      INSERT INTO table_a(time, name, description)
      SELECT *
      FROM temp_h ON conflict (time) 
      DO update set name=EXCLUDED.name, description=EXCLUDED.description;
      
      DROP TABLE temp_h;
      """
      

      【讨论】:

      • 一个小建议:使用CREATE TEMPORARY TABLE temp_h,所以它会在会话结束时自动删除(或事务,如果你想使用ON COMMIT DROP并且它是@ 987654321@,其中improves performance
      • this post 中,有人认为不应在没有需要的情况下更新相同的行。
      【解决方案4】:

      https://www.postgresql.org/docs/current/static/sql-copy.html

      postgres 中没有copy ... on conflict do 语句

      https://www.postgresql.org/docs/current/static/sql-insert.html

      仅限insert ... on conflict do

      【讨论】:

      • 谢谢,但我怎样才能从 csv do upsert 复制??
      • 您不能使用 csv 进行更新插入。要么解析 csv 并在冲突语句上创建插入,要么复制 csv 然后计算出欺骗
      猜你喜欢
      • 2020-06-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多