Pandas: Ao usar to_sql(), continuar se forem detectadas chaves primárias duplicadas?

Criado em 13 abr. 2017  ·  19Comentários  ·  Fonte: pandas-dev/pandas

Exemplo de código, um exemplo de copiar e colar, se possível

df.to_sql('TableNameHere', engine, if_exists='append', chunksize=900, index=False)

Descrição do Problema

Estou tentando anexar um grande DataFrame a uma tabela SQL. Algumas das linhas no DataFrame são duplicatas daquelas na tabela SQL, outras não. Mas to_sql() para completamente de executar se até mesmo uma duplicata for detectada.

Faria sentido para to_sql(if_exists='append') apenas avisar o usuário quais linhas tinham chaves duplicadas e apenas continuar a adicionar as novas linhas, não parar completamente a execução. Para grandes conjuntos de dados, você provavelmente terá duplicatas, mas deseja ignorá-las.

Talvez adicionar um argumento para ignorar duplicatas e continuar executando? Talvez uma opção adicional if_exists como 'append_skipdupes' ?

Saída de pd.show_versions()

VERSÕES INSTALADAS

compromisso: Nenhum
python: 3.6.0.final.0
bits python: 64
SO: Windows
Versão do SO: 10
máquina: AMD64
processador: Intel64 Family 6 Model 60 Stepping 3, GenuineIntel
byteorder: pouco
LC_ALL: Nenhum
LAN: Nenhum
LOCAL: Inglês_Estados Unidos.1252

pandas: 0,19,2
nariz: nenhum
pip: 9.0.1
ferramentas de configuração: 28.8.0
Cíton: Nenhum
numpy: 1.12.0
cip: Nenhum
statsmodels: Nenhum
xarray: Nenhum
IPython: 5.3.0
esfinge: Nenhuma
Paty: Nenhum
dateutil: 2.6.0
pytz: 2016.10
bloco: Nenhum
gargalo: nenhum
tabelas: Nenhuma
numexpr: Nenhum
matplotlib: Nenhum
openpyxl: Nenhum
xlrd: Nenhum
xlwt: Nenhum
xlsxwriter: Nenhum
lxml: Nenhum
bs4: Nenhum
html5lib: 0,999999999
httplib2: Nenhum
apiclient: Nenhum
sqlalchemy: 1.1.9
pymysql: Nenhum
psycopg2: Nenhum
jinja2: 2.9.5
boto: nenhum
pandas_datareader: Nenhum

Enhancement IO SQL

Comentários muito úteis

Isso também deve suportar o modo "na atualização duplicada".

Todos 19 comentários

Isso também deve suportar o modo "na atualização duplicada".

@rosstripi Acho que a ideia de ter isso certamente seria aceita, mas AFAIK o principal gargalo é uma implementação para isso usando sql/sqlalchemy de maneira agnóstica de sabor. Alguma exploração de como isso poderia ser feito é certamente bem-vinda!

Olá, você conseguiu alguma solução para isso? Por favor deixe-me saber

Alguma atualização sobre essa implementação?

Agora estou enfrentando esse problema com o PostgreSQL e SQLAlchemy e adoraria ter isso "na atualização duplicada".

Obrigado pelo trabalho

Uma solução alternativa seria remover o índice exclusivo no banco de dados:

sqlquery="ALTER 'TABLE DATABASE'.'TABLE' DROP INDEX 'idx_name'"
após
df.to_sql('TableNameHere', engine, if_exists='append', chunksize=900, index=False)
pode ser executado.

Apenas deixe seu servidor MySQL adicionar o índice novamente e elimine as duplicatas.
sqlquery="ALTER IGNORE TABLE 'DATABASE'.'TABLE' ADD UNIQUE INDEX 'idx_name' ('column_name1' ASC, 'column_name2' ASC, 'column_name3' '[ASC | DESC]')"

Dependendo de sua aplicação específica, isso pode ser útil.
De qualquer forma if_exists opção como append_skipdupes seria muito melhor.

append_skipdupes seria a maneira perfeita de lidar com isso.

sim, append_skipdupes +1

Concordei que seria bom poder lidar com isso com opções em df.to_sql() .

Aqui está a solução que eu uso no sqlite:

CREATE TABLE IF NOT EXISTS my_table_name (
    some_kind_of_id INT PRIMARY KEY ON CONFLICT IGNORE,
    ...

Então, quando insiro duplicatas, elas são ignoradas silenciosamente e as não duplicadas são processadas corretamente. No meu caso, os dados são (ou seja , deveriam ser ) estáticos, então não preciso atualizar. É só que a forma do feed de dados é tal que obterei duplicatas que são ignoráveis.

uma outra solução alternativa com MariaDb e MySql:
df.to_csv("test.csv")
então use:
LOAD DATA INFILE 'test.csv' IGNORE INTO TABLE mytable ou
LOAD DATA INFILE 'test.csv' REPLACE INTO TABLE mytable .

LOAD DATA é muito mais rápido que INSERT.

código completo:

csv_path = str(Path(application_path) / "tmp" / "tmp.csv").replace("\\", "\\\\")
df.to_csv(csv_path, index=False, sep='\t', quotechar="'", na_rep=r'\N')
rq = """LOAD DATA LOCAL INFILE '{file_path}' REPLACE INTO TABLE {db}.{db_table}
        LINES TERMINATED BY '\\r\\n'
        IGNORE 1 LINES
         ({col});
        """.format(db=db,
                   file_path=csv_path,
                   db_table=table_name,
                   col=",".join(df.columns.tolist()))

Acredito que isso esteja sendo abordado em #29636 com o argumento upsert_ignore , que aborda #14553.

append_skipdupes +1

+1 para append_skipdupes

Concordo que 'append_skipdupes' deve ser adicionado.

Sim por favor. 'append_skipdupes' deve ser adicionado e não apenas para a coluna Chave Primária. Se houver duplicatas entre outras colunas exclusivas, também deve pular a adição dessas novas linhas duplicadas.

+1 para append_skipdupes

append_skipdupes +1

append_skipdupes +1

+1 para append_skipdupes

Enquanto isso, você pode usar isso https://pypi.org/project/pangres/

Esta página foi útil?
0 / 5 - 0 avaliações