如何在ADO.NET项目的代码中存储大型查询

我正在开发一个Web项目,其数据访问层基于ADO.NET(用于最快的执行)。 项目中有一些非常大的SQL查询,这些查询是用C#代码内联编写的。 我想知道我是否可以更优雅地移动这些查询以减少一些混乱,但我不确定可以使用什么方法。 我知道资源文件,但这些不能在这里使用,因为一些查询是参数化的。

语言:C#

如果您不能或不愿意使用存储过程并且希望保持Sql接近C#代码,则可以提取Sql并将它们放入项目中包含的外部文件中。

选项1:与子文件夹中的可执行文件一起复制的文本文件

文件复制始终

并访问其内容,如下所示:

private String LoadFileContent() { String fileName = "Sql\\LoadAllData.sql"; if (!File.Exists(fileName)) { String errorMessage = String.Format("File '{0}' does not exist or access to it is denied", fileName); throw new FileNotFoundException(errorMessage, fileName); } String fileContent = String.Empty; using (StreamReader sr = File.OpenText(fileName)) { fileContent = sr.ReadToEnd(); } return fileContent; } 

要么

选项2:作为程序集中的资源嵌入的文本文件

文件作为嵌入式资源

并使用此方法访问该文件:

  private String LoadAssemblyResource() { Assembly assembly = Assembly.GetExecutingAssembly(); String fileName = "StackOverflowWinForm.SQL.LoadAllData.sql"; // Handy bit of debug code to list all the resource names in case there // is an issue trying to find/load a resource String[] resourceNames = assembly.GetManifestResourceNames(); String fileContent = String.Empty; using (Stream stream = assembly.GetManifestResourceStream(fileName)) { if (stream == null) { String errorMessage = String.Format("Resource File '{0}' does not exist", fileName); throw new MissingManifestResourceException(errorMessage); } using (StreamReader reader = new StreamReader(stream)) { fileContent = reader.ReadToEnd(); } } return fileContent; } 

如果您正在开发Web应用程序或Web服务,我建议使用嵌入式资源的后一种方法,这样您就不必过于担心被黑客攻击/更改的Web服务器上的映射路径,安全性和文本文件。

这两种方法都替换了代码中的字符串文字并将它们移动到外部文件中。 加载外部文件后,仍可以像以前一样操作字符串。

我经常使用这两种方法,具体取决于所讨论的Sql的具体情况和大小。

我建议将查询放入SQL存储过程,并使用ADODB.Command对象来运行它们。 您可以将相同的主体应用于运行System.Data.SqlClient查询的代码。

根据构建查询的复杂程度,创建多个查询以替换内联调用可能是有意义的。 特别是,如果您有一个按产品或按存储或按链(例如)过滤的代码块,并且这些代码都由一个动态C#代码块处理以构建命令,那么最好有3个单独的SQL中的存储过程处理每个案例,而不是尝试在一个过程中复制该动态行为。

这种方法的另一个好处是,由于SQL构建查询计划的方式,您可能会发现存储过程具有更好的整体性能和索引调优机会。

关于SQL如何管理查询计划的一些一般性评论:

如果在数据库中构建存储过程,SQL将在第一次运行每个过程时生成查询计划,使用调用所需的任何参数和流来优化查询。 如果动态加载查询 – 无论是使用生成的动态SQL还是加载保存为文件的SQL脚本 – 那么SQL每次运行调用时都会运行此分析。

每次运行生成查询计划都会影响性能。 根据您的数据库和查询,此命中可能非常小 – 每天运行一次的查询只需几毫秒 – 或者非常重要 – 每天运行数千或数百万次的查询只需一秒或两秒。

将您的呼叫拆分为3个单独的过程是一个好主意,因为SQL Server在第一个运行示例上构建计划。 如果你有一个带有可选ID值的过程,如果你传递了值就返回一行,或者如果你没有那么返回所有行…那么根据首先调用哪一行,SQL会尝试做每次调用它时都要进行索引查找或表扫描,这对于其他操作都不是最佳选择。 将其拆分为两个单独的调用允许SQL为每个操作生成最佳查询计划。

另一个方面是记录和分析。 许多SQL性能工具(包括SQL内置的工具)能够查看与相关存储过程相同的多个调用,并确定长期性能趋势。 有些工具甚至可以很好地确定程序的确切部分,这些部分运行得很差。 但是,如果您使用的是动态生成的SQL,则这些调用将成为单独事件的大海。 因此,如果你有一个长时间运行的存储过程每天冒一次或两次,那么你每天数百万次的呼叫会丢失,但如果3秒呼叫是一个存储过程,那么你可以看到它总共变为90服务器工作负载的百分比,是重构和查询调优的理想选择。

因此,虽然您感觉有点像违反DRY主体以生成多个类似的查询作为单独的存储过程,但您希望在使用SQL时调整您的思维模式以获得最佳性能。