多线程SQL选择语句

时间:2017-09-14 00:05:09

标签: c# sql-server multithreading task

我是多线程新手和SQL新手,所以请原谅任何新手的错误。

我试图异步执行许多SQL查询。查询是同一数据库中同一个表中的所有select语句。我可以同步运行它们并且一切正常,但是测试一小部分会让我相信同步运行所有查询大约需要150个小时,这太长了。因此,我试图弄清楚如何并行运行它们。

我试图在run a method multiple times simultaneously in c#的答案之后对代码进行建模,但我的代码没有正确执行(这是错误的,虽然我不知道具体如何。代码只是说错误发生了。)

这就是我所拥有的(我实际做的更小更简单的版本):

class Program
{
    static void Main(string[] args)
    {
        List<string> EmployeeIDs = File.ReadAllLines(/* Filepath */);
        List<Tuple<string, string>> NamesByID = new List<Tuple<string, string>>();

        //What I do not want to do (because it takes too long) ...
        using (SqlConnection conn = new SqlConnection(/* connection string */))
        {
            foreach (string id in EmployeeIDs)
            {
                using (SqlCommand cmd = new SqlCommand("SELECT FirstName FROM Employees WITH (NOLOCK) WHERE EmployeeID = " + id, conn))
                {
                    try
                    {
                        conn.Open();
                        NamesByID.Add(new Tuple<string, string> (id, cmd.ExecuteScalar().ToString()));
                    }
                    finally
                    {
                        conn.Close();
                    }
                }
            }
        }


        //What I do want to do (but it errors) ...
        var tasks = EmployeeIDs.Select(id => Task<Tuple<string, string>>.Factory.StartNew(() => RunQuery(id))).ToArray();
        Task.WaitAll(tasks);
        NamesByID = tasks.Select(task => task.Result).ToList();
    }

    private static Tuple<string, string> RunQuery(string id)
    {
        using (SqlConnection conn = new SqlConnection(/* connection string */))
        {
            using (SqlCommand cmd = new SqlCommand("SELECT FirstName FROM Employees WITH (NOLOCK) WHERE EmployeeID = " + id, conn))
            {
                try
                {
                    conn.Open();
                    return new Tuple<string, string> (id, cmd.ExecuteScalar().ToString());
                }
                finally
                {
                    conn.Close();
                }
            }
        }
    }
}

注意:我并不关心这是多线程的(task,parallel.foreach,backgroundworker等)。这将用于运行约30,000个精选查询一次,所以我只需要它快速运行(我希望~8小时=一个工作日,但我会采取我能得到的)一次。它不一定非常漂亮。

提前谢谢!

3 个答案:

答案 0 :(得分:3)

这是完全错误的。您应该构建一个查询以选择所需的所有FirstNames。如果你需要将一堆id传递给服务器,这没问题,只需使用table valued parameter(又名TVP),昏迷分隔的值列表实际上不能很好地扩展。如果正确写入查询并将表编入索引,则应该非常快。 100k行表是一张小表。

然后查询可能如下所示

SELECT DollarAmount, comp.CompanyID
FROM Transactions 
JOIN (SELECT MIN(TransactionID) as minTransactionID, CompanyID 
      FROM CompanyTransactions       
      GROUP BY CompanyID
     ) AS comp 
ON Transactions.TransactionID = comp.minTransactionID
JOIN @IDList ON id = comp.CompanyID

如果TVP中的ID不是唯一的,则可以使用IN代替JOIN

顺便说一下。你知道NOLOCK的意思吗?如果您是数据库的唯一用户并使用单线程或不修改任何数据,那么您是安全的。除此之外,这意味着您可以轻松获得:

  • 结果中可能缺少某些记录
  • 结果中有重复记录
  • 结果中有行,从未提交过,从未被接受为有效数据
  • 如果您使用varchar(max),则可能会获得从未存储过的文本

答案 1 :(得分:1)

您想要执行一个查询以获取所有ID /名称组合,然后将它们放入字典中(以便快速访问)。这将消除运行30,000个查询的非常缓慢的过程,并降低代码的复杂性。

如果您发布了实际的SQL查询(如果需要可以更改列和表名),我可以为您提供更具体的内容,但这应该是关闭的:

;WITH CompTransCTE AS (
    SELECT CompanyID, MIN(TransactionID) AS TransactionID
    FROM CompanyTransactions
    WHERE CompanyID IN (/*Comma seperated list of values*/)
    GROUP BY CompanyID
)
SELECT CT.CompanyID, T.DollarAmount, T.TransactionID
FROM Transactions AS T
INNER JOIN CompTransCTE AS CT ON CT.TransactionID = T.TransactionID;

答案 2 :(得分:0)

如果不在数据库中创建用户定义的表类型,您可以使用SqlBulkCopy将ID加载到临时表中,并在查询中引用它。

std::__cxx11::basic_string