SQL 当 运行 来自 C# 时查询超时,在 SQL Server Management Studio 中快速

SQL query times out when run from C#, fast in SQL Server Management Studio

我有一个 C# 程序使用下面列出的代码执行 SQL 查询。直到前几天,我一直在使用这段代码一段时间,没有任何问题。

我正在将一个查询字符串传递给 SQL,其中包含一个字符串列表,这些字符串是股票标识符。几天前我运行它和查询超时,如果我让它会运行一个多小时。过去几天我一直在尝试调试它。在我最初的查询中,大约有 900 个标识符。

我已经尝试改变我能想到的一切,但我得到了无法解释的结果。

例如:

  1. 查询适用于一个股票列表,但不适用于另一个在字符串数量和总长度方面具有相同长度的列表

  2. 它适用于一个列表,但不适用于相反顺序的同一个列表

  3. 对于一个列表,如果恰好有 900 个标识符,它就可以工作,但如果有 899 或 901 个,它就不会工作,我可以包含或排除不同的标识符并得到相同的结果,所以它不是一些带有标识符之一的时髦东西。

在每一种情况下,我都捕获了我的程序传递的查询字符串并复制到 SQL Server Management Studio,并且在每种情况下,查询 运行s in 1第二.

我已经在这个论坛和其他论坛上阅读了所有关于在 SQL Server Management Studio 中工作但在程序 运行 时超时的查询的所有内容,但这似乎有所不同,因为我可以找出它失败的案例和它不起作用的类似案例。

关于我可能会在哪里查看可能发生的事情的建议,我将不胜感激。

using (SqlConnection conn = new SqlConnection(_connectString))
{
    conn.Open();

    using (SqlCommand cmd = new SqlCommand(queryString, conn))
    {
        cmd.Parameters.Clear();
        cmd.CommandTimeout = _timeout;

        SqlParameter param;

        if (parms != null)
        {
            foreach (string parm in parms.Keys)
            {
                param = cmd.Parameters.AddWithValue(parm, parms[parm]);
            }
        }

        SqlDataReader reader = cmd.ExecuteReader();

        while (reader.Read())
        {
            QueryResult record = new QueryResult();
            record.Fields = new List<object>();

            for (int i = 0; i < returnColumns; ++i)
            {
                object value = reader.GetValue(i);

                if (value == DBNull.Value)
                    record.Fields.Add(null);
                else
                    record.Fields.Add(value);
            }

            result.Add(record);
        }

        reader.Close();
    }

    conn.Close();
}

这是我的查询。在此版本中,我包含 65 只股票,但它不起作用(<=64 有效)。

select
    distinct a.Cusip
,   d.Value_ / f.CumAdjFactor as split_adj_val

from qai.prc.PrcScChg a

join qai.dbo.SecMapX b
    on a.Code = b.venCode
    and b.VenType = 1
    and b.exchange = 1
    and b.Rank = (select Min(Rank) from qai.dbo.SecMapX where VenCode = a.Code and VenType = 1 and Exchange = 1)

join qai.dbo.SecMapX b2
    on b2.seccode = b.seccode
    and b2.ventype = 40
    and b2.exchange = 1
    and b2.Rank = (select Min(Rank) from qai.dbo.SecMapX where SecCode = b.SecCode and VenType = 40 and Exchange = 1)

join qai.dbo.SecMapX b3
    on b3.seccode = b.seccode
    and b3.ventype = 33
    and b3.exchange = 1
    and b3.Rank = (select Min(Rank) from qai.dbo.SecMapX where SecCode = b.SecCode and VenType = 33 and Exchange = 1)

join qai.dbo.DXLSecInfo c
    on b2.VenCode = c.Code

join qai.dbo.DXLAmData d
    on c.Code = d.Code
    and d.Date_ = @Date
    and d.Item = 6

left JOIN qai.dbo.DS2Adj f 
    ON f.InfoCode = b3.VenCode
    AND f.AdjType = 2
    and f.AdjDate <= @Date
    and ( f.EndAdjDate >= @Date or f.EndAdjDate is null )

where 
    a.cusip in ('00101J10', '00105510', '00120410', '00130H10', '00206R10',
    '00282410', '00287Y10', '00289620', '00724F10', '00817Y10', '00846U10',
    '00915810', '00936310', '00971T10', '01381710', '01535110', '01741R10',
    '01849010', '02000210', '02144110', '02209S10', '02313510', '02360810',
    '02553710', '02581610', '02687478', '03027X10', '03073E10', '03076C10',
    '03110010', '03116210', '03209510', '03251110', '03265410', '03741110',
    '03748R10', '03783310', '03822210', '03948310', '04621X10', '05276910',
    '05301510', '05329W10', '05333210', '05348410', '05361110', '05430310',
    '05493710', '05722410', '05849810', '06050510', '06405810', '06738310',
    '07181310', '07373010', '07588710', '07589610', '08143710', '08467070',
    '08651610', '09062X10', '09247X10', '09367110', '09702310', '09972410')

您尚未发布您的查询,但仅根据它是如何使用动态参数列表和参数的绝对数量构建的,我将猜测并说它与参数嗅探 - 参见:

http://www.brentozar.com/archive/2013/06/the-elephant-and-the-mouse-or-parameter-sniffing-in-sql-server/

http://blogs.msdn.com/b/sqlprogrammability/archive/2008/11/26/optimize-for-unknown-a-little-known-sql-server-2008-feature.aspx

该问题的基本思想是为一组特定参数创建最佳查询执行计划,这对于另一组参数非常次优。

有几种方法可以解决参数嗅探问题(值得庆幸的是,其中许多方法在 sql 服务器 2008 中开放)。

你可以:

  1. 重构您的查询
  2. WITH RECOMPILE 添加到您的存储过程/将 option (recompile) 添加到您的查询
  3. optimize for unknown/option (optimize for... 到你的 proc/query
  4. 其他?

三件事要看,按优先顺序排列:

  1. Avoid using the AddWithValue() function,因为当 ADO.Net 猜测列类型错误时,这可能会产生灾难性的性能影响。做你必须做的事情才能为每个参数设置一个显式的数据库类型
  2. 调查OPTION RECOMPILE
  3. 调查OPTIMIZE FOR UNKNOWN。只有在其他人都失败后才这样做。