pythonmysqlpymysqlexecutemany

Mysql execute many wont work when add on duplicate


I have mysql table and I want to run the executemany for that table. I want to make sure, if there is duplicate entry, then it should be updated. I wrote query as below with data.

a = 'INSERT INTO tabl (switch_id, readiness, message) VALUES (%s, %s, %s) ON DUPLICATE KEY UPDATE readiness=%s, message=%s'
b = [(12780, 'not_ready', 'StatusDB data', 'not_ready', 'StatusDB data.'),
     (12781, 'not_ready', 'StatusDB data.', 'not_ready', 'StatusDB data.')]

When I try to do execute many on these data with CONN of my database connection object, it gives error.

>>> with CONN.cursor() as c:
...     c.executemany(a, [b[0]])
...
Traceback (most recent call last):
  File "<console>", line 2, in <module>
  File "/home/myuser/var/virtualenvs/venv//lib/python3.8/site-packages/django/db/backends/utils.py", line 69, in executemany
    return self._execute_with_wrappers(sql, param_list, many=True, executor=self._executemany)
  File "/home/myuser/var/virtualenvs/venv//lib/python3.8/site-packages/django/db/backends/utils.py", line 75, in _execute_with_wrappers
    return executor(sql, params, many, context)
  File "/home/myuser/var/virtualenvs/venv//lib/python3.8/site-packages/django/db/backends/utils.py", line 89, in _executemany
    return self.cursor.executemany(sql, param_list)
  File "/home/myuser/var/virtualenvs/venv//lib/python3.8/site-packages/django/db/backends/mysql/base.py", line 83, in executemany
    return self.cursor.executemany(query, args)
  File "/home/myuser/var/virtualenvs/venv//lib/python3.8/site-packages/pymysql/cursors.py", line 182, in executemany
    return self._do_execute_many(
  File "/home/myuser/var/virtualenvs/venv//lib/python3.8/site-packages/pymysql/cursors.py", line 205, in _do_execute_many
    v = values % escape(next(args), conn)
TypeError: not all arguments converted during string formatting

When I remove the ON DUPLICATE syntaxt, it works fine.

>>> with CONN.cursor() as c:
...     c.executemany(a[:-49], [b[0][:3]])
...
Traceback (most recent call last):
  File "/home/myuser/var/virtualenvs/venv//lib/python3.8/site-packages/django/db/backends/utils.py", line 89, in _executemany
    return self.cursor.executemany(sql, param_list)
  File "/home/myuser/var/virtualenvs/venv//lib/python3.8/site-packages/django/db/backends/mysql/base.py", line 83, in executemany
    return self.cursor.executemany(query, args)
  File "/home/myuser/var/virtualenvs/venv//lib/python3.8/site-packages/pymysql/cursors.py", line 182, in executemany
    return self._do_execute_many(
  File "/home/myuser/var/virtualenvs/venv//lib/python3.8/site-packages/pymysql/cursors.py", line 220, in _do_execute_many
    rows += self.execute(sql + postfix)
  File "/home/myuser/var/virtualenvs/venv//lib/python3.8/site-packages/pymysql/cursors.py", line 153, in execute
    result = self._query(query)
  File "/home/myuser/var/virtualenvs/venv//lib/python3.8/site-packages/pymysql/cursors.py", line 322, in _query
    conn.query(q)
  File "/home/myuser/var/virtualenvs/venv//lib/python3.8/site-packages/pymysql/connections.py", line 558, in query
    self._affected_rows = self._read_query_result(unbuffered=unbuffered)
  File "/home/myuser/var/virtualenvs/venv//lib/python3.8/site-packages/pymysql/connections.py", line 822, in _read_query_result
    result.read()
  File "/home/myuser/var/virtualenvs/venv//lib/python3.8/site-packages/pymysql/connections.py", line 1200, in read
    first_packet = self.connection._read_packet()
  File "/home/myuser/var/virtualenvs/venv//lib/python3.8/site-packages/pymysql/connections.py", line 772, in _read_packet
    packet.raise_for_error()
  File "/home/myuser/var/virtualenvs/venv//lib/python3.8/site-packages/pymysql/protocol.py", line 221, in raise_for_error
    err.raise_mysql_exception(self._data)
  File "/home/myuser/var/virtualenvs/venv//lib/python3.8/site-packages/pymysql/err.py", line 143, in raise_mysql_exception
    raise errorclass(errno, errval)
pymysql.err.IntegrityError: (1062, "Duplicate entry '12780' for key 'tabl.PRIMARY'")

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<console>", line 2, in <module>
  File "/home/myuser/var/virtualenvs/venv//lib/python3.8/site-packages/django/db/backends/utils.py", line 69, in executemany
    return self._execute_with_wrappers(sql, param_list, many=True, executor=self._executemany)
  File "/home/myuser/var/virtualenvs/venv//lib/python3.8/site-packages/django/db/backends/utils.py", line 75, in _execute_with_wrappers
    return executor(sql, params, many, context)
  File "/home/myuser/var/virtualenvs/venv//lib/python3.8/site-packages/django/db/backends/utils.py", line 89, in _executemany
    return self.cursor.executemany(sql, param_list)
  File "/home/myuser/var/virtualenvs/venv//lib/python3.8/site-packages/django/db/utils.py", line 90, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "/home/myuser/var/virtualenvs/venv//lib/python3.8/site-packages/django/db/backends/utils.py", line 89, in _executemany
    return self.cursor.executemany(sql, param_list)
  File "/home/myuser/var/virtualenvs/venv//lib/python3.8/site-packages/django/db/backends/mysql/base.py", line 83, in executemany
    return self.cursor.executemany(query, args)
  File "/home/myuser/var/virtualenvs/venv//lib/python3.8/site-packages/pymysql/cursors.py", line 182, in executemany
    return self._do_execute_many(
  File "/home/myuser/var/virtualenvs/venv//lib/python3.8/site-packages/pymysql/cursors.py", line 220, in _do_execute_many
    rows += self.execute(sql + postfix)
  File "/home/myuser/var/virtualenvs/venv//lib/python3.8/site-packages/pymysql/cursors.py", line 153, in execute
    result = self._query(query)
  File "/home/myuser/var/virtualenvs/venv//lib/python3.8/site-packages/pymysql/cursors.py", line 322, in _query
    conn.query(q)
  File "/home/myuser/var/virtualenvs/venv//lib/python3.8/site-packages/pymysql/connections.py", line 558, in query
    self._affected_rows = self._read_query_result(unbuffered=unbuffered)
  File "/home/myuser/var/virtualenvs/venv//lib/python3.8/site-packages/pymysql/connections.py", line 822, in _read_query_result
    result.read()
  File "/home/myuser/var/virtualenvs/venv//lib/python3.8/site-packages/pymysql/connections.py", line 1200, in read
    first_packet = self.connection._read_packet()
  File "/home/myuser/var/virtualenvs/venv//lib/python3.8/site-packages/pymysql/connections.py", line 772, in _read_packet
    packet.raise_for_error()
  File "/home/myuser/var/virtualenvs/venv//lib/python3.8/site-packages/pymysql/protocol.py", line 221, in raise_for_error
    err.raise_mysql_exception(self._data)
  File "/home/myuser/var/virtualenvs/venv//lib/python3.8/site-packages/pymysql/err.py", line 143, in raise_mysql_exception
    raise errorclass(errno, errval)
django.db.utils.IntegrityError: (1062, "Duplicate entry '12780' for key 'tabl.PRIMARY'")

Its giving DUPLICATE error, but ultimately its able to reach to database. While when we use original query, it gives error for paramater.

If I loop through parameter and run cursor.execute it works fine with ON DUPLICATE syntaxt.

Whey its not able to match all the parameter when we use executemany ?


Solution

  • I think the issue is that you can't use parameterized values in the ON DUPLICATE KEY UPDATE portion when using executemany. You have to refer back to a column value that was already given in the VALUES clause.

    Try this:

    a = 'INSERT INTO tabl (switch_id, readiness, message) VALUES (%s, %s, %s) ON DUPLICATE KEY UPDATE readiness=VALUES(readiness), message=VALUES(message)'
    b = [(12780, 'not_ready', 'StatusDB data'),
         (12781, 'not_ready', 'StatusDB data.']
    

    Also see this answer https://stackoverflow.com/a/52260250/494134