Pandas: Al usar to_sql(), ¿continuar si se detectan claves primarias duplicadas?

Creado en 13 abr. 2017  ·  19Comentarios  ·  Fuente: pandas-dev/pandas

Ejemplo de código, un ejemplo copiable y pegable si es posible

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

Descripción del problema

Estoy tratando de agregar un DataFrame grande a una tabla SQL. Algunas de las filas del DataFrame son duplicados de las de la tabla SQL, otras no. Pero to_sql() deja de ejecutarse por completo si se detecta incluso un duplicado.

Tendría sentido que to_sql(if_exists='append') simplemente advierta al usuario qué filas tienen claves duplicadas y simplemente continúe agregando las nuevas filas, no deje de ejecutarse por completo. Para grandes conjuntos de datos, es probable que tenga duplicados, pero querrá ignorarlos.

¿Quizás agregar un argumento para ignorar los duplicados y seguir ejecutando? ¿Quizás una opción adicional if_exists como 'append_skipdupes' ?

Salida de pd.show_versions()

VERSIONES INSTALADAS

compromiso: ninguno
pitón: 3.6.0.final.0
bits de Python: 64
SO: Windows
Lanzamiento del sistema operativo: 10
máquina: AMD64
Procesador: Intel64 Familia 6 Modelo 60 Paso 3, GenuineIntel
orden de bytes: pequeño
LC_ALL: Ninguno
IDIOMA: Ninguno
LOCAL: inglés_Estados Unidos.1252

pandas: 0.19.2
nariz: Ninguno
pipa: 9.0.1
herramientas de configuración: 28.8.0
Citón: Ninguno
número: 1.12.0
espía: Ninguno
modelos estadísticos: Ninguno
matriz: ninguno
Python: 5.3.0
esfinge: Ninguno
Paty: Ninguno
dateutil: 2.6.0
Pytz: 2016.10
bloque: Ninguno
cuello de botella: Ninguno
tablas: ninguna
exprnumero: Ninguno
matplotlib: Ninguno
openpyxl: Ninguno
xlrd: Ninguno
xlwt: Ninguno
xlsxwriter: Ninguno
lxml: Ninguno
bs4: Ninguno
html5lib: 0.999999999
httplib2: Ninguno
apiciente: Ninguno
sqlalchemy: 1.1.9
pymysql: Ninguno
psycopg2: Ninguno
jinja2: 2.9.5
Boto: Ninguno
pandas_datareader: Ninguno

Enhancement IO SQL

Comentario más útil

Esto también debería ser compatible con el modo "en actualización duplicada".

Todos 19 comentarios

Esto también debería ser compatible con el modo "en actualización duplicada".

@rosstripi Creo que la idea de tener esto ciertamente sería aceptada, pero AFAIK el principal cuello de botella es una implementación para esto usando sql/sqlalchemy de una manera agnóstica. ¡Alguna exploración de cómo se podría hacer esto es ciertamente bienvenida!

Hola, encontraste alguna solución para esto? Por favor hagamelo saber

¿Alguna actualización sobre esta implementación?

Ahora estoy enfrentando este problema con PostgreSQL y SQLAlchemy y me encantaría tener eso "en actualización duplicada".

gracias por el trabajo

Una solución sería eliminar el índice único en la base de datos:

sqlquery="ALTER 'TABLE DATABASE'.'TABLE' DROP INDEX 'idx_name'"
después
df.to_sql('TableNameHere', engine, if_exists='append', chunksize=900, index=False)
se puede ejecutar

Simplemente deje que su servidor MySQL agregue el índice nuevamente y elimine los duplicados.
sqlquery="ALTER IGNORE TABLE 'DATABASE'.'TABLE' ADD UNIQUE INDEX 'idx_name' ('column_name1' ASC, 'column_name2' ASC, 'column_name3' '[ASC | DESC]')"

Dependiendo de su aplicación específica, esto puede ser útil.
De todos modos, la opción if_exists como append_skipdupes sería mucho mejor.

append_skipdupes sería la manera perfecta de manejar esto.

sí, append_skipdupes +1

Acordé que sería bueno poder lidiar con esto con opciones en df.to_sql() .

Aquí está la solución que uso en sqlite:

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

Luego, cuando inserto duplicados, se ignoran silenciosamente y los no duplicados se procesan correctamente. En mi caso, los datos son (es decir , deberían ser ) estáticos, por lo que no necesito actualizarlos. Es solo que la forma de la fuente de datos es tal que obtendré duplicados que se pueden ignorar.

otra solución alternativa con MariaDb y MySql:
df.to_csv("test.csv")
entonces usa:
LOAD DATA INFILE 'test.csv' IGNORE INTO TABLE mytable o
LOAD DATA INFILE 'test.csv' REPLACE INTO TABLE mytable .

LOAD DATA es mucho más 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()))

Creo que esto se está abordando en #29636 con el argumento upsert_ignore , que aborda #14553.

append_skipdupes +1

+1 por append_skipdupes

De acuerdo, se debe agregar 'append_skipdupes'.

Sí, por favor. Se debe agregar 'append_skipdupes' y no solo para la columna Clave principal. Si hay duplicados entre otras columnas únicas, también debe omitir la adición de esas nuevas filas duplicadas.

+1 por append_skipdupes

append_skipdupes +1

append_skipdupes +1

+1 por append_skipdupes

Mientras tanto puedes usar este https://pypi.org/project/pangres/

¿Fue útil esta página
0 / 5 - 0 calificaciones