C#中使用SQLDataReader执行SQL Server查询的问题

3

我正在尝试使用 SqlDataReader 在 C# 中执行查询,但是从数据库中接收到错误消息,"语法不正确,附近有 '.'"。

我不确定我的 SQL 查询有什么问题。在 SQL Server Management Studio 中,我可以完美地执行它。

try
{
   SqlConnection thisConnection = new SqlConnection();
   thisConnection.Open();
   SqlCommand thisCommand = thisConnection.CreateCommand();
   thisCommand.CommandText = "SELECT"
                +"db.name DBName,"
                +"tl.request_session_id,"
                +"wt.blocking_session_id,"
                +"OBJECT_NAME(p.OBJECT_ID) BlockedObjectName,"
                +"tl.resource_type,"
                +"h1.TEXT AS RequestingText,"
                +"h2.TEXT AS BlockingText,"
                +"tl.request_mode"
                +"FROM sys.dm_tran_locks AS tl"
                +"INNER JOIN sys.databases db ON db.database_id = tl.resource_database_id"
                +"INNER JOIN sys.dm_os_waiting_tasks AS wt ON tl.lock_owner_address = wt.resource_address"
                +"INNER JOIN sys.partitions AS p ON p.hobt_id = tl.resource_associated_entity_id"
                +"INNER JOIN sys.dm_exec_connections ec1 ON ec1.session_id = tl.request_session_id"
                +"INNER JOIN sys.dm_exec_connections ec2 ON ec2.session_id = wt.blocking_session_id"
                +"CROSS APPLY sys.dm_exec_sql_text(ec1.most_recent_sql_handle) AS h1"
                +"CROSS APPLY sys.dm_exec_sql_text(ec2.most_recent_sql_handle) AS h2";

   SqlDataReader thisReader = thisCommand.ExecuteReader();
   while (thisReader.Read())
   {
      Console.WriteLine("\t{0}\t{1}", thisReader["DBName"], thisReader["BlockedObjectName"]);
   }
   thisReader.Close();
   thisConnection.Close();
}
catch (SqlException e)
{
   Console.WriteLine(e.Message);
}

1
建议:将您的IDisposable SqlConnectionSqlCommandSqlDataReader放入using(....) { ... }块中以确保正确处理;尽可能晚地打开连接 - 就在ExecuteReader之前 - 而不是更早。 - marc_s
我已经修复了我的SQL查询中的语法错误。现在,在执行thisReader.Read()之后,我遇到了“枚举未产生结果”的错误。我不确定为什么会这样。 - Mad Gibberish
3个回答

16
你需要在一些要拼接的行末添加空格:
例如:
            +"tl.request_mode"
            +"FROM sys.dm_tran_locks AS tl"

将创建SQL。

 ...tl.request_modeFROM sys.dm_tran_locks AS tl...

将其更改为

            +"tl.request_mode " 
            +"FROM sys.dm_tran_locks AS tl"

还有几行代码也存在相同的问题。

这是一个很好的例子,说明在测试动态构建的SQL语句时,打印出连接的字符串实际非常重要,而不仅仅是粘贴代码并删除引号和加号。


2
如果您要使用连接字符串,请使用StringBuilder。 - Brian
3
在这种情况下,可能没有什么区别。如果所有的字符串都像这样是文字,编译器将在 MSIL 中将它们组合成一个单独的字符串。然而,我仍然认为,在字符串前面加上 @ 符号并且取消所有 +"" 的操作会更易读。 - JohnFx
1
我正要说同样的话,@方法唯一的缺点是如果在CIL中留下制表符,看起来像"SELECT\r\n\t\t\t\t\tdb.Name DBName,\r\n\t\t\t\t\t"等等。当你使用Reflector查看生产代码时(当你不确定部署了哪个版本时...)会很痛苦。 - Joshua

2

正如John所提到的,由于您缺少一些行末空格,因此需要在行末添加空格。

例如,您最初的错误来自前两行。

thisCommand.CommandText = "SELECT" 
                +"db.name DBName," 

将会等于

SELECTdb.name DBName,

那就是因为 SELECT 语句上加了 "db.",所以才会出现 "Incorrect syntax near'.'" 的错误提示。

1

JohnFx和kevchadders是正确的,错误是由于结果字符串没有适当的空格造成的。至于你可以怎么做 - 如果你希望查询保持更或多或少相同的格式,你可以使用@引用:

thisCommand.CommandText = 
@"SELECT
    db.name DBName,
    tl.request_session_id,
    wt.blocking_session_id,
    OBJECT_NAME(p.OBJECT_ID) BlockedObjectName,
    tl.resource_type,
    h1.TEXT AS RequestingText,
    h2.TEXT AS BlockingText,
    tl.request_mode
FROM sys.dm_tran_locks AS tl
INNER JOIN sys.databases db ON db.database_id = tl.resource_database_id
INNER JOIN sys.dm_os_waiting_tasks AS wt ON tl.lock_owner_address = wt.resource_address
INNER JOIN sys.partitions AS p ON p.hobt_id = tl.resource_associated_entity_id
INNER JOIN sys.dm_exec_connections ec1 ON ec1.session_id = tl.request_session_id
INNER JOIN sys.dm_exec_connections ec2 ON ec2.session_id = wt.blocking_session_id
CROSS APPLY sys.dm_exec_sql_text(ec1.most_recent_sql_handle) AS h1
CROSS APPLY sys.dm_exec_sql_text(ec2.most_recent_sql_handle) AS h2
";

SqlDataReader thisReader = thisCommand.ExecuteReader();

这将尊重您的行结束符和其他空格,因此您可以获得一个可读的查询,您可以轻松地从C#复制并粘贴到SSMS中,没有空格错误,没有连接,并且没有所有的StringBuilder.Append语句。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接