Bonjour,
Après des heures pour essayer de débugger un script pour recopier une base de données dans une autre, j’abandonne et je viens solliciter votre aide car je ne comprends vraiment pas où se trouve le problème. J’ai une table table_name dans une première base de données que je veux recopier dans une autre table table_name dans une autre base de données. Les bases de données étant grandes, je veux aussi enregistrer l’avancée du recopiage de chaque ligne et enregistrer les éventuels erreurs.
Alors j’établis d’abord les connexions et je crée les curseurs
conn_origin = mysql.connector.connect(...) #J'ai testé les connexions, elles marchent bien
conn_copy = mysql.connector.connect(...)
cursor_origin = conn_origin.cursor()
cursor_copy = conn_copy.cursor()
Comme j’ai un fichier qui enregistre l'id de chaque ligne bien recopiée, j’aimerais bien s’il y a un problème récupérer cet id et commencer à partir de la ligne d’après, donc je définis cette fonction :
def find_id(file_path):
with open(file_path, 'r') as f:
for line in f:
pass
return int(''.join(char for char in line if char.isdigit()))
Maintenant je peux créer la fonction qui va recopier les données :
def copy_data(incrementer, table_name, table_columns):
#Je récupère le dernier id et je commence à partir de lui, ou j'initialise à 1
if os.path.isfile("copy_logs.txt"):
n_rows = find_id("copy_logs.txt")+1
else:
n_rows = 1
file1 = open("copy_logs.txt", 'a')
file2 = open("errors_logs.txt", 'a')
#Je désactive les contraintes sur les clés étrangères dans la table de copie
disable_fk = """SET foreign_key_checks=0"""
cursor_copy.execute(disable_fk)
#Je récupère la taille de la table originale pour effectuer une boucle while
length_query = """SELECT count(id) FROM """+table_name
cursor_origin.execute(length_query)
result = cursor_origin.fetchone()
length = result[0]
#Je crée un format pour les valeurs à insérer et les noms de colonnes
n_elements = len(table_columns)
table_format = '('
for i in range(n_elements):
if i==n_elements-1:
table_format += '%s)'
else:
table_format += '%s,'
columns = '('+','.join(table_columns)+')'
#Je mets la requête de recopiage
copy_query = """INSERT INTO """+table_name+' '+columns+""" VALUES """+table_format
#J'initialise à False une variable erreur pour arrêter complètement le script
#en cas d'erreur
erreur = False
#Maintenant la boucle pour recopier la table
while n_rows<length:
#Je mets ici un bloc try-except pour récupèrer les lignes à recopier
try:
retrive_query = """SELECT * FROM """+table_name+""" WHERE id BETWEEN """+str(n_rows)+""" AND """+str(n_rows+incrementer-1)
cursor_origin.execute(retrieve_query)
rows = cursor_origin.fetchall()
except:
file2.write("Il y a eu un problème.")
error = True
break
#Je mets ici une boucle pour recopier les lignes une par une
for row in rows:
try:
cursor_copy.execute(copy_query, row)
conn_copy.commit()
file1.write("On a recopié la ligne : "+str(row[0])+"\n")
except:
file2.write("Il y a eu un problème à la ligne : "+str(row[0])+"\n")
error = True
break
#Il faut tout arrêter s'il y a une seule erreur
if error:
break
n_rows += incrementer
file1.close()
file2.close()
Et bien sûr je ferme les connexions à la fin :
conn_origin.close()
conn_copy.close()
J’ai remarqué ça pendant plusieurs vains essais, le script se déroule jusqu’à la fin, c’est à dire jusqu’à ce que n_rows>length
. Ma table initiale est de taille 467 586, et je trouve que mon copy_logs s’étend bizarrement jusqu’à 468 000. Je ne vois pas comment c’est possible, j’ai bien for row in rows
et c’est bien l'id, row[0]
, que j’écris dans le fichier texte.
Autre problème, c’est que quand je regarde la taille de la table d’arrivée, celle dans laquelle j’insère des valeurs, je trouve qu’elle est de taille 421 359. Donc il y a 46 227 lignes qui n’ont pas été recopié, je ne sais pour quelle raison, et que ces raisons n’ont pas été captées par les blocs try-except
.
Je ne comprends vraiment pas d’où viennent ces problèmes, j’ai aussi essayé de mettre des try-except
à chaque fois qu’il y a un execute
mais en vain. Toujours les mêmes problèmes.
Je sais que pour recopier une table dans une autre on peut directement faire une grande requête et utiliser fetchmany()
mais on m’a demandé explicitement d’opérer de cette façon là.
J’espère que vous pourrez m’aider à y voir plus clair car j’ai beau à me casser la tête et à essayer de débugger le code je n’y arrive pas. Merci d’avance pour toute idée, clarification ou autre.