代码之家  ›  专栏  ›  技术社区  ›  user1451111

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

  •  1
  • user1451111  · 技术社区  · 7 年前

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

    语言:C#

    2 回复  |  直到 7 年前
        1
  •  4
  •   JayV    7 年前

    如果您不能或不愿意使用存储过程,并且希望将Sql保持在接近C代码的位置,则可以提取Sql并将其放入外部文件中,如图所示,这些文件包含在项目中。

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

    File Copy Always

    并按如下方式访问其内容:

        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:作为资源嵌入程序集中的文本文件

    File as embedded resource

    并使用以下方法访问文件:

        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的大小使用这两种方法。

        2
  •  0
  •   Randy R    6 年前

    我建议将查询放入SQL存储过程中,并使用ADODB。命令对象运行它们。您可以将相同的主体应用于运行系统的代码。数据SqlClient查询。

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

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

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

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

    每次运行生成查询计划都会影响性能。根据您的数据库和查询,这种影响可能很小(对于每天运行一次的查询而言是几毫秒),对于每天运行数千次或数百万次的查询而言则是几秒钟或两秒钟。

    将调用拆分为3个单独的过程是一个好主意,因为SQL Server在第一次运行的示例上构建了计划。如果您有一个接受可选ID值的过程,并且如果传递该值,则返回一行,如果不传递,则返回所有行。。。然后,根据首先调用的是哪一个,SQL将尝试在每次调用它时执行索引查找或表扫描,这两种操作都不适合其他操作。将其拆分为两个单独的调用可以让SQL为每个操作生成最佳的查询计划。

    这方面的另一个方面更多用于日志记录和分析。许多SQL性能工具(包括内置于SQL中的工具)能够查看同一存储过程的许多相关调用,并确定长期性能趋势。有些工具甚至可以很好地确定过程中运行不佳的确切部分。但是,如果您使用的是动态生成的SQL,那么这些调用都会成为一个独立事件的海洋。因此,如果有一个长时间运行的存储过程每天冒泡一次或两次,那么每天进行数百万次的3秒调用就会丢失,但是如果3秒调用是一个存储过程,那么您可以看到它总共会占服务器工作负载的90%,是重构和查询调优的一个很好的候选者。

    因此,虽然感觉您违反了DRY原则,将多个类似的查询作为单独的存储过程生成,但您希望在使用SQL时调整心态,以获得最佳性能。