web-dev-qa-db-fra.com

déclaration de préparation du pilote golang sql

En ce qui concerne le pilote SQL de golang, quelle est la différence entre les deux déclarations ci-dessous?

// store.DB is *sql.DB type
rows, err := store.DB.Query(SQL, args ...)
// err != nil
defer rows.Close()

et

// store.DB is *sql.DB type
stmt, err := store.DB.Prepare(SQL)
// err != nil
defer stmt.Close()

rows, err := stmt.Query(args ...)
// err != nil
defer rows.Close()

Il semble qu'ils sont les mêmes? Y a-t-il une différence subtile?

Mettre à jour

Il n'est pas nécessaire d'exécuter plusieurs stmt.Exec ou stmt.Query après db.Prepare, une seule exec ou query est effectuée après chaque prepare. Et lorsque nous utilisons db.Query ou db.Exec, nous passons des arguments aux méthodes au lieu d'utiliser une chaîne SQL brute (pour des raisons de sécurité). 

J'ai trouvé un lien de référence: http://go-database-sql.org/prepared.html
Il semble que les deux méthodes utilisent instruction préparée, quelle est la différence?

16
jtuki

Les différences peuvent être subtiles, parfois importantes et parfois même inexistantes.

En général, une instruction préparée 1. est préparée avec le serveur (analyse SQL, plan d'exécution généré, etc.), 2. est exécutée avec les paramètres supplémentaires, puis 3. est fermée. Il vous permet de réutiliser le même code SQL avec différents paramètres transmis à chaque fois, de vous protéger contre l’injection de code SQL, d’apporter certaines améliorations en termes de performances (spécifique au pilote/protocole, YMMV) et d’empêcher des étapes répétées, telles que la génération de plans d’exécution et l’analyse SQL dans l'étape prepare ci-dessus. 

Pour une personne qui écrit du code source, une instruction préparée peut être plus pratique que de concaténer des chaînes et de les envoyer au serveur de base de données.

La méthode DB.Query() prend SQL en tant que chaîne et zéro argument ou plus (comme le fait Exec() ou QueryRow()). Une chaîne SQL sans arguments supplémentaires interrogera exactement ce que vous avez écrit. Cependant, sous réserve d'une chaîne SQL avec des espaces réservés et des arguments supplémentaires, une instruction préparée est en cours d'exécution pour vous. 

La méthode DB.Prepare() exécute explicitement une instruction préparée, à laquelle vous transmettez ensuite des arguments, comme dans: stmt.Exec(...args).

Il y a plusieurs choses qui méritent réflexion, en termes de différences entre les deux et pourquoi utiliser l'un ou l'autre.

Vous pouvez utiliser DB.Query() sans arguments. Cela peut être très efficace car il permet de contourner la séquence prepare -> execute -> close par laquelle l'instruction préparée passe nécessairement. 

Vous pouvez également l'utiliser avec des arguments et des espaces réservés supplémentaires dans la chaîne de requête. Il exécutera une instruction préparée sous les couvertures, comme indiqué plus haut. Le problème potentiel ici est que lorsque vous effectuez un certain nombre de requêtes, chacune d’elles aboutit à une instruction préparée discrète. Étant donné que des étapes supplémentaires sont impliquées, cela peut être assez inefficace car il se prépare, s'exécute et se ferme chaque fois que vous effectuez cette requête.

Avec une instruction préparée explicite, vous pouvez éventuellement éviter cette inefficacité en tentant de réutiliser le code SQL que vous avez préparé précédemment, avec des arguments potentiellement différents.

Mais cela ne fonctionne pas toujours comme prévu. En raison du pool de connexions sous-jacent géré par db/sql, votre "connexion à la base de données" est plutôt virtuelle. La méthode DB.Prepare() préparera l’instruction sur une connexion particulière, puis essaiera d’obtenir la même connexion au moment de l’exécuter, mais si cette connexion n’est pas disponible, elle en récupérera simplement une qui est disponible, puis la préparera et l’exécutera. Si vous utilisez cette même déclaration préparée maintes et maintes fois, vous pourriez aussi, sans le savoir, la préparer encore et encore. Cela se manifeste bien souvent lorsque le trafic est dense.

Il est donc évident que votre utilisation dépend de votre cas d'utilisation spécifique, mais j'espère que les détails ci-dessus vous aideront à clarifier suffisamment pour que vous puissiez prendre la meilleure décision dans chaque cas.

Mettre à jour

Étant donné la mise à jour dans OP, il n'y a pratiquement pas de différence lorsque la requête ne doit être exécutée qu'une seule fois, car les requêtes avec arguments sont effectuées sous la forme d'instructions préparées en arrière-plan. 

Utilisez les méthodes directes, par exemple DB.Query() et ses analogues, par opposition à l'utilisation explicite d'instructions préparées, dans la mesure où le code source sera un peu plus simple.

Dans la mesure où les instructions préparées, dans ce cas, sont utilisées pour des raisons de sécurité, il peut être intéressant de s’efforcer de traiter les problèmes de sécurité par d’autres moyens et d’utiliser des requêtes en texte clair à la place, afin d’améliorer les performances. Toutefois, tout gain éventuel peut être sans importance, sauf si le trafic est suffisant (ou si le trafic devrait augmenter considérablement à l'avenir) pour nécessiter un allègement de la charge du serveur. Encore une fois, cela revient au cas d'utilisation réel.

Pour ceux qui sont intéressés par certaines métriques sur la différence entre les déclarations préparées et les requêtes en texte clair directes, il existe un bon article ici (qui explique également en grande partie ce qui précède).

27
Snowman

Cela dépend un peu du pilote que vous utilisez et de savoir si votre logiciel de base de données prend en charge les requêtes non préparées.

Prepare se connecte à la base de données et crée une instruction préparée, liée à cette connexion à la base de données. Une fois exécuté, il vérifie si cette connexion est toujours disponible et, dans le cas contraire, crée une nouvelle connexion, prépare à nouveau la fonction, puis l'exécute.

Edit: Remarque: si vous ne connectez qu'une base de données à la fois à la base de données et que vous exécutez la même requête à plusieurs reprises avec des arguments différents, alors Prepare peut être plus rapide, car il stocke la requête sur le Côté DB. Cependant, le détournement de connexion par d'autres routines annule cette impression et si vous n'utilisez la requête qu'une seule fois, vous ne tirez aucun avantage de Prepare.

Query effectue l'une des deux choses selon le pilote. Si le type Conn renvoyé par le pilote à partir de Open() a également une méthode Query() et prend donc en charge les requêtes directes, alors sql.Query() appelle directement cela et renvoie ses résultats. Si Conn n'a pas une méthode Query(), sql.Query() prépare une instruction, puis l'exécute.

Par exemple, le pilote pq pour PostgreSQL (github.com/lib/pq) a une méthode Query(). Cette méthode prépare et exécute, comme le paquet sql le ferait, mais a également une alternative: si la requête n'a pas d'argument, elle s'exécute à l'aide de l'interface simpleQuery, qui note que le paquet est considérablement plus rapide qu'un outil prepare-and- exécuter le cycle.

0
Kaedys