2017-02-22 18 views
0

これはC#の私の最初のプロジェクトです。私はAccess VBAで少し経験があります。私はスタンドアロンのプログラムであるために私のアプリを上に移動したいと思います。私はトレーニングの種類と日付を持つテーブルを照会しています。私は、実行された日付に基づいて、いくつかの訓練のタイプを互いに比較したいと思います。 3つのトレーニングタイプは、RWT010、RWP000、およびRWT010BPです。 RWT010BPが存在し、それが新しい場合、それは私が必要とする唯一のものです。それ以外の場合はRWT010とRWP000が必要です。変数に値を読み込む方法を考えましたが、それらを扱うことができる必要があります。私はdateTime値の名前を同じ行のtrainTypeにしたいと思います。そうすれば、私はそれらを比較して適切な組み合わせを出力できます。oracle sqlのstring変数の名前変数。#

私の古いアクセス・ロジックは、このように見えた:

LABEL_DATE: IIf(IsNull([RWT010]),"RWT010BP: " & _ 
    Format([RWT010BP],"Short Date"),IIf([RWT010BP]>[RWT010],"RWT010BP: " & _ 
    Format([RWT010BP],"Short Date"),"RWT010: " & _ 
    Format([RWT010],"Short Date") & " & " & "RWP000: " & _ 
    Format([RWP000],"Short Date"))) 

これはどこまで私はC#で得ているです:元のAccessロジックは3と1 DBの行を持っているように見えます

Console.Write("Enter ID: "); 
     int idnum = Convert.ToInt32(Console.ReadLine()); 
     string sql = "SELECT EXPID, TYPE, DATE_LATEST FROM TRAINING_TABLE where expid =" + idnum; 



     OracleCommand cmd = new OracleCommand(); 

     cmd.Connection = conn; 

     cmd.CommandText = sql; 

     using (DbDataReader reader = cmd.ExecuteReader()) 
     { 
      if (reader.HasRows) 
      { 

       while (reader.Read()) 
       { 

        int expid = reader.GetInt32(0); 
        string trainType = reader.GetString(1); 
        DateTime trainDate = reader.GetDateTime(2); 
+0

は、あなたがあなたのC#の変数名は 'trainDate'は' trainType'の値に応じて変更したいと言っていますか?可能であれば、それはひどい考えですが、そうではありません。それとも私はあなたを誤解していますか? –

+0

私は実際に、異なるトレーニングタイプの日付の比較を行う最も効率的な方法が何であるか尋ねたはずです。私は本当にこれで新しいことを謝ります。 –

+0

私は効率について心配しません。クラリティははるかに重要です。ここで何が起こっているのかについての私の最初の推測では、アクセス、 'RWT010'などで、単一の行のすべての日付フィールドですが、オラクルでは、あなたが複数の行があり、各行は名前フィールドに加えて、単一の日付フィールドを持っているということです。名前フィールドは '' RWT010 "'、 '' RWT010BP ''、または '' RWP000 "'の文字列です。正しいですか? –

答えて

0

日付フィールド、[RWT010][RWT010BP]、および[RWP000]です。しかし、Oracleでは正規化されているため、複数の行が返されるようになりました。それぞれのフィールドには、、"RWT010BP"、または"RWP000"に等しい[TYPE]という名前のフィールドがあります。

あなたは考えていたので、Accessの場合と同様に、RWP000の日付値を名前で処理したいと考えていました。あなたは正しかった、それはそれを行うための最も明白な方法です、そして私はあなたに方法を教えます。私はあなたが求めていたものを誤解しました。

これを行う1つの方法は、Accessロジックを複製するOracleストアドプロシージャを作成することです。それはあなたが尋ねた質問ではありませんが、それを行う正当な方法です。しかし、データベースが変更されてAccessバージョンよりも複雑になります。とにかくOracle SQLを何年も書いていないし、セミコロンについての恣意的な、暗黙の構文エラー空白。

だから私は、DB行からdatetimesを取得してローカル変数に入れて、C#でフィールドの代わりにそれらの変数を使ってAccessロジックを複製するC#でループを書くことです。 Accessバージョンと比較して少し冗長になりますが、時にはそれがどのようになっているのでしょうか。

int idnum = Convert.ToInt32(Console.ReadLine()); 

string sql = "SELECT EXPID, TYPE, DATE_LATEST FROM TRAINING_TABLE where expid =" + idnum; 

// I don't know how you're using this so I'll just declare it here 
// and leave that to you. 
String dateLabel = ""; 

OracleCommand cmd = new OracleCommand(); 

cmd.Connection = conn; 

cmd.CommandText = sql; 

using (DbDataReader reader = cmd.ExecuteReader()) 
{ 
    DateTime? RWT010 = null; 
    DateTime? RWT010BP = null; 
    DateTime? RWP000 = null; 

    // No need to check reader.HasRows. If it has no rows, reader.Read() 
    // will return false the first time, that's all. 

    while (reader.Read()) 
    { 
     // Doesn't look to me like expid is used 
     //int expid = reader.GetInt32(0); 
     string trainType = reader.GetString(1); 
     DateTime trainDate = reader.GetDateTime(2); 

     switch (trainType) { 
      case "RWT010": 
       RWT010 = trainDate; 
       break; 
      case "RWT010BP": 
       RWT010BP = trainDate; 
       break; 
      case "RWP000": 
       RWP000 = trainDate; 
       break; 
     } 
    } 

    if (RWT010 == null || RWT010BP > RWT010) { 
     dateLabel = String.Format("RWT010BP: {0:d}", RWT010BP); 
    } else { 
     dateLabel = String.Format("RWT010: {0:d} & RWP000: {1:d}", RWT010, RWP000); 
    } 
} 

オリジナルのロジックは、このでした:

If RWT010 isn't null, 
     Do A 
    Otherwise, if RWT010BP > RWT010 
     ALSO do A 
    But if none of the above, 
     Do B 

最初の二つの分岐はまったく同じことを行うので、私たちは1本の枝にそれらの両方を凝縮することができます。

「Do not Repeat Yourself」と言われています。今年からこのコードに戻って、2本の線が同じであることが必要かどうか疑問に思ったり、同じであることに気付かずにどちらか一方のみを変更したりするのは不思議です。それはちょうど混乱です。

あなたがfamiliar with String.Format()ではない場合は、それにたくさんあります。最初の引数文字列の{0}は、「2番目の引数をここに挿入する」ことを意味します。 {1}は「3番目を挿入する」などを意味します。中括弧の中の ":d"はオプションです。フォーマット情報として "d"をその値に渡すことを意味します。 DateTimeは "d"を "Short Date"と解釈します。

dateLabel = String.Format("RWT010BP: {0}", RWT010BP.Value.ToShortDateString()); 

またはこのよう:

dateLabel = "RWT010BP: " + RWT010BP.Value.ToShortDateString(); 

私はRWT010BPがそれ後?で宣言されているため、そのラインではなく、単にRWT010BPRWT010BP.Valueを使用する必要があります。また、このようにそれを行うことができます。それはそれを "nullable"値にします。通常のDateTimeはnullにはできませんが、ここではnullを処理する必要があります。

あなたがC#6を使用している場合は、私が好むれ、このようにそれを行うことができます。私は上記のC#のバージョンがわからないので、これを使用しませんでした。コードを乱雑にする「ノイズ」を最小限に抑えてください。

dateLabel = $"RWT010BP: {RWT010BP:d}"; 

上記のString.Format("{0:d}", ...)と同じ ":d"です。

もう1つ:idnumはintですが、文字列値をSQL文字列に連結することはありません。これは大規模なセキュリティ上の脆弱性であり、ここの人々は(正しく、私は恐れている)あなたにそれを考えさえするのは非常に困難な時間を与えるでしょう。代わりに

使用OracleCommand.Parametersas shown in this answer。私はこの場合でも、条件付き反射と同じように、個人的に使用したでしょう。

+0

うわー、それは私のコードの本当に良い説明と解決策です。私は最初にトレーニングの種類と日付をどのように取得したのか混乱し、各タイプを処理するためにサブクエリを作成したことに気付きました。注射の脆弱性についての良い点。誰かがそれを使ってできることはたくさんあります。 –

+0

@OryWeaverありがとうございます。問題が解決した場合は、それを回答としてマークすることができます。 –