这是我在c#中的第一个项目。我在Access VBA方面有一点经验。我想将我的应用程序移动到独立程序。我正在查询具有培训类型和日期的表格。我想根据他们执行的日期比较一些类型的培训。三种培训类型是RWT010,RWP000和RWT010BP。如果RWT010BP存在并且更新,那么它是我唯一需要的。否则我需要RWT010和RWP000。我已经想出如何将值加载到变量中,但我需要能够使用它们。我想将dateTime值的名称作为同一行的trainType。这样我可以比较它们并输出正确的组合。
我的旧Access逻辑看起来像这样:
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 :(得分:0)
原始的Access逻辑看起来像一个数据库行,其中包含三个日期字段[RWT010]
,[RWT010BP]
和[RWP000]
。但是在Oracle中已经规范化,因此您需要返回多行,每行都有一个名为[DATE_LATEST]
的日期时间字段,然后是名为[TYPE]
的名称字段。等于"RWT010"
,"RWT010BP"
或"RWP000"
。
您正在考虑,您希望按名称处理这些RWP000
日期值,就像在Access中一样。你是对的,这是最明智的做法,我会告诉你如何做。我误解了你的要求。
执行此操作的一种方法是编写复制Access逻辑的Oracle存储过程。这不是你提出的问题,但这是一种合法的方式。但是,由于数据库的变化,它会比Access版本更复杂,无论如何我多年没有编写Oracle SQL而且我没有Oracle服务器给我带来任意的,神秘的有关分号和空格的语法错误。
所以我要做的就是在C#中编写一个循环来从DB行中获取日期时间并将它们放在局部变量中,然后使用这些变量而不是字段在C#中复制Access逻辑。与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}
表示"在此插入第二个参数&#34 ;; {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}";
这是相同的":d"如上面的String.Format("{0:d}", ...)
。
还有一件事: idnum
是一个int,但不能将字符串值连接成SQL字符串。这是一个巨大的安全漏洞,这里的人们(正确的,我害怕)会给你一个非常艰难的时间来考虑它。
使用OracleCommand.Parameters
代替as shown in this answer。即使在这种情况下,我也会习惯使用它,就像条件反射一样。