2017-02-22 10 views
0

Это мой первый проект в C#. У меня есть небольшой опыт в Access VBA. Я хотел бы переместить мои приложения в автономные программы. Я запрашиваю таблицу, в которой есть типы и даты обучения. Я хотел бы сравнить некоторые виды обучения друг с другом в зависимости от дат, которые они выполняли. Три типа обучения: RWT010, RWP000 и RWT010BP. Если RWT010BP существует и является новым, это единственный, который мне нужен. В противном случае мне нужны RWT010 и RWP000. Я понял, как загрузить значения в переменные, но мне нужно иметь возможность работать с ними. Я хотел бы, чтобы имя значения dateTime было типом trainType для той же строки. Таким образом, я могу сравнить их и вывести правильную комбинацию.Переменная имени из строки oracle sql string в C#

Моя старая логика доступа выглядит следующим образом:

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#:

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

Я бы не стал беспокоиться об эффективности. Ясность гораздо важнее. Мое первое предположение о том, что происходит здесь, заключается в том, что в Access, RWT010 и т. Д. Все поля даты в одной строке, но в Oracle у вас несколько строк, и каждая строка имеет поле имени плюс одно поле даты. И поле имени - это строка, которая может быть «RWT010», «RWT010BP» или «RWP000», «Правильно ли это? –

ответ

0

Это выглядит как оригинальный логики доступа имеет один DB ряд с тремя поля даты, [RWT010], [RWT010BP] и [RWP000]. Но в Oracle это нормализовано, поэтому вы возвращаете несколько строк, каждая из которых имеет поле datetime с именем [DATE_LATEST], а затем поле с именем [TYPE], которое равно "RWT010", "RWT010BP" или "RWP000".

И вы думали, что хотите использовать эти значения даты по имени, как в Access. Вы были правы, это самый ясный способ сделать это, и я покажу вам, как это сделать. Я неправильно понял то, что вы просили.

Один из способов сделать это - написать хранимую процедуру Oracle, которая дублирует логику доступа. Это не вопрос, который вы задали, но это законный способ сделать это. Тем не менее, это было бы сложнее, чем версия Access из-за изменения в базе данных, и в любом случае я не писал Oracle SQL в течение многих лет, и у меня нет сервера Oracle, который может дать мне произвольные критические синтаксические ошибки о точках с запятой и пробелы.

То, что я собираюсь сделать, это написать цикл в C#, чтобы захватить данные из строк DB и поместить их в локальные переменные, а затем дублировать логику доступа на 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 

Первые две ветви сделать то же самое, так что мы можем сконденсировать их обоих в одну ветвь.

«Не повторяйте себя», как говорится. Вы не хотите возвращаться к этому коду через год, задайтесь вопросом, должны ли эти две строки быть одинаковыми, а затем угадать неправильно или не заметить, что они одинаковы и только меняют одно или другое. Это просто беспорядок.

Если вы не familiar with String.Format(), там много чего. В строке первого аргумента {0} означает «вставить здесь второй аргумент»; {1} означает «вставить третий» и т. Д. «: D» внутри фигурных скобок необязателен; это означает передать «d» в качестве информации о формате в значение его вставки. DateTime будет интерпретировать, что «d» означает «короткая дата».Вы также можете сделать это следующим образом:

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

Или так:

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

Я должен использовать RWT010BP.Value в этой строке вместо просто RWT010BP потому RWT010BP объявлен с ? после него. Это делает его «нулевым» значением. Обычный DateTime не может быть нулевым, но здесь нам нужно разместить нули.

Если вы используете C# 6, вы можете сделать это так, что я предпочитаю. Я не использовал его выше, потому что я не знаю, какую версию C# вы используете. Всегда предпочитайте наименьшее количество «шума», загромождающего код.

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

Это тот же ": д", как в String.Format("{0:d}", ...) выше.

Еще одна вещь:idnum - это int, но никогда не объединяет строковое значение в строку SQL. Это серьезная уязвимость в плане безопасности, и люди здесь (по правде говоря, я боюсь) дают вам очень тяжелое время даже для размышлений.

Использовать OracleCommand.Parameters вместо, as shown in this answer. Я бы использовал это, даже в этом случае, лично, как условный рефлекс.

+0

Вау, это действительно хорошее объяснение и решение для моего кода. Я запутался в том, как я получил типы обучения и даты в первую очередь, а затем понял, что написал подзапросы для обработки каждого типа. Хороший вопрос об уязвимости инъекции. С этим можно многое поделать. –

+0

@OryWeaver Спасибо. Если он разрешит проблему, вы можете пометить ее как ответ. –

Смежные вопросы