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é.
CURSOR
, je ne l'ai jamais vu utilisé de cette manière. De quel genre de relation s'agit-il?Le texte est long, donc dès la conclusion.
cursor
a environ trois types d'implémentations.FETCH NEXT
et FETCH ALL
.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() |
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 |
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.
Le curseur est déclaré comme ceci.
DECLARE nom du curseur Instruction CURSOR FOR SELECT
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
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
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.
Seules les méthodes susceptibles d'être liées à SQL CURSOR seront couvertes.
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');
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
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
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.
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)
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