Questions relatives aux opérations de tri, recherche, filtrage (FireDAC)
Remonter à FAQ (FireDAC)
Cette rubrique liste des questions et réponses relatives aux opérations de tri, recherche et filtrage des ensembles de données.
Q1 : L'ensemble de données ne trie pas correctement les chaînes non anglaises. J'obtiens l'erreur "[FireDAC][DatS]-2. Objet [] introuvable". Quel est le problème ?
R : Pour corriger ce problème, procédez comme suit :
- Ouvrez $(BDS)\source\data\firedac\FireDAC.inc ;
- Trouvez les lignes :
{$define FireDAC_NOLOCALE_DATA} // define to use binary data comparison
{$define FireDAC_NOLOCALE_META} // define to use binary metadata comparison
- Commentez-les ;
- Enregistrez le fichier et recompilez votre application.
Remarque : Si vous utilisez uniquement des textes en anglais (ASCII), assurez-vous que les lignes ci-dessus sont décommentées. Cela accélère considérablement les opérations de tri et de recherche.
Q2 : Est-il nécessaire de créer un index pour ordonner les données ?
R : Vous n'avez pas besoin de définir des index si vous devez uniquement ordonner des données. IndexFieldNames s'en occupe à votre place. Les index vous permettent de définir les vues, qui sont un mélange de filtrage et de classement. Notez que les index et IndexDefs sont mutuellement exclusifs pour tous les ensembles de données FireDAC. En d'autres termes, vous devez remplir les index ou IndexDefs, mais pas les deux.
Q3 : Puis-je utiliser des champs de référence dans les définitions d'index ?
R : Non. En revanche, vous pouvez utiliser des champs calculés internes dans les index. Pour ce faire, ajoutez des champs persistants, puis ajoutez le champ fkInternalCalc, créez des gestionnaires d'événement OnCalcFields et calculez ce champ dans le gestionnaire d'événement. Par exemple :
procedure TForm21.FDTable1CalcFields(DataSet: TDataSet);
begin
DataSet.FieldByName('f_calc').AsString :=
FDMemTable2.Lookup('code',
DataSet.FieldByName('f_code').AsInteger, 'name');
end;
Q4 : Puis-je utiliser des champs calculés dans les définitions d'index ?
R : La version actuelle de FireDAC ne supporte pas les champs avec FieldKind = fkCalculated dans Locate, Lookup, IndexFieldNames, etc. La solution consiste à utilise des champs fkInternalCalc.
Q5 : Quand j'appelle la méthode FindNearest à l'exécution, je reçois un message d'erreur indiquant qu'il n'y a pas d'index actif.
R : L'index doit être sélectionné, pas seulement actif. Pour cela :
- Définissez Indexes[i].Selected := True
- Ou définissez FDTable1.IndexFieldName := <nom de votre index>
Q6 : Comment puis-je définir un classement de caractères différent pour l'indexation de TADDataset ?
R : En général, il existe trois options :
- Assignez LCID à TFDMemTable.Table.Locale. FireDAC utilise CompareStringA et CompareStringW avec l'indicateur SORT_STRINGSORT. Le classement par défaut est indépendant du SGBD. Il s'agit de LOCALE_USER_DEFAULT. Voir les documents de l'API Win pour plus de détails.
- Changez le code source - FireDAC.DatS.pas, TFDDatSRow.CompareData et implémentez votre propre algorithme de comparaison.
- Recensez une fonction personnalisée avec un évaluateur d'expression Voir FireDAC.Stan.Expr.pas pour plus de détails. Vous pouvez ensuite utiliser cette fonction avec TFDMemTable.Indexes[..].Expression. Par exemple : Expression := 'MySort(Name)'.
Dans les futures versions, nous implémenterons des classements personnalisés. Cela sera utile pour les pilotes SQLite, comme les vôtres.
Q7 : Comment puis-je ordonner les données de cette façon : COL1 décroissant, COL2 croissant ?
R : IndexFieldNames := 'col_1:D;col_2';
Q8 : FDQuery.Locate déclenche une "Erreur de séquence de fonctions" avec SQL Server. Quel est le problème ?
R : Il semble que FDQuery ait été ouvert après le démarrage d'une transaction explicite, puis qu'une transaction ait été validée. Vous avez ensuite appelé FDQuery.Locate, mais tous les enregistrements n'ont pas été extraits de FDQuery. A la place, Locate appelle implicitement FetchAll. C'est la raison pour laquelle cette erreur est déclenchée sur SQL Server.
Pour éviter cette erreur, procédez comme suit :
- Définissez FDQuery.FetchOptions.Mode = fmAll pour ce composant FDQuery spécifique ;
- Appelez FetchAll pour ce composant FDQuery avant d'appeler Commit.
Le comportement de SQL Server consiste à invalider les curseurs ouverts après Commit/Rollback.
Q9 : Quel est le meilleur moyen d'implémenter des tables de référence ? Elles semblent être volumineuses car l'ouverture/chargement est lent(e)
R : Si vous n'avez pas besoin de tous les enregistrements sur le client, utilisez TFDQuery avec SELECT ... WHERE ....
Q10 : Avez-vous des conseils sur la façon de gérer les champs de référence pour obtenir les meilleures performances ?
R : Définissez TFDQuery.IndexFieldNames sur LookupKeyFields, puis FireDAC utilisera l'index client pour localiser les enregistrements dans un ensemble de données de référence.
Si le nombre de valeurs de clés uniques n'est pas élevé, définissez LookupCache sur True.
Q11 : Je ne parviens pas à filtrer par la valeur DateTime. Quel est le problème ?
Q : J'utilise Delphi 2007 avec FireDAC 2.0.11.895 sur une base de données Postgres. Quand j'essaie de filtrer sur un champ DateTime, je ne parviens pas à obtenir une correspondance exacte avec une valeur transmise comme ceci :
created_date = '8/10/2009 14:42:14' // or even...
created_date = '8/10/2009 14:42:14.247' // ... the exact time with millisec
A la place, je dois utiliser quelque chose qui ressemble à :
created_date >= '8/10/2009 14:42:14' AND created_date < '8/10/2009 14:42:15' or
created_date >= '8/10/2009 14:42:14.000' AND created_date <= '8/10/2009 14:42:14.999'
R : Le problème vient du fait que le SGBD ou l'API du SGBD peut arrondir la valeur heure. Alors qu'un programmeur s'attend à voir 0,247 en partie décimale, il peut voir 0,246 ou autre chose. Ce n'est pas dû à un dysfonctionnement de FireDAC, mais au mode de fonctionnement du SGBD.
Vous pouvez arrondir la valeur heure à la seconde et comparer cette valeur avec une constante sans la partie décimale :
uses
FireDAC.Stan.ExprFuncs;
...
FDQuery1.Filter = 'TimeStampDiff(''second'', created_date,
convert(''timestamp'', ''8/10/2009 14:42:14'')) = 0';