Les jointures SQL sont-elles cohérentes dans un jeu de résultats?

c# queue sql sqlbulkcopy sql-server

Question

Je me sens un peu comme un idiot, car je ne suis pas sûr de la terminologie à utiliser ici. Alors laissez-moi essayer de peindre la meilleure image possible. Si je joins deux tables avec une jointure

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

t1.prop n'est pas unique et supposons qu'il y en ait 2, t2.prop est unique, existe-t-il une possibilité lointaine que le calcul informatisé t2.prop soit renseigné dans l'un, mais pas dans l'autre? Dans ma tête, je ne peux pas l'imaginer, je suppose que tous les résultats de t2.prop puis appliqués aux résultats.

Donc, si la réponse à cette question est non, alors peut-être que quelqu'un peut signaler une chose qui me manque, j'essaie de réparer une table de file d'attente et j'ai de la chance que je voie une sorte de situation de concurrence. Je l'ai réduite à l'énoncé ci-dessus, ce que je ne crois pas, ou la requête ignore des éléments en fonction d'indications de verrouillage, ou peut-être des lectures non conformes (le niveau d'isolement est lu, validées), ou je suis confus.

Voici le flux de travail de base.

  1. À tout moment, un bloc d'éléments est poussé dans la file d'attente à l'aide d'une copie en bloc (SQLBulkCopy .net). Il fait partie d'une transaction et est validé avec un horodatage (un seul thread remplit également cette file d'attente. 1 le fil pourrait le faire).

  2. Un seul consommateur scanne activement la file d'attente avec une requête qui ressemble essentiellement à ceci

    SELECT Q.* from Queue Q with(rowlock, updlock, nowait) 
    join table t on Q.Prop = t.Prop
    order by Q.Timestamp;
    

Je pars du principe que cela me rendrait les plus anciens éléments validés de la file d'attente, classés par horodatage, où (à un moment donné, j'avais lu de nouveau ici, mais j'avais peur que des choses me soient retournées, mais aussi au hasard. essayé tablock et verrouillé la table sur l'insert, mais cela ne faisait aucune différence).

Donc, mon problème est que je continue à avoir des articles traités en panne pour un t.Prop individuel.

J'ai ajouté des déclencheurs affichant des éléments. L'horodatage est antérieur à celui des autres éléments, mais ils sont lus dans la file d'attente dans le mauvais ordre. Des astuces ou de l'aide?

Mettre à jour

J'ai prouvé que je pouvais obtenir un résultat partiel où je m'attendais à tout ou rien

    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);
            }
        });
    }

J'ai réussi à obtenir que ce soit tout ou rien en utilisant inner hash join , mais sur une autre note, je pourrais essayer d'utiliser le niveau d'isolation de capture instantanée.

Réponse populaire

Soit vous utilisez des lectures de données modifiées ( read uncommitted ) dans le lecteur, soit la copie en bloc n’utilise pas de transactions. Vérifier les deux est assez facile:

SELECT Q.* from Queue Q with(rowlock, updlock, nowait, readcommitted) 
  left join table t on Q.Prop = t.Prop
order by Q.Timestamp;

(Bien que personnellement, je ne me fie pas à l'indication nowait et nowait un where explicite).

Ce dernier peut facilement être tracé par SQL Profiler. Il n'est pas nécessaire d'inclure tous les lots dans la trace - il suffit d'ajouter des événements liés à la transaction.

PS Bien que le meilleur moyen de mettre en place une file d’attente est de l’utiliser. La file d'attente Service Broker, par exemple.



Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi