到目前为止,我有两个简单的测试,对于一个有点奇怪的SQL查询。我不明白有两件事情正在发生。我的第一个测试是我没有插入任何数据并运行我的查询,我希望if (reader.HasRows)
部分返回false(它确实)并且正确而不是返回0.第二个测试我插入2记录一个是"付费"而另一个不是(它会在一秒钟内变得更有意义)。我希望只看到付费行的总和,但我什么都没得到。问题是它是一个时间敏感的查询,可以更改时区。我不确定为什么会这样,但我不能改变这个事实。由于它与工作有关,我不得不改变一些事情。 Ebay模拟器听起来就是一个很好的例子。
在mysql中创建一个简单的表
CREATE TABLE `order` (
`id` int(11) unsigned NOT NULL,
`Total` decimal(8,2) DEFAULT NULL,
`OrderDate` datetime(6) DEFAULT NULL,
`Paid` tinyint(3) unsigned NOT NULL,
`Deleted` tinyint(3) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `id_UNIQUE` (`id`),
);
现在使用NUnit进行2次简单测试
[TestFixture]
public class DbAccountingStatisticsTests
{
private readonly DateTime TodayStartOrDay = DateTime.Today;
private readonly DateTime TodayEndOfDay = DateTime.Today.AddDays(1).AddTicks(-1);
private LoggingAssert _logging;
private DeferredMultiTableDatabaseActions _dbActions;
private static readonly TimeZoneInfo Pst = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");
private static readonly TimeZoneInfo Est = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
[SetUp]
public void Setup()
{
_logging = LoggingAssert.Setup();
_dbActions = new DeferredMultiTableDatabaseActions();
}
[TearDown]
public void TearDown()
{
_dbActions.SubmitChanges();
_logging.FailIfAnyLoggedErrors();
}
[Test]
public void WithZeroShippedOrders()
{
var db = new DbAccountingStatistics();
var stats = db.GetOrderStats(
TimeZoneInfo.ConvertTime(TodayStartOrDay, Pst),
TimeZoneInfo.ConvertTime(TodayEndOfDay, Pst)
);
Assert.That(stats.NumberofOrders, Is.Zero);
Assert.That(stats.TotalOrders, Is.Zero);
}
[Test]
public void NonPaidOrdersNotIncluded()
{
AddOrder(orderTotal: 100, paid: false);
AddOrder(orderTotal: 100, paid: true);
var db = new DbAccountingStatistics();
var stats = db.GetOrderStats(
TimeZoneInfo.ConvertTime(TodayStartOrDay, Est),
TimeZoneInfo.ConvertTime(TodayEndOfDay, Est)
);
Assert.That(stats.NumberofOrders, Is.EqualTo(1));
Assert.That(stats.TotalOrders, Is.EqualTo(100));
}
private void AddOrder(decimal orderTotal, bool paid)
{
var db = new DbOrder();
var id = db.Insert(new PocoOrder
{
Paid = paid,
Total = orderTotal,
OrderDate = DateTime.Today//TimeZoneInfo.ConvertTime(DateTime.Today, Est)
});
_logging.FailIfAnyLoggedErrors();
_dbActions.AddSimpleChange($"delete from order where id = {id}");
}
}
现在正在测试的类和给我带来麻烦的sql
public class DbAccountingStatistics
{
private readonly DeferredMultiTableDatabaseActions _dataAdapter = new DeferredMultiTableDatabaseActions();
private const string SqlGetOrderStats =
"SELECT count(id) as orderNum, sum(total) as Total " +
"FROM order " +
"WHERE Date(CONVERT_TZ(OrderDate,'US/Eastern','US/Pacific')) >= @STARTDATE " +
"AND Date(CONVERT_TZ(OrderDate,'US/Eastern','US/Pacific')) <= @ENDDATE " +
"AND Deleted = 0 and paid = 1";
public PocoAccountingStatistics GetOrderStats(DateTime startDate, DateTime endDate)
{
var stats = new PocoAccountingStatistics();
_dataAdapter.AddDeferredAction(new DeferredAction<PocoAccountingStatistics>
{
Sql = SqlGetOrderStats,
SqlParameters = new Dictionary<string, object>
{
{"@STARTDATE", startDate},
{"@ENDDATE", endDate},
},
ParseAction = FillOrderStats
});
_dataAdapter.Fill(stats);
return stats;
}
private void FillOrderStats(PocoAccountingStatistics stats, IDataRecord record)
{
var orderCountOrdinal = record.GetOrdinal("orderNum");
var totalOrdinal = record.GetOrdinal("Total");
stats.NumberofOrders = (long)record[orderCountOrdinal];
stats.TotalOrders = record.IsDBNull(totalOrdinal)? 0 : record.GetDecimal(totalOrdinal);
}
}
public class PocoAccountingStatistics
{
public long NumberofOrders { get; set; }
public decimal TotalOrders { get; set; }
}
public class DeferredAction
{
public string Sql { get; set; }
public Dictionary<string, object> SqlParameters { get; set; }
}
public class DeferredAction<T> : DeferredAction
{
public Action<T, IDataRecord> ParseAction { get; set; }
}
public class DeferredMultiTableDatabaseActions
{
private static readonly Logger logger = LogManager.GetLogger(nameof(DeferredMultiTableDatabaseActions));
private readonly List<DeferredAction> _deferredActions = new List<DeferredAction>();
public void AddSimpleChange(string sql)
{
_deferredActions.Add(new DeferredAction
{
Sql = sql,
});
}
public void AddDeferredAction(DeferredAction deferredAction)
{
_deferredActions.Add(deferredAction);
}
public void SubmitChanges()
{
using (var connection = new BfConnection().DbConnection)
{
var db = new Database(connection);
connection.Open();
foreach (var clown in _deferredActions)
{
using (var command = db.GetCommand(clown.Sql))
{
var rowsAffected = db.ExecuteNonQuery(command);
if(rowsAffected < 1)
logger.Error($"Expected atleast 1 rows to be affected for statment '{clown.Sql}'");
}
}
}
}
public void Fill<T>(T poco)
{
using (var connection = new BfConnection().DbConnection)
{
var db = new Database(connection);
connection.Open();
foreach (var deferredAction in _deferredActions)
{
using (var command = db.GetCommand(deferredAction.Sql))
{
foreach (var parameter in deferredAction.SqlParameters)
AddParameter(command, parameter.Key, parameter.Value);
using (var reader = db.ExecuteReader(command))
{
if (!reader.HasRows)
continue;
if (reader.Read())
((DeferredAction<T>) deferredAction).ParseAction(poco, reader);
}
}
}
}
}
private static void AddParameter(DbCommand command, string name, object value)
{
var parameter = command.CreateParameter();
parameter.ParameterName = name;
parameter.Value = value;
command.Parameters.Add(parameter);
}
}
我第一次运行此测试时得到DbNull
因为我的时区没有在本地设置所以我去了mysql的网站并得到了这个设置。然后我研究了Convert_TZ
的全部内容,并发现它正在将OrderDate
从EST转换为PST并与我的两个参数进行比较。在测试中,我已经将日期插入PST,EST和我当前的时间(CST)。我也改变了测试来转换时间而不是转换时间。每次我回到0总计和计数。我不明白为什么当我插入两个记录时HasRows
为真,如果where子句是错误的,那么与我插入任何内容时HasRows
都返回false。我已经和它斗争了几个小时甚至在睡觉后我仍然无法弄清楚出了什么问题。