Curseurs SQL (tech)
Sommaire |
Introduction
Les requêtes SQL (TQuery) permettent d'accéder à une ou plusieurs tables d'une base de données en utilisant des instructions SQL.
A la différence des curseurs, les requêtes SQL ne s’appuient pas sur le modèle objet mais sur le modèle relationnel sous-jacent.
Elles ne doivent donc être utilisées que dans les cas où les fonctionnalités orientées objets disponibles s’avèrent insuffisantes.
Généralement l’utilisation d’une requête SQL suit le schéma suivant :
- L’objet requête est créé avec la fonction QueryBroker.
- Le code SQL est défini.
- La requête est ouverte.
- Tant qu’il existe des données dans le résultat la requête est « fetchée » ; les données sont retournées ligne par ligne dans cette boucle.
- La requête est fermée.
Bien que l’écriture du code SQL soit libre, le développeur doit respecter un certain nombre de règles pour garantir que ce code soit portable d’un serveur à l’autre.
Déclaration d'une variable requête SQL
Les requêtes SQL doivent être déclarées :
var q:TQuery;
Création d'une requête SQL
Une requête est créée avec la fonction globale QueryBroker.
function QueryBroker(const iDatabaseURL,iTraceName,iClasseName:string):TQuery;
iDatabaseURL | string | URL de la base de données. Si ce paramètre est vide la base de données par défaut est utilisée. |
iTraceName | string | Identifiant de la requête dans la trace. |
iClasseName | string | Nom de la classe métier concernée.
Si cette classe est fournie le résultat de la requête peut être mappé sur un objet métier de la classe indiquée, la propriété Instance est alors utilisable. La requête peut alors être utilisée comme un curseur. |
Création d'une requête SQL ODBC
Une requête SQL ODBC utilise le pilote ODBC inclu dans le framework 1000 et peut être exécutée sur une source ODBC quelconque.
function ODBCQueryBroker(const iODBC_DSN:string; const iTraceName:string):TQuery;
iODBC_DSN | string | Chaîne d'identification ODBC de connexion |
iTraceName | string | Identifiant de la requête dans la trace. |
Propriétés
Propriétés de paramétrage de la requête
ACLASSNAME | string | Nom de la classe métier sur laquelle est mappé la requête. |
UPDATEQUERY | Boolean | Indique si cette requête est une requête de mise à jour.
Si cette propriété est positionnée une requête de mise à jour sera utilisé; sinon une requête de lecture sera utilisée. |
SQL | TStrings | Ordre SQL de la requête |
TRANSLATOR | Translateur SQL | Objet technique permettant de formatter le code SQL en fonction du pilote de base de données |
Propriétés à l'exécution
la requête====
INSTANCE | Objet métier | Instance en cours, n'est utilisable que si un nom de classe métier a été utilisée lors de l'appel à QueryBroker et que tous les attribut de la classe ont été retournés par la requête. |
EOF | Boolean | Testez Eof pour déterminer si le curseur est positionné sur le dernier enregistrement d'un ensemble de données. |
FIELDCOUNT | Integer | Consultez la propriété FieldCount pour déterminer le nombre de champs associés à l'ensemble de données. |
FIELDS [ INDEX ] | TField | Utilisez Fields pour accéder aux composants champs en mode indexé. |
Méthodes
OPEN | procedure Open; |
Ouvre l'ensemble de données. |
OPENNE | procedure OpenNE; |
Ouvre l’ensemble de données sans provoquer d’exception en cas d’erreur. |
FIRST | procedure First; |
Appelez First pour placer le curseur sur le premier enregistrement de l'ensemble de données. |
NEXT | procedure Next; |
Appelez Next pour placer le curseur sur l'enregistrement suivant de l'ensemble de données. |
CLOSE | procedure Close; |
Ferme un ensemble de données. |
CANCEL | procedure Cancel; |
Termine la requête en interrompant l’exécution sur le serveur. |
CLEAR | procedure Clear; |
Réinitialise le contenu SQL de la requête. |
EXECSQL | procedure ExecSQL; |
La méthode ExecSQL exécute l'instruction SQL affectée à la propriété SQL. ExecSQL est également utilisée pour exécuter les requêtes ne renvoyant pas de curseur aux données (telles que INSERT, UPDATE, DELETE). |
FIELDBYNAME | function FieldByName (const iFieldName : string):TField; |
Appelez FieldByName pour obtenir les informations sur un champ lorsque vous en connaissez le nom. FieldName indique le nom d'un champ existant. FieldByName renvoie le composant TField du champ spécifié. |
NEXTRESULTSET | procedure NextResultSet; |
Passe à l'ensemble résultat suivant dans le cas d'une requête multi-résultat. |
L'objet TFIELD représente un champ d'un ensemble de données. Il dispose des propriétés suivantes :
FIELDNAME | string | Indique le nom de la colonne physique de l'ensemble de données. |
NAME | string | Contient le nom du composant. |
ASVARIANT | Variant | Représente la valeur du champ en type variant. |
FIELDNO | Integer | Indique le numéro d'ordre de la colonne reliée au composant champ dans la table physique sous-jacente à un ensemble de données. |
DATATYPE | Enum | Identifie le type de données du composant champ. |
DATALEN | Integer | |
SIZE | Integer | Indique la taille utilisée dans la définition du champ de la base de données physique pour les types de données gérant différentes tailles. |
Exemples
Utilisation de requête pour réaliser des mises à jour
Les requêtes SQL peuvent être utilisées pour réaliser des mises à jour ensemblistes qui ne seraient pas performante si elles étaient réalisées en technologie objet.
Dans ce scénario il faut faire attention à :
- Les requêtes SQL ne sont pas objets, elles ne peuvent pas être combinées avec une transaction objet.
- Les requêtes SQL peuvent être incluses à l'intérieur d'une transaction longue; en effet une transaction longue démarre une transaction SQL sur son BeginLongTran et la termine sur son CommitLongTran, les ordres SQL exécutés à l'intérieur sont donc soumis à la transaction.
- Si aucune transaction explicite n'est démarée la base de données est en mode AUTOCOMMIT, les ordres sont donc validés un par un et ne sont pas transactionnels.
Dans cette exemple le corps de l'exécution démarre une transaction longue et appel une série de mise à jour à l'intérieur de la transaction :
//Procedure doBigUpdate; begin ClassManager.BeginLongTran(1,'TTiers'); try _majService; _majContactEtablissement; _majSiteUnique; ... ClassManager.CommitLongTran; except ClassManager.RollBackLongTran; raise; end; end;
Notez que dans cette utilisation d'une transaction longue BatchLongTran est inutile car il ni a pas d'objet métiers participant à la transaction à moins que vous combiniez usage de requête SQL et mise à jour d'objets métiers.
Les procédures de mise à jour s'écrivent ainsi :
//Procedure _majService; var vCursorUpdate: TQuery; vSQL : String; vTService : String; vTSocieteOperateur : String; begin ProgressMessage(_TP('Mise à jour du service')); vCursorUpdate := QueryBroker('','TSite',''); vTService := ClassManager.FindClassTableName('TService'); vTSocieteOperateur := ClassManager.FindClassTableName('TSocieteOperateur'); vSQL := ' UPDATE %s SET oidresponsable = ( SELECT oidInterlocuteurSociete FROM %s WHERE (oidContactEtablissement = %s.oidresponsable) ) WHERE EXISTS ( SELECT * FROM %s WHERE (oidContactEtablissement = %s.oidresponsable) )'; vSQL := Format(vSQL,[vTService, vTSocieteOperateur, vTService, vTSocieteOperateur, vTService]); vCursorUpdate.Sql.Add(vSQL); vCursorUpdate.ExecSQL; vCursorUpdate.Close; end;
Utilisation de requête pour retrouver la valeur d'un attribut
Dans cet exemple une requête est utilisée pour retrouvé la valeur en base de données d'un objet chargé en mémoire :
var vSumpDispo:TQuery; vSum:Double; begin // // QUERYBROKER permet de recuperer la valeur de la BD au lieu de celle de l'instance // en mémoire, nécessaire car on prends en compte les reservations. // vSumDispo := QueryBroker('','TCAPACITEENTREESTOCK',''); vSumDispo.SQL.Add('SELECT quantiteSortie qte FROM '+ClassManager.FindClassTableName('TCAPACITEENTREESTOCK')); vSumDispo.SQL.Add('WHERE (oid = ' + vSumDispo.Translator.dbOutOid(InstanceOid) + ')'); vSumDispo.Open; vSumDispo.First; vSum := vSumDispo.FieldByName('qte').AsVariant; vSumDispo.Close; end;
— Code métier — Développement DSM —