gpt4 book ai didi

c# - 如何对任意查询执行计数(可能包含排序依据)

转载 作者:行者123 更新时间:2023-11-30 22:34:19 25 4
gpt4 key购买 nike

我的任务是更新我们内部使用的内部框架。该框架所做的一件事是您向它传递一个查询,它会返回查询中包含的行数(该框架大量使用 DataReader,因此我们需要事前的总数来处理 UI 事物)。

需要进行计数的查询可能因项目而异(SOL 注入(inject)不是问题,查询不是来自用户输入,只是在其他程序员使用框架时硬编码)他们的项目。)有人告诉我,让程序员为计数编写第二个查询是 Not Acceptable 。

目前的解决方案是执行以下操作(我没有写这个,我只是被告知要修复它)。

//executes query and returns record count
public static int RecordCount(string SqlQuery, string ConnectionString, bool SuppressError = false)
{

//SplitLeft is just myString.Substring(0, myString.IndexOf(pattern)) with some error checking. and InStr is just a wrapper for IndexOf.
//remove order by clause (breaks count(*))
if (Str.InStr(0, SqlQuery.ToLower(), " order by ") > -1)
SqlQuery = Str.SplitLeft(SqlQuery.ToLower(), " order by ");

try
{
//execute query
using (SqlConnection cnSqlConnect = OpenConnection(ConnectionString, SuppressError))
using (SqlCommand SqlCmd = new SqlCommand("select count(*) from (" + SqlQuery + ") as a", cnSqlConnect))
{
SqlCmd.CommandTimeout = 120;
return (Int32)SqlCmd.ExecuteScalar();
}
}
catch (Exception ex)
{
if (SuppressError == false)
MessageBox.Show(ex.Message, "Sql.RecordCount()");

return -1;
}

}

但是它会中断查询(同样,不是我的查询,我只需要让它工作)

select [ClientID], [Date], [Balance] 
from [Ledger]
where Seq = (select top 1 Seq
from [Ledger] as l
where l.[ClientID] = [Ledger].[ClientID]
order by [Date] desc, Seq desc)
and Balance <> 0)

因为它将删除 order by 之后的所有内容并中断查询。我想我可能会从简单的字符串匹配转向更复杂的解析器,但在我这样做之前我想问问是否有更好的方法。

更新: order by 子句被删除,因为如果您使用我的方法或 CTE 包含它,您将收到错误 The ORDER BY 子句在 View 、内联函数、派生的中无效表、子查询和公用表表达式,除非还指定了 TOP 或 FOR XML。

更多细节:该框架用于编写转换应用程序。我们编写应用程序从客户的旧数据库中提取数据,并在客户购买我们的 CRM 时将其移动到我们的数据库格式中。软件。我们经常使用写得不好的源表,其大小可能有几千兆。我们没有资源将整个表保存在内存中,因此我们使用 DataReader 来提取数据,因此所有内容都不会立即在内存中。然而,一个要求是一个进度条,其中包含要处理的记录总数。此 RecordCount 函数用于计算进度条的最大值。它工作得很好,唯一的障碍是,如果编写转换的程序员需要对数据输出进行排序,在最外层的查询中断中有一个 order by 子句 count(*)


部分解决方案:我在尝试解决这个问题时想到了这个,它不会 100% 有效,但我认为它会比当前的解决方案更好

如果我找到一个 order by 子句,我会检查查询中的第一件事是否是一个选择(并且后面没有 Top)我用 select top 100 percent 替换那个开始的文本。它工作得更好,但我没有将其作为解决方案发布,因为我希望有一个通用的解决方案。

最佳答案

假设您只会看到相当普通的 select 语句,我认为您不需要完整的 SQL 解析器来执行您想要的操作。您可以合理地假设您拥有语法上有效的 SQL。不过,您需要构建分词器(词法分析器)。

Transact SQL 所需的词法分析非常简单。 token 列表包括(从我的脑海中脱颖而出,因为我已经有一段时间没有这样做了):

  • 空格
  • 两种评论:
    • -- 风格的注释/.../`式注释
  • 三种类型的引用文字:
    • 字符串文字(例如,`'my string literal'),以及
    • 两种风格的引用保留字用作列或对象名称:
      • ANSI/ISO 风格,使用双引号(例如,"table")
      • Transact-SQL 风格,使用方括号(例如,[table])
  • 十六进制文字(例如,0x01A2F)
  • 数字文字(例如 757-32185.4-7.6E-325.0m , $5.3201 等)
  • 单词,是否保留:一个 unicode 字母、下划线 ('')、'at'-sign ('@') 或哈希 ('#'),后跟零个或多个 unicode 字母、十进制数字、下划线(“”)或 @、美元或井号(“@”、“$”或“#”)。
  • 运算符,包括括号。

几乎所有的事情都可以用正则表达式来完成。如果您使用的是 Perl ,你会在一天内完成,很容易。不过,在 C# 中可能需要更长的时间。

我可能会将注释视为空白,并将多个空白序列和注释折叠成单个空白标记,因为它有助于识别结构,例如 order by

您不需要解析器的原因是您不太关心解析树。您真正关心的是嵌套括号。所以……

  1. 一旦您获得了发出标记流的词法分析器,您需要做的就是吃掉并丢弃计算开/关括号的标记,直到您在括号深度 0 处看到“from”关键字。

  2. select count(*) 写入您的 StringBuilder。

  3. 开始将标记(包括 from)附加到 StringBuilder 中,直到您在括号深度 0 处看到“order by”。您需要构建一定数量的前瞻性进入你的词法分析器来执行此操作(请参阅我之前关于将空白序列和/或注释折叠成单个空白标记的说明。)

  4. 至此,您应该已经大功告成了。执行查询。

注意事项

  1. 参数化查询可能不起作用。

  2. 带有 CTE 和 with 子句的递归查询可能会被破坏。

  3. 这将丢弃 ORDER BY 子句之后的任何内容:如果查询使用查询提示、FOR 子句或 COMPUTE/COMPUTE BY,您的结果可能与原始查询不同(尤其是对于任何 compute 子句,因为它们会分解查询结果集)。

    <
  4. UNION 查询会被破坏,因为像

          select c1,c2 from t1
    UNION select c1,c2 from t2

    会变成

          select count(*) from t1
    UNION select c1,c2 from t2
  5. 所有这些都是完全未经测试的,只是我基于多年来不得不做的古怪事情的想法。

关于c# - 如何对任意查询执行计数(可能包含排序依据),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7891464/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com