我觉得自己有点像白痴,因为我不确定这里使用的术语是什么。所以让我试着画出最好的画面。如果我将两个表连接在一起
select t1.prop, t2.prop from t1 join t2 on t1.prop = t2.prop
t1.prop
并不是唯一的,我们假设其中有2个,t2.prop
是唯一的,中间查询计算t2.prop
是否存在远程可能性填充在一个,但不是另一个?在我脑海里,我无法想象它,我认为它会找到t2.prop
的所有结果,然后将其应用于结果。
所以,如果答案是否定的,那么也许有人可以指出我在这里失踪的可能事情,我试图修复排队表,幸运的是我看到了一些一种竞争条件。我已经将其缩小到上面的声明,我不相信,或者查询是基于锁定提示跳过项目,或者可能是脏读(隔离级别是读取提交),或者我&# 39;迷茫。
这是基本的工作流程。
在任何时候,使用批量复制(SQLBulkCopy .net)将一大块项目推入队列,并且它在一个事务中,并使用时间戳提交(只有一个线程填充此队列)同样,所以在任何时候,1个线程都可能这样做。)
只有一个消费者正在使用看起来与此类似的查询主动扫描队列
SELECT Q.* from Queue Q with(rowlock, updlock, nowait)
join table t on Q.Prop = t.Prop
order by Q.Timestamp;
我假设这会返回队列中按时间戳排序的最早的已提交项目(在某一点上,我曾在这里读过,但我担心这可能会让我失去一些东西。顺序,我也随机尝试了tablock并将表锁定在插件上,但这没有区别。)
所以,我的问题是我不断为个人t.Prop
处理无序处理的项目。
我添加了触发器,显示项目Timestamp在其他项目时间戳之前,但它们以错误的顺序从队列中读取。任何提示或帮助?
我证明我能够得到一个部分结果集,我期待全部或全部
private static void OutOfOrder()
{
var cnt = 100;
var go = true;
using (var db = new DBManager())
{
using (var cmd = db.GetCommand())
{
cmd.CommandText = "Delete from Foo";
cmd.ExecuteNonQuery();
cmd.CommandText = "Delete from Bar";
cmd.ExecuteNonQuery();
cmd.CommandText = "Insert Into Foo (B) Values ('joint')";
for (var i = 0; i < cnt; i++)
{
cmd.ExecuteNonQuery();
}
}
}
var task1 = Task.Run(() =>
{
var inserted = false;
while (go)
{
using (var db = new DBManager())
{
using (var cmd = db.GetCommand())
{
var text = inserted ? "Delete from Bar" : "Insert Into Bar (B, C) Values ('joint', 'blah')";
cmd.CommandText = text;
Console.WriteLine(DateTime.Now.ToLongTimeString() + " - " + text);
cmd.ExecuteNonQuery();
inserted = !inserted;
}
}
Thread.Sleep(20);
}
});
var task2 = Task.Run(() =>
{
var text = "Select * from Foo join Bar on Foo.B = Bar.B";
while (go)
{
using (var db = new DBManager())
{
using (var cmd = db.GetCommand())
{
cmd.CommandText = text;
Console.WriteLine(DateTime.Now.ToLongTimeString() + " - " + text);
var ret = cmd.ExecuteDataTable();
var retCount = ret.Rows.Count;
var valid = retCount == 0 || retCount == 100;
if (!valid)
{
Console.WriteLine("Error, got {0} rows back!!!", ret.Rows.Count);
go = false;
}
}
}
Thread.Sleep(17);
}
});
}
我能够使用inner hash join
将其全部或全部删除,但另一方面,我可能会尝试使用快照隔离级别。
答案 0 :(得分:0)
首先要做的是:如果您想要LEFT JOIN
,则必须明确提及LEFT
关键字。简单地写一个联接意味着INNER JOIN
(因为这是INNER
关键字在可选中的唯一情况。)
写为:
SELECT Q.* from Queue Q with(rowlock, updlock, nowait)
join table t with(rowlock, updlock, nowait)
on Q.Prop = t.Prop
order by Q.Timestamp;
答案 1 :(得分:0)
您在阅读器中使用脏读(read uncommitted
),或者批量复制不使用事务。检查两者很容易:
SELECT Q.* from Queue Q with(rowlock, updlock, nowait, readcommitted)
left join table t on Q.Prop = t.Prop
order by Q.Timestamp;
(虽然我个人不会依赖nowait
提示并添加明确的where
)。
后者可以通过SQL事件探查器轻松跟踪。您不必将所有批量包含在跟踪中 - 只需添加与事务相关的事件。
P.S。虽然实现队列的最佳方法是使用一个。例如,Service Broker队列。