SQL连接是否在结果集中保持一致

时间:2014-12-01 05:28:25

标签: c# sql sql-server queue sqlbulkcopy

我觉得自己有点像白痴,因为我不确定这里使用的术语是什么。所以让我试着画出最好的画面。如果我将两个表连接在一起

select t1.prop, t2.prop from t1 join t2 on t1.prop = t2.prop

t1.prop并不是唯一的,我们假设其中有2个,t2.prop是唯一的,中间查询计算t2.prop是否存在远程可能性填充在一个,但不是另一个?在我脑海里,我无法想象它,我认为它会找到t2.prop的所有结果,然后将其应用于结果。

所以,如果答案是否定的,那么也许有人可以指出我在这里失踪的可能事情,我试图修复排队表,幸运的是我看到了一些一种竞争条件。我已经将其缩小到上面的声明,我不相信,或者查询是基于锁定提示跳过项目,或者可能是脏读(隔离级别是读取提交),或者我&# 39;迷茫。

这是基本的工作流程。

  1. 在任何时候,使用批量复制(SQLBulkCopy .net)将一大块项目推入队列,并且它在一个事务中,并使用时间戳提交(只有一个线程填充此队列)同样,所以在任何时候,1个线程都可能这样做。)

  2. 只有一个消费者正在使用看起来与此类似的查询主动扫描队列

    SELECT Q.* from Queue Q with(rowlock, updlock, nowait) 
    join table t on Q.Prop = t.Prop
    order by Q.Timestamp;
    
  3. 我假设这会返回队列中按时间戳排序的最早的已提交项目(在某一点上,我曾在这里读过,但我担心这可能会让我失去一些东西。顺序,我也随机尝试了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将其全部或全部删除,但另一方面,我可能会尝试使用快照隔离级别。

2 个答案:

答案 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队列。