SQL代码比C#代码更快吗?

几个月前,我开始在这家编程公司工作。 他们使用的一种做法是在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或“普通”代码?

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

性能

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

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

格式化和本地化

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

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

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

分担负担

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

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

第二部分可能会慢一点的原因因为你需要从SQL服务器中提取数据并将其提供给C#部分代码,这需要更多时间。

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

如果您将它们留在SQL Server上,您将获得获取数据的额外处理,这就是全部。

根据经验,我的一个优化是始终只提取所需的数据 – 从sql服务器读取的数据越多,并将其移动到任何地方(asp.net,console,c#program等),你花费的时间越多它们周围,特别是如果它们是大字符串,或者从字符串到数字进行大量转换。

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

我自己进行一次测试

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

我运行两个案例。

 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); } 

VS

 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处理慢。

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

在SQL中尽可能多地做,是的,但只要它服务于那个目标。 我会仔细研究你的“SQL例子”,完全是基于这个原因。 您的“C#示例”看起来更像是对我的责任分离。

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


至于这种特殊情况下的表现:

我希望“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#例子”对我来说看起来更好。

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

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

您的问题是关于字符串操作操作是否应该在C#或SQL中完成。 我认为这个例子非常小,任何性能增益 – 单向或其他 – 都是无关紧要的。 问题是“应该在哪里完成”?

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

在性能方面,数据库代码和C#代码之间存在关键差异。 数据库代码自动并行运行。 因此,如果您的数据库服务器是multithreading的,那么单独的线程可能同时进行这些字符串操作(没有承诺,这里的关键字是“可能”)。

通常,在考虑拆分时,您希望最小化来回传递的数据量。 这种情况的差异似乎很小。

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

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

不要忘记SQL CLR 。 有很多操作,T-SQL代码只是速度较慢。

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

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