【问题标题】:Is SQL code faster than C# code? [closed]SQL 代码比 C# 代码快吗? [关闭]
【发布时间】:2014-02-23 21:33:16
【问题描述】:

几个月前,我开始在这家编程公司工作。他们使用的一种做法是在 SQL 而不是 C# 中做尽可能多的工作。

所以,假设我有一个编写一些文件列表的简单示例:

是这样的:

string SQL = @"
    SELECT f.FileID,
           f.FileName,
           f.FileExtension,
           '/files/' + CAST(u.UserGuid AS VARCHAR(MAX)) + '/' + (f.FileName + f.FileExtension) AS FileSrc,
           FileSize=
           CASE
               WHEN f.FileSizeB < 1048576 THEN CAST(CAST((f.FileSizeB / 1024) AS DECIMAL(6, 2)) AS VARCHAR(8)) + ' KB'
               ELSE CAST(CAST((f.FileSizeB / 1048576) AS DECIMAL(6, 2)) AS VARCHAR(8)) + ' MB'
           END
      FROM Files f
INNER JOIN Users u
        ON f.UserID = u.UserID
";

// some loop for writing results {
//     write...
// }

比这样更快或更好:

string SQL = @"
    SELECT u.UserGuid,
           f.FileID,
           f.FileName,
           f.FileExtension,
           f.FileSizeB
      FROM Files f
INNER JOIN Users u
        ON f.UserID = u.UserID";

// some loop for writing results {
       string FileSrc = "/Files/" + result["UserGuid"] + "/" + result["FileName"] + result["FileExtension"];
       string FileSize = ConvertToKbOrMb(result["FileSizeB"]);  
//     write...
// }

这个特定的代码无关紧要(这只是一些基本示例)......问题是关于这种事情的一般情况......在 SQL 或 SQL 上施加更多负载会更好吗? “正常”代码?

【问题讨论】:

  • 这将取决于很多事情:您的数据库结构、您正在执行的操作、您的机器与数据库服务器相比的性能等等。一般 你最好让数据库做数据库擅长的事情。
  • 一般来说,不能说苹果是否比橘子快。
  • 除了 John Saunders 的评论和 Jim Mischel 的评论,查看 Indexes(运行 Profiler 以微调 T-SQL 代码)。您可以在 Db 端做一些事情来确保传输数据的速度更快。

标签: c# sql sql-server


【解决方案1】:

这只是一种糟糕的编程习惯。您应该分离和隔离程序的不同部分,以便于将来的维护(想想下一个程序员!)

性能

许多解决方案的数据库性能不佳,因此大多数开发人员通常将 SQL 数据库访问限制为可能的最小事务。理想情况下,原始数据到人类可读形式的转换应该在可能的最后一点发生。此外,非格式化数据的内存使用量要小得多,虽然内存很便宜,但你不应该浪费它。每一个额外的字节被缓冲、缓存和传输都会占用时间,并减少可用的服务器资源

例如对于 Web 应用程序的格式化应该由 JSON 数据包中的本地 JavaScript 模板完成。这减少了后端 SQL 数据库和应用服务器的工作量,减少了需要通过网络传输的数据,所有这些都加快了服务器性能

格式化和本地化

许多解决方案对同一事务有不同的输出需求,例如不同的视图,不同的本地化等。通过将格式化嵌入到 SQL 事务中,您必须为每个本地化创建一个新事务,这将成为维护的噩梦

同样格式化的事务不能用于 API 接口,您需要另一组没有格式化的 API 接口的事务

使用 c# 你应该使用经过良好测试的模板或字符串处理库,或者至少 string.Format(),不要对字符串使用 '+' 运算符,这很慢

分担负载

大多数解决方案都有多个客户端用于一个 DB,因此客户端格式化负载由多个客户端 CPU 共享,而不是单个 SQL 数据库 CPU

我严重怀疑 SQL 比 c# 快,你应该执行一个简单的基准测试并将结果发布在这里:-)

【讨论】:

  • 字符串上的链式 + 运算符被转换为对使用线程本地 StringBuilder 的 string.Concat 的调用
  • 想想下一个程序员!
【解决方案2】:

第二部分可能会慢一点的原因是因为你需要从SQL server中拉出数据并把它交给C#部分代码,这需要更多的时间。

ConvertToKbOrMb(result["FileSizeB"]) 这样的阅读越多,总是需要更多的时间,并且还取决于您的 DAL 层。我看到了一些非常慢的 DAL。

如果您将它们留在 SQL Server 上,您将获得获取数据的额外处理,仅此而已。

根据经验,我的优化之一是始终只提取所需的数据 - 您从 sql 服务器读取的数据越多并将其移动到任何位置(asp.net、控制台、c# 程序等),您花费的时间就越多花费来移动它们,特别是如果它们是大字符串,或者进行大量从字符串到数字的转换。

要回答直接问题,什么更快 - 我说你无法比较它们。如果您编写良好的代码和良好的查询,它们都会尽可能快。 SQL Server 还保留了大量的统计信息并改进了返回查询——c# 没有这种部分,那拿什么来比较呢?

我自己的一个测试

好的,我这里有很多来自一个项目的数据,并进行快速测试,实际上并不能证明一个比另一个快。

我运行了两个案例。

SELECT TOP 100 PERCENT cI1,cI2,cI3 
  FROM [dbo].[ARL_Mesur] WITH (NOLOCK)  WHERE [dbo].[ARL_Mesur].[cWhen] > @cWhen0;

        foreach (var Ena in cAllOfThem)
        {
            // this is the line that I move inside SQL server to see what change on speed
            var results = Ena.CI1 + Ena.CI2 + Ena.CI3;

            sbRender.Append(results);
            sbRender.Append(Ena.CI2);
            sbRender.Append(Ena.CI3);
        }

SELECT TOP 100 PERCENT (cI1+cI2+cI3) as cI1,cI2,cI3 
   FROM [dbo].[ARL_Mesur] WITH (NOLOCK)  WHERE [dbo].[ARL_Mesur].[cWhen] > @cWhen0;


        foreach (var Ena in cAllOfThem)
        {
            sbRender.Append(Ena.CI1);
            sbRender.Append(Ena.CI2);
            sbRender.Append(Ena.CI3);
        }

结果显示速度差不多。 - 所有参数都是double - 读取已优化,我根本不进行额外读取,只需将处理从一个部分移至另一部分。

165,766 行上,有一些结果:

Start  0ms  +0ms
 c# processing  2005ms  +2005ms
sql processing  4011ms  +2006ms


Start  0ms  +0ms
 c# processing  2247ms  +2247ms
sql processing  4514ms  +2267ms


Start  0ms  +0ms
 c# processing  2018ms  +2018ms
sql processing  3946ms  +1928ms

Start  0ms  +0ms
c# processing  2043ms  +2043ms
sql processing  4133ms  +2090ms

所以,速度可能会受到许多因素的影响......我们不知道您的公司是什么问题导致 c# 比 sql 处理慢。

【讨论】:

  • 他们看起来都在拉相同的行。唯一的区别是计算列是否计算。例如。字符串连接在客户端或服务器上完成。
  • @MartinSmith 这段代码不能给我更多的线索,除了我看到的这部分,result["FileName"],这个ConvertToKbOrMb 和所有的内容。这些是需要一次又一次进行的额外读取,这需要更多时间并取决于如何读取它们,是否首先将其放在数组中,如果不是,直接读取或使用一些 DAL ?...
  • 好吧,这个特定的代码无关紧要(这只是一些基本示例)......问题一般是关于这种事情
  • @Draško 好的,我真的根据经验回答,我发现从服务器读取数据时存在一些延迟。在那之后,如果你有一个缓慢的 DAL 也是一件大事,这是一种在 C# 部分处理数据的缓慢方法。所有这些都是你们公司所说的,实际上没有给出一个非常具体的例子,在时间和延迟方面有具体的计数 - 这就是为什么我根据经验而不是实际数据来回答。
  • 我真的不明白你的意思。如果您只是使用数据读取器循环遍历结果,那么在C# 中进行这两种计算肯定会比 TSQL 更快。而且您通过网络发送的冗余数据更少。连接的字符串包括重复的常量子字符串,使其比所需的可变部分更长。
【解决方案3】:

作为一般经验法则:SQL 用于操作数据,而不是格式化数据的显示方式。

在 SQL 中尽你所能,是的,但只要它服务于那个目标。仅在此基础上,我会仔细研究您的“ SQL示例”。您的“C# 示例”对我来说看起来像是更清晰的职责分离。

话虽如此,请不要太过分,停止在SQL中做应该在SQL中完成的事情,例如过滤和加入。例如,在 C# 中重新实现 INNER JOIN Users u ON f.UserID = u.UserID 在性能方面将是一场灾难。


至于这种特殊情况下的性能:

我希望“C# 示例”(不是所有 C#,只是这个示例)会稍微快一些,因为...

    f.FileSizeB

...看起来比...窄

   '/files/' + CAST(u.UserGuid AS VARCHAR(MAX)) + '/' + (f.FileName + f.FileExtension) AS FileSrc,
   FileSize=
   CASE
       WHEN f.FileSizeB < 1048576 THEN CAST(CAST((f.FileSizeB / 1024) AS DECIMAL(6, 2)) AS VARCHAR(8)) + ' KB'
       ELSE CAST(CAST((f.FileSizeB / 1048576) AS DECIMAL(6, 2)) AS VARCHAR(8)) + ' MB'
   END

...这应该可以节省一些网络带宽。而且网络带宽往往是比 CPU(尤其是客户端 CPU)更稀缺的资源。

当然,您的里程可能会有所不同,但无论哪种方式,性能差异都可能足够小,因此其他问题(例如代码的整体可维护性)变得相对更重要。坦率地说,在这方面,您的“C# 示例”在我看来更好。

【讨论】:

    【解决方案4】:

    有充分的理由在数据库服务器上做尽可能多的事情。尽量减少必须来回传递的数据量,并为服务器提供尽可能多的优化流程的余地。

    但是,您的示例并未真正说明这一点。两个进程来回传递尽可能多的数据(也许第一个传递更多),唯一的区别是谁进行计算,可能是客户端做得更好。

    【讨论】:

      【解决方案5】:

      您的问题是关于字符串操作操作应该在 C# 还是 SQL 中完成。我认为这个例子是如此之小,以至于任何性能提升——单向或其他——都无关紧要。问题是“应该在哪里完成”?

      如果代码是应用程序一部分的“一次性”代码,那么在应用程序级别进行操作就很有意义。如果此代码在整个应用程序中重复出现,那么您需要封装它。我认为封装它的最佳方法是使用 SQL Server 计算列、视图、表值函数或标量函数(在这种情况下,计算列更可取)。这确保了无论在哪里调用,相同的处理都会发生相同的情况。

      就性能而言,数据库代码和 C# 代码之间存在关键区别。数据库代码自动并行运行。因此,如果您的数据库服务器是多线程的,那么单独的线程可能会同时执行这些字符串操作(没有承诺,这里的关键词是“可能”)。

      一般来说,在考虑拆分时,您希望尽量减少来回传递的数据量。这种情况下的差异似乎很小。

      因此,如果这是具有此逻辑的应用程序中的一个位置,则在应用程序中执行此操作。如果应用程序充满了需要此逻辑的对该表的引用,那么请考虑计算列。如果应用程序在不同的表上有很多类似的请求,那么考虑一个标量值函数,尽管这可能会影响查询利用并行性的能力。

      【讨论】:

      • 将其包装在标量 UDF 中实际上保证了它不会并行 sqlblog.com/blogs/paul_white/archive/2011/12/23/…
      • 但是除非/直到他们实现了并行性的规定好处,并且标量 UDF 的建议实现相互矛盾。 DB 方面的另一个可能的优点(理论上)是计算仅使用表Files 中的列。如果每个文件与许多用户相关联并且计算成本很高,则查询优化器原则上可以在连接之前对其进行一次评估,而不是之后多次评估。
      【解决方案6】:

      这真的取决于你在做什么。

      不要忘记SQL CLR。有很多操作是 T-SQL 代码比较慢的。

      【讨论】:

        【解决方案7】:

        通常在生产环境中,数据库基础架构层的资源是应用层的两倍,有时是应用层的三倍。

        此外,对于在数据库上本地运行的 SQL 代码,SQL 代码在应用程序上运行并通过数据库驱动程序传递具有明显优势。

        【讨论】:

        • SQL Server 许可的成本是不在那里这样做的一个论据。例如。请参阅 Brent Ozar 关于排序 here 的评论。不过,很多环境可能都有备用容量。
        猜你喜欢
        • 1970-01-01
        • 2013-05-18
        • 1970-01-01
        • 1970-01-01
        • 2015-11-02
        • 1970-01-01
        • 2015-03-26
        • 2010-10-09
        相关资源
        最近更新 更多