Python et DB: comprendre le curseur DBI

J'ai étudié le curseur qui apparaît lors de l'utilisation d'une base de données avec Python car il est trop inconnu. Nous avons étudié psycopg2 pour PostgreSQL et MySQLdb pour MySQL du point de vue de la différence entre SQL CURSOR et Python cursor et de la fidélité avec laquelle SQL CURSOR est implémenté.

Question

Conclusion

Le texte est long, donc dès la conclusion.

  1. Python cursor a environ trois types d'implémentations.
  1. Dans psycopg2 pour PostgreSQL, déclarer un ** curseur nommé ** est côté serveur. Ce serait côté client sans nom.
  2. MySQLdb pour MySQL est uniquement côté client.

Implémentation du curseur Python

type DB DBI méthode de déclaration du curseur
Du côté serveur PostgreSQL psycopg2 cursor('Nom')
Côté client(Avec tampon) PostgreSQL psycopg2 cursor()
Côté client(Avec tampon) MySQL MySQLdb cursor()
Côté client(Pas de tampon) MySQL MySQLdb connect( cursorclass = MySQLdb.cursors.SSCursor )Comme curseur()

Table de correspondance de SQL CURSOR et de curseur Python

La relation entre le curseur côté serveur ("SSC") et la méthode Python est la suivante: Il ne peut pas être utilisé avec le curseur côté client ("CSC") sauf indication contraire.

SQL Python Remarques
DECLARE CURSOR name curseur dans psycopg2('name') Non normalisé selon l'implémentation du pilote. Une des faiblesses de Python.
FETCH NEXT fetchone() Broderie au SCC
FETCH FORWARD n fetchmany(n) Broderie au SCC
FETCH ALL fetchall() Broderie au SCC
FETCH PRIOR N'est pas applicable scroll(-2, mode='relative'); fetchone()Peut être remplacé par
FETCH FIRST N'est pas applicable scroll(-1, mode='absolute'); fetchone()Peut être remplacé par
FETCH LAST N'est pas applicable scroll(-2, mode='absolute'); fetchone()Peut être remplacé par
MOVE mode value scroll(value, mode) Dans CSC, cela devient une émulation et vous ne pouvez qu'avancer. J'obtiens une exception NotSupportedError lorsque j'essaye de revenir en arrière.
CURRENT OF Équivalent Référence à la ligne du curseur

Enquête

J'ai étudié respectivement SQL CURSOR et Python.

SQL CURSOR

Le curseur peut obtenir les résultats de requêtes telles que les instructions SELECT une par une, ou passer à la ligne précédente ou suivante. Vous pouvez également faire référence à la ligne actuelle à partir d'autres instructions SQL. Les détails sont omis car le but n'est pas d'expliquer la grammaire.

Déclaration du curseur

Le curseur est déclaré comme ceci.

DECLARE nom du curseur Instruction CURSOR FOR SELECT

Obtenir la ligne

Pour obtenir la ligne suivante du curseur, exécutez une instruction similaire à la suivante: La direction normale est «NEXT» ou «FORWARD» pour obtenir la ligne tout en avançant. Il est également possible de retourner au milieu, auquel cas spécifier «PRIOR» ou «BACKWARD». Il est également possible de passer au début «FIRST» ou à la fin «LAST» à la fois.

FETCH direction FROM nom du curseur

Déplacez le curseur

Utilisez MOVE si vous voulez juste vous déplacer sans avoir de ligne. La spécification de direction est la même que FETCH.

MOVE direction FROM nom du curseur

Référence à la ligne du curseur

Vous pouvez également faire référence à la ligne de curseur à partir d'autres instructions. Si vous faites cela, vous pouvez facilement effectuer d'autres opérations tout en utilisant le résultat de SELECT. Par exemple, pour voir une ligne où un curseur est actuellement récupéré, utilisez CURRENT OF comme suit:

UPDATE names SET name='tomochi'WHERE CURRENT OF nom du curseur

Python cursor

cursor est créé en appelant la méthode .cursor () de l'objet de connexion. Les transactions fonctionnent sur des objets de connexion, donc si vous créez plusieurs curseurs à partir d'un objet de connexion, ils seront exécutés dans une transaction.

méthode du curseur (partie)

Seules les méthodes susceptibles d'être liées à SQL CURSOR seront couvertes.

Expérience

Expérimentez pour voir si le curseur Python déclare et utilise SQL CURSOR. La méthode est Activez la journalisation des instructions dans PostgreSQL et MySQL respectivement, exécutez les curseurs normaux et les curseurs nommés, et comparez les instructions SQL réellement émises.

PostgreSQL + psycopg2 Redémarrez avec log_statement = 'all' dans postgresql.conf. Créez une base de données PostgreSQL de manière appropriée et placez-y la table et les données de test.

names.sql


CREATE TABLE names (
  name varchar(100)
);

INSERT INTO names VALUES ('kenji');
INSERT INTO names VALUES ('keigo');

Curseur sans nom

psql1.py


import psycopg2
conn = psycopg2.connect(database='kenji')
cu = conn.cursor()
cu.execute('SELECT * FROM names')
print cu.fetchone()
conn.close()

Le résultat est le suivant: Il n'y a pas de déclaration du curseur côté serveur et toutes les lignes sont récupérées.

LOG:  statement: BEGIN
LOG:  statement: SELECT * FROM names

Curseur nommé

Ensuite, essayez SELECT avec le curseur nommé de la même manière. La seule différence par rapport à ce qui précède est le nom donné au curseur ().

psql2.py


import psycopg2
conn = psycopg2.connect(database='kenji')
cu = conn.cursor('foo')
cu.execute('SELECT * FROM names')
print cu.fetchone()
conn.close()

Le résultat montre que le curseur est déclaré côté serveur et que FETCH est en cours d'exécution.

LOG:  statement: BEGIN
LOG:  statement: DECLARE "foo" CURSOR WITHOUT HOLD FOR SELECT * FROM names
LOG:  statement: FETCH FORWARD 1 FROM "foo"

MySQL + MySQLdb Ajoutez la ligne suivante à la section [mysqld] dans my.cnf et redémarrez.

general_log_file        = /var/log/mysql/mysql.log
general_log             = 1

Curseur normal

msql1.py


import MySQLdb
conn = MySQLdb.connect(db='kenji', user='kenji')
cu = conn.cursor()
cu.execute('SELECT * FROM names')
print cu.fetchone()
conn.close()

Le résultat est, comme prévu, un simple SELECT sans curseur.

Connect	kenji@localhost on kenji
Query	set autocommit=0
Query	SELECT * FROM names
Quit

SSCursor Ensuite, expérimentons en utilisant la classe de curseur décrite comme "curseur côté serveur" dans le manuel MySQLdb. Il est activé en passant cursorclass = MySQLdb.cursors.SSCursor à connect ().

msql2.py


import MySQLdb
import MySQLdb.cursors
conn = MySQLdb.connect(db='kenji', user='kenji',
                       cursorclass = MySQLdb.cursors.SSCursor)
cu = conn.cursor()
cu.execute('SELECT * FROM names')
print cu.fetchone()
conn.close()

Le résultat est le même qu'un curseur normal. Aucun curseur côté serveur n'a été déclaré.

Connect	kenji@localhost on kenji
Query	set autocommit=0
Query	SELECT * FROM names
Quit

Quand je lis la source, c'est une implémentation surprenante qui ** bloque en ne lisant pas les données du socket **. Ne serait-il pas possible d'exécuter d'autres instructions s'il était bloqué au niveau de la connexion? Il y a deux lignes dans la table des noms, mais que se passe-t-il si j'obtiens la première ligne et que je la sélectionne séparément?

msql3.py


import MySQLdb
import MySQLdb.cursors
conn = MySQLdb.connect(db='kenji', user='kenji',
                       cursorclass = MySQLdb.cursors.SSCursor)
cu1 = conn.cursor()
cu2 = conn.cursor()
cu1.execute('SELECT name as name1 FROM names')
print "CU1", cu1.fetchone()
cu2.execute('SELECT name as name2 FROM names')
print "CU2", cu2.fetchone()
conn.close()

L'exécution a été refusée.

$ python msql3.py
CU1 ('kenji',)
Traceback (most recent call last):
  File "msql3.py", line 9, in <module>
    cu2.execute('SELECT name as name2 FROM names')
  File "/usr/lib/python2.7/dist-packages/MySQLdb/cursors.py", line 174, in execute
    self.errorhandler(self, exc, value)
  File "/usr/lib/python2.7/dist-packages/MySQLdb/connections.py", line 36, in defaulterrorhandler
    raise errorclass, errorvalue
_mysql_exceptions.ProgrammingError: (2014, "Commands out of sync; you can't run this command now")
Exception _mysql_exceptions.ProgrammingError: (2014, "Commands out of sync; you can't run this command now") in <bound method SSCursor.__del__ of <MySQLdb.cursors.SSCursor object at 0x7f6d9b542f50>> ignored
Exception _mysql_exceptions.ProgrammingError: (2014, "Commands out of sync; you can't run this command now") in <bound method SSCursor.__del__ of <MySQLdb.cursors.SSCursor object at 0x7f6d9b542e50>> ignored

"vous ne pouvez pas exécuter cette commande maintenant". Je ne comprends pas entièrement le Manuel du connecteur MySQL (bibliothèque client), mais les résultats Il semble que l'instruction suivante ne puisse pas être exécutée dans le même thread car elle n'a pas été complètement lue. Il sera dans un état où il pourra être lu mais pas écrit à partir d'autres threads. SSCursor est inutile.

Référence au curseur côté serveur

Utilisez un curseur côté serveur et un curseur normal dans PostgreSQL. Accédez au curseur côté serveur. Un exemple très simple de mise à jour de'kenji 'dans la table des noms en' omochi '.

psycopg2_named_cursor_example.py


import psycopg2

conn = psycopg2.connect(database='kenji')

cu1 = conn.cursor('foo')   #Curseur côté serveur 1
cu2 = conn.cursor()  #Curseur normal 2

#Vous n'êtes pas obligé de verrouiller la ligne, mais d'une manière ou d'une autre.
cu1.execute("SELECT name FROM names WHERE name=%s FOR UPDATE;", ('kenji',))

print cu1.fetchone()

cu2.execute("UPDATE names SET name='tomochi' WHERE CURRENT OF foo;")   #METTRE À JOUR la ligne actuelle du curseur 1
cu2.close()

conn.commit()
conn.close()

Le journal de suivi est le suivant. Le SQL s'exécute comme prévu.

LOG:  statement: BEGIN
LOG:  statement: DECLARE "foo" CURSOR WITHOUT HOLD FOR SELECT name FROM names WHERE name='kenji' FOR UPDATE
LOG:  statement: FETCH FORWARD 1 FROM "foo"
LOG:  statement: UPDATE names SET name='tomochi' WHERE CURRENT OF foo
LOG:  statement: COMMIT

«kenji» dans la table des noms a été mis à jour en «omochi ».

kenji=> select * from names;
  name
---------
 keigo
 tomochi
(2 rows)

Déplacez le curseur

J'ai également essayé l'expérience scroll (), mais elle a été omise car il n'y avait aucune différence par rapport au SQL à noter.

Recommended Posts

Python et DB: comprendre le curseur DBI
Compréhension complète du threading Python et du multitraitement
Comprendre Python Coroutine
Comprendre l'auto python
[Python] Une compréhension approximative des itérables, des itérateurs et des générateurs
Astuces Python et Numpy
[Python] pip et roue
Itérateur et générateur Python
Paquets et modules Python
Intégration Vue-Cli et Python
Essayez le fonctionnement de la base de données avec Python et visualisez avec d3
Ruby, Python et carte
LiNGAM (version ICA) à comprendre avec des formules mathématiques et Python
entrée et sortie python
Python et Ruby se séparent
Traitement asynchrone de Python ~ Comprenez parfaitement async et attendez ~
Obtenez une compréhension abstraite des modules et des packages Python
Python asyncio et ContextVar
[Introduction à cx_Oracle] (Partie 6) Mappage des types de données DB et Python
Problèmes et solutions à la demande de MySQL db dans Python 3
Programmation avec Python et Tkinter
Chiffrement et déchiffrement avec Python
Python: variables de classe et d'instance
3-3, chaîne Python et code de caractère
Série Python 2 et série 3 (édition Anaconda)
Python et matériel - Utilisation de RS232C avec Python -
Python sur Ruby et Ruby en colère sur Python
division des nombres réels python (/) et division des nombres entiers (//)
Installez Python et Flask (Windows 10)
À propos des objets et des classes Python
À propos des variables et des objets Python
Apache mod_auth_tkt et Python AuthTkt
Å (Ongustorome) et NFC @ Python
Apprenez à connaître les packages et les modules Python
# 2 [python3] Séparation et commentaire
Copie superficielle Python et copie profonde
Mémo tranche python et rubis
Accéder à Oracle DB depuis Python
Installation de Python et grammaire de base
J'ai comparé Java et Python!
Copie superficielle Python et copie profonde
À propos de Python, len () et randint ()
À propos de la date et du fuseau horaire Python
Installez Python 3.7 et Django 3.0 (CentOS)
Paiza Python Primer 8: Comprendre les classes
Construction d'environnement Python et TensorFlow
Variables de classe et d'instance Python
Syntaxe Ruby et Python ~ branch ~
[Python] Python et sécurité-① Qu'est-ce que Python?
Pile et file d'attente en Python
métaclasse python et déclaration sqlalchemy
Implémentation de Fibonacci et des nombres premiers (python)
bases de python: conditions et itérations
Opérateur de bits Python et somme logique
Module de débogage et de test Python
Liste Python et tapples et virgules
Variables Python et ID d'objet
Notation et générateur d'inclusion de liste Python
À propos de Python et des expressions régulières