IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C# Discussion :

SqlDataReader ne lit rien


Sujet :

C#

  1. #1
    Membre émérite
    Profil pro
    Développeur Web
    Inscrit en
    Février 2008
    Messages
    2 393
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Février 2008
    Messages : 2 393
    Points : 2 711
    Points
    2 711
    Par défaut SqlDataReader ne lit rien
    Bonjour tout le monde,


    Dans un projet WinForms pour .Net Framework 4.7.2, je tente de lire les valeurs d'un enregistrement, et HasRows vaut faux, alors que sous SSMS, j'ai copié la requête en remplaçant le paramètre par sa valeur, et j'obtiens bien l'enregistrement auquel je m'attends.

    Donc, j'ai du me tromper quelque part dans mon code, mais ça ne me saute pas aux yeux ...

    À propos, pour l'interface graphique il avait fallu mettre dans la chaîne de connexion "Trust Server Certificate=True", là il a fallu que je l'enlève car ça n'était pas reconnu.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
                using (SqlConnection cnx = new SqlConnection("Data Source=.\\SQLEXPRESS;Initial Catalog=Repas;IntÀ 
                    cnx.Open();
                    using(SqlCommand cmd = new SqlCommand("SELECT LaDate, Observations, Matin, Midi, Soir FROM Repas WHERE LaDate = @laDate",cnx))
                    {
                        cmd.Parameters.AddWithValue("laDate", oldDate);
                        SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleResult);
                        reader.Read();
                        if (reader.HasRows)
                        {
                            System.Threading.Thread.Sleep(1000);
                            txbDateHeure.Text = reader[0].ToString();
                            txbSYS.Text = reader[1].ToString();
                            txbDIA.Text = reader[2].ToString();
                            txbPul.Text = reader[3].ToString();
                            txbOXY.Text = reader[4].ToString();
                        }
                        reader.Close();
                    }
                    cnx.Close();
                }
    ***
    Au demeurant j'ai bien fait de poser la question, car c'est une fois que je l'ai vue dans le forum que je me rends compte que j'ai cherché les champs d'une table, dans une autre.

    Je reste surpris qu'en rectifiant le nom de la table ça ne retourne toujours rien, mais j'ai l'impression qu'il va falloir retourner un peu tout ça.

  2. #2
    Membre émérite
    Profil pro
    Développeur Web
    Inscrit en
    Février 2008
    Messages
    2 393
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Février 2008
    Messages : 2 393
    Points : 2 711
    Points
    2 711
    Par défaut
    Ah oui, c'était particulièrement important de ne pas se tromper de table, car il se trouve que dans une des deux les dates sont ... arrondies à la date (pas d'heure, on met tout à minuit).

    Et dans l'autre, non.

    Autant c'est facile de repérer un enregistrement créé à minuit, autant si le même jour on en a un à 15:16:30, un à 15:20:18 et un à 15:25:34, et qu'on ne veut pas les mélanger, pour viser le bon ça devient tout de suite plus coriace, comme ça a été soulevé voici quelque temps déjà :
    https://www.developpez.net/forums/d1...date-datetime/

    Dans ce fil on voulait viser un enregistrement par date, ils se sont bien embêtés à tracer un intervalle sur une journée, aujourd'hui on dirait WHERE CAST(LaDate AS Date) = '2023-08-02', et puis ça serait marre.

    Mais si il y en a trois, est-ce que quelqu'un se sent de taille à donner une requête plus précise pour chacun des trois ?

    Il y aurait bien la solution de cumuler un DATEDIFF pour chacun des champs de la date, mais je redoute que ça aurait vite fait d'être lourd.

    Une meilleure idée, quelqu'un ?
    Ah, un intervalle sur une ou deux secondes, peut-être ?

    Ça serait une approximation, mais si personne n'a de meilleure idée ça va finir comme ça.

  3. #3
    Modérateur
    Avatar de sevyc64
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Janvier 2007
    Messages
    10 223
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Pyrénées Atlantiques (Aquitaine)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Janvier 2007
    Messages : 10 223
    Points : 28 213
    Points
    28 213
    Par défaut
    C'est toujours difficile de travailler avec des dates. Mais il y a quelques règles.
    - Il faut toujours travailler le plus longtemps possible avec du type Date (ou DateTime). Cela veut dire, qu'une donnée d'un type quelconque qui doit être utilisée comme Date, doit être convertie le plus tôt possible en Date, avant la première utilisation.Et on ne travaillera pas avec le type initial.
    - De même une donnée de type Date, qui doit être utilisée dans un autre type, sera convertie la plus tard possible dans ce type, juste avant cette utilisation. Et on ne travaillera pas plus avec ce nouveau type que le strict nécessaire, et juste pour cette utilisation-là.
    - Même si ça doit compliquer le code, ou si ça demande un travail supplémentaire, surtout on évite au maximum de manipuler des dates sous forme de chaines de caractères.

    - Concernant les variables, champs, objets de type Date, il faut toujours bannir les typages automatiques, implicites. il faut toujours explicitement typer ses variable du bon type que l'on souhaite.
    - De même il faut toujours bannir les transtypes, cast et autres conversions automatiques et toujours explicitement les définir.

    Ex: Dans ton code tu écrit cmd.Parameters.AddWithValue("laDate", oldDate);. On ne sait pas de type est le champ LaDate dans la base. On ne sait pas de quel type est oldDate. Ici, le paramètre laDate serait du même type que oldDate. Il sera potentiellement traduit dans un autre format pour pouvoir être intégré à la requête, puis reconverti par Sql Server pour correspondre au type du champ LaDate.
    Si ça marche la plupart du temps, avec des types Date c'est beaucoup plus aléatoire.

    Au moins pour les dates, perso, j'essaye de toujours passer par des paramètres que j’écris de la sorte :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    cmd.Parameters.Add("@laDate", System.Data.SqlDbType.DateTime);
    cmd.Parameters["@laDate"].Value = oldDate;
    Ici, le paramètre est déjà directement de type DateTime comme le champ dans la base de données. Le système se démerdera à faire les traductions qui vont bien mais au final Sql Server n'aura aucune conversion à faire, il utilisera directement la valeur reçue. Par contre, si une conversion est nécessaire, elle sera explicitement codée avant l'affectation à .Value
    Par exemple si oldDate est en réalité une chaine de caractère, il faudra écrire quelque chose du genre cmd.Parameters["@laDate"].Value = DateTime.Parse(oldDate);.

    Mais si il y en a trois, est-ce que quelqu'un se sent de taille à donner une requête plus précise pour chacun des trois ?
    Si tu as 3 enregistrement sur le même jour et que tu ne filtre que sur le jour en faisant par exemple un cast(LaDate as Date) tu auras bien tes 3 enregistrements, et heureusement.
    Si tu n'en veux qu'un seul des 3 il faudra affiner ton filtre, par exemple sur d'autres critères et colonnes en plus de la date. Si tu n'as que la date comme critère, tu n'auras effectivement pas d'autre choix que de borner un intervalle.
    Et là, 2 solutions. Soit tu passe les 2 bornes en paramètres de ta requête, ce qui revient à gérer l'intervalle dans ton code, soit tu ne passe qu'un paramètre, par exemple la borne basse, et tu calcule automatiquement l'autre borne dans la requête à partir d'un intervalle fixe déterminé.
    Par exemple pour un intervalle fixe de +4 secondes :
    Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
    ... Where LaDate between @laDate and DateAdd(second, 4, @laDate)
    pour un intervalle fixe de +/- 2 minutes :
    Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
    ... Where LaDate between DateAdd(minute, -2, @laDate) and DateAdd(minute, 2, @laDate)

  4. #4
    Membre émérite
    Profil pro
    Développeur Web
    Inscrit en
    Février 2008
    Messages
    2 393
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Février 2008
    Messages : 2 393
    Points : 2 711
    Points
    2 711
    Par défaut
    Citation Envoyé par sevyc64 Voir le message
    C'est toujours difficile de travailler avec des dates. Mais il y a quelques règles.
    - Il faut toujours travailler le plus longtemps possible avec du type Date (ou DateTime). Cela veut dire, qu'une donnée d'un type quelconque qui doit être utilisée comme Date, doit être convertie le plus tôt possible en Date, avant la première utilisation.Et on ne travaillera pas avec le type initial.
    - De même une donnée de type Date, qui doit être utilisée dans un autre type, sera convertie la plus tard possible dans ce type, juste avant cette utilisation. Et on ne travaillera pas plus avec ce nouveau type que le strict nécessaire, et juste pour cette utilisation-là.
    - Même si ça doit compliquer le code, ou si ça demande un travail supplémentaire, surtout on évite au maximum de manipuler des dates sous forme de chaines de caractères.

    - Concernant les variables, champs, objets de type Date, il faut toujours bannir les typages automatiques, implicites. il faut toujours explicitement typer ses variable du bon type que l'on souhaite.
    - De même il faut toujours bannir les transtypes, cast et autres conversions automatiques et toujours explicitement les définir.
    C'est intéressant, car je venais de me rendre compte que justement, ce qu'il y a de commun à deux dates/Heures dans la même seconde, c'est leur expression en chaîne de caractères.
    Or il se trouve que comparer des chaînes n'est pas si trivial que ça non plus.
    Si ce sont des champs de type chaîne de caractères ça va tout seul, mais si c'est le résultat d'une opération avec une formule à rallonge, ce n'est pas évident à reprendre dans une autre colonne. Une piste peut être de fournir les formules dans une requête, et de les exploiter dans une autre requête qui appelle la première.

    Alors apparemment ce n'est pas ta tasse de thé. Alors, il va falloir trouver des opérateurs adaptés pour les comparaisons de dates. Je n'ai pas passé tant que ça de temps à potasser les requêtes enregistrées standard, peut-être y a-t-il quelque chose d'intéressant là-dedans.

    Ex: Dans ton code tu écrit cmd.Parameters.AddWithValue("laDate", oldDate);. On ne sait pas de type est le champ LaDate dans la base. On ne sait pas de quel type est oldDate. Ici, le paramètre laDate serait du même type que oldDate. Il sera potentiellement traduit dans un autre format pour pouvoir être intégré à la requête, puis reconverti par Sql Server pour correspondre au type du champ LaDate.
    Si ça marche la plupart du temps, avec des types Date c'est beaucoup plus aléatoire.
    LaDate est de type DateTime, et oldDate aussi.
    LaDate est la sauvegarde de l'heure système au moment de la création de l'enregistrement (ou de l'ouverture du formulaire), oldDate est la sauvegarde de LaDate à l'ouverture d'(un autre) formulaire, pour être capable de traiter la modification du champ LaDate.

    Dans un premier jet j'avais appelé le champ Date.
    Bon, il faut bien faire des bêtises de temps en temps, hein.

    Au moins pour les dates, perso, j'essaye de toujours passer par des paramètres que j’écris de la sorte :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    cmd.Parameters.Add("@laDate", System.Data.SqlDbType.DateTime);
    cmd.Parameters["@laDate"].Value = oldDate;
    Je ne me rappelle plus comment je faisais quand j'ai commencé le projet, j'essayais quelque chose comme ça, ce sont les messages d'erreurs qui m'ont mené à AddWithValue.

    Ici, le paramètre est déjà directement de type DateTime comme le champ dans la base de données. Le système se démerdera à faire les traductions qui vont bien mais au final Sql Server n'aura aucune conversion à faire, il utilisera directement la valeur reçue. Par contre, si une conversion est nécessaire, elle sera explicitement codée avant l'affectation à .Value
    Par exemple si oldDate est en réalité une chaine de caractère, il faudra écrire quelque chose du genre cmd.Parameters["@laDate"].Value = DateTime.Parse(oldDate);.
    Si il y a des conversions à faire, bien entendu, il s'agit de ne pas se louper.
    Mais dans l'exemple tout est de type DateTime. Enfin ... Pour ce qui concerne ce champ, car il y a aussi des champs de type texte.

    Si tu as 3 enregistrement sur le même jour et que tu ne filtre que sur le jour en faisant par exemple un cast(LaDate as Date) tu auras bien tes 3 enregistrements, et heureusement.
    Si tu n'en veux qu'un seul des 3 il faudra affiner ton filtre, par exemple sur d'autres critères et colonnes en plus de la date. Si tu n'as que la date comme critère, tu n'auras effectivement pas d'autre choix que de borner un intervalle.
    Et là, 2 solutions. Soit tu passe les 2 bornes en paramètres de ta requête, ce qui revient à gérer l'intervalle dans ton code, soit tu ne passe qu'un paramètre, par exemple la borne basse, et tu calcule automatiquement l'autre borne dans la requête à partir d'un intervalle fixe déterminé.
    Par exemple pour un intervalle fixe de +4 secondes :
    Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
    ... Where LaDate between @laDate and DateAdd(second, 4, @laDate)
    pour un intervalle fixe de +/- 2 minutes :
    Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
    ... Where LaDate between DateAdd(minute, -2, @laDate) and DateAdd(minute, 2, @laDate)
    Bonne pioche, ça va m'éviter de chercher
    Date_Sub peut être pratique pour la borne inférieure.
    Tiens, c'est marrant, DateAdd s'écrit en un seul mot, et pas Date_Sub.

  5. #5
    Modérateur
    Avatar de sevyc64
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Janvier 2007
    Messages
    10 223
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Pyrénées Atlantiques (Aquitaine)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Janvier 2007
    Messages : 10 223
    Points : 28 213
    Points
    28 213
    Par défaut
    Date_Sub n'est pas du SQL Server, c'est apparemment du MySql ou du PHP.

    En SQL Server tu utilise DateAdd avec un nombre négatif

  6. #6
    Membre émérite
    Profil pro
    Développeur Web
    Inscrit en
    Février 2008
    Messages
    2 393
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Février 2008
    Messages : 2 393
    Points : 2 711
    Points
    2 711
    Par défaut
    Ah, oui, pas la peine de se fourvoyer sur une fausse piste.

    Quand je pense au nombre de fois où je mets "WinForms" dans la requête et la première réponse concerne Access ...

  7. #7
    Membre émérite
    Profil pro
    Développeur Web
    Inscrit en
    Février 2008
    Messages
    2 393
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Février 2008
    Messages : 2 393
    Points : 2 711
    Points
    2 711
    Par défaut
    De toute manière on démarre sur de bonnes bases.

    Si je tape
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SELECT LaDate < '2023-09-01T17:43:52' FROM Tension
    on me répond "Syntaxe incorrecte vers '<'." (msg 102).

    Donc là, la question n'est même plus de savoir si c'est inférieur ou supérieur, mais si l'erreur est avant le signe < ou après, ou si c'est le signe < lui-même qui est incorrect.

    ***
    Ah oui, comme ça c'est bon :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    SELECT LaDate, CAST('2023-07-31T17:50:42' AS datetime) AS REFDATE FROM Tension
    WHERE LaDate > CAST('2023-07-31T17:50:42' AS datetime)
    ORDER BY LaDate
    La requête SQL c'est une chaîne de caractères, donc on est bien obligé de convertir.
    Enfin du moins, pour tester sous SSMS.

  8. #8
    Membre émérite
    Profil pro
    Développeur Web
    Inscrit en
    Février 2008
    Messages
    2 393
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Février 2008
    Messages : 2 393
    Points : 2 711
    Points
    2 711
    Par défaut
    Ça y est, ça se met à jour, merci.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    cmd.CommandText = "UPDATE Tension SET LaDate=@laDate, "
        + "commentaire = @commentaire, "
        + "SysVal = @SysVal, "
        + "DiaVal = @DiaVal, "
        + "PulVal = @PulVal, "
        + "OxyVal = @OxyVal "
        + "WHERE LaDate < DATEADD(second, 1, @oldDate) "
        + "AND LaDate > DATEADD(second, -1, @oldDate);";

  9. #9
    Membre émérite Avatar de Thumb down
    Homme Profil pro
    Retraité
    Inscrit en
    Juin 2019
    Messages
    1 485
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Juin 2019
    Messages : 1 485
    Points : 2 266
    Points
    2 266
    Par défaut
    Bonjour,
    Pour info VS accepte le multi lignes dans un string, pas besoin de concaténation de chaque lignes.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    cmd.CommandText = "UPDATE Tension SET LaDate=@laDate,
        commentaire = @commentaire,"

  10. #10
    Membre émérite
    Profil pro
    Développeur Web
    Inscrit en
    Février 2008
    Messages
    2 393
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Février 2008
    Messages : 2 393
    Points : 2 711
    Points
    2 711
    Par défaut
    Citation Envoyé par Thumb down Voir le message
    Bonjour,
    Pour info VS accepte le multi lignes dans un string, pas besoin de concaténation de chaque lignes.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    cmd.CommandText = "UPDATE Tension SET LaDate=@laDate,
        commentaire = @commentaire,"
    Tiens, ça ne me rappelle rien ...
    Ou bien c'est pour le plaisir de baisser le pouce ?

    ***
    Ah dans le code oui pardon, j'étais parti sur le TextBox.
    Oui, c'est vrai que si on veut baisser le pouce, on peut trouver un os là-dessus

    On peut même avoir un texte formaté, mais dans ce cas il faut faire attention de démarrer au début de la ligne, en ignorant les indentations.

    Ça améliore la lisibilité de cette chaîne de caractères, pas toujours du code dans son ensemble si on le fait trop souvent dans le même module.

    Pardon pour le hors sujet auprès des autres, j'étais dans le lune ...

  11. #11
    Membre chevronné
    Homme Profil pro
    edi
    Inscrit en
    Juin 2007
    Messages
    905
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : edi

    Informations forums :
    Inscription : Juin 2007
    Messages : 905
    Points : 1 923
    Points
    1 923
    Par défaut
    Citation Envoyé par Thumb down Voir le message
    Bonjour,
    Pour info VS accepte le multi lignes dans un string, pas besoin de concaténation de chaque lignes.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    cmd.CommandText = "UPDATE Tension SET LaDate=@laDate,
        commentaire = @commentaire,"
    Il faut utiliser les chaînes verbatim
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    string str = @"Les chaînes verbatim 
    peuvent se répartir sur 
    plusieurs lignes.";
    ou les chaînes brutes (raw strings) à partir de C# 11 (dotnet 8)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    string str = """Ceci 
    est une chaîne brute""";

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [FMOD] FMODex ne lit rien.
    Par Atikae dans le forum FMOD
    Réponses: 7
    Dernier message: 19/05/2008, 16h53
  2. Ne rien afficher
    Par rockbiker dans le forum DirectX
    Réponses: 3
    Dernier message: 20/05/2003, 18h02
  3. [debutante] [JDBComboBox]rien à l'affichage
    Par Lina dans le forum JBuilder
    Réponses: 3
    Dernier message: 22/11/2002, 13h31

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo