几个月前,我开始在这家编程公司工作。他们使用的一种做法是在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或'正常'代码?
答案 0 :(得分:20)
这只是一个糟糕的编程习惯。您应该分离并隔离程序的不同部分,以便日后维护(想想下一个程序员!)
<强>性能强>
许多解决方案的DB性能较差,因此大多数开发人员通常会限制SQL数据库访问可能的最小事务。理想情况下,原始数据到人类可读形式的转换应该在最后一点发生。此外,非格式化数据的内存使用量要小得多,虽然内存很便宜,但您不应该浪费它。缓冲,缓存和传输的每个额外字节都会花费时间,并减少可用的服务器资源
e.g。对于Web应用程序格式化应该由JSON数据包中的本地JavaScript模板完成。这减少了后端SQL数据库和应用程序服务器的工作量,并减少了需要通过网络传输的数据,所有这些都加快了服务器性能
格式化和本地化
许多解决方案对同一交易有不同的输出需求,例如不同的视图,不同的本地化等。通过将格式嵌入到SQL事务中,您将不得不为每个本地化创建一个新的事务,这将成为维护的噩梦
格式化的事务也不能用于API接口,你需要另一组API接口的事务,它没有格式化
使用c#你应该使用经过良好测试的模板或字符串处理库,或者至少 string.Format(),不要在字符串中使用'+'运算符,它非常慢< / p>
分担负担
大多数解决方案都有一个数据库的多个客户端,因此客户端格式化负载与多个客户端CPU共享,而不是单个SQL数据库CPU
我严重怀疑SQL比c#更快,你应该执行一个简单的基准测试并在此处发布结果: - )
答案 1 :(得分:13)
第二部分可能慢一点的原因是,因为你需要从SQL服务器中提取数据并将其提供给C#部分代码,这需要更多时间。
ConvertToKbOrMb(result["FileSizeB"])
所做的阅读次数越多,总是需要花费更多时间,而且还要依赖于DAL图层。我看到一些DAL真的很慢。
如果您将它们留在SQL Server上,您将获得额外的数据处理,这就是全部。
从经验来看,我的一个优化总是只提取所需的数据 - 你从sql server读取的数据越多,并将其移动到任何地方(asp.net,console,c#program等),你的时间就越多花钱来移动它们,特别是如果它们是大字符串,或者从字符串到数字进行大量转换。
回答直接问题,什么是更快 - 我说你无法比较它们。如果您制作好的代码和良好的查询,它们都会尽可能快。 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);
}
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处理慢。
答案 2 :(得分:8)
作为一般经验法则: 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#例子”对我来说看起来更好。
答案 3 :(得分:5)
有充分的理由在数据库服务器上尽可能多地做。最大限度地减少必须来回传递的数据量,并为服务器提供优化流程的余地,这是一件好事。
但是,您的示例中并未真正说明这一点。两个进程来回传递尽可能多的数据(可能第一次传递更多),唯一的区别是谁进行计算,可能是客户端做得更好。
答案 4 :(得分:5)
您的问题是关于字符串操作操作是否应该在C#或SQL中完成。我认为这个例子非常小,任何性能增益 - 单向或其他 - 都是无关紧要的。问题是“应该在哪里完成”?
如果代码是部分应用程序的“一次性”代码,那么在应用程序级别进行操作非常有意义。如果在整个应用程序中重复此代码,则需要封装它。我认为封装它的最佳方法是使用SQL Server计算列,视图,表值函数或标量函数(在这种情况下计算列更可取)。这确保无论在何处调用,相同的处理都相同。
在性能方面,数据库代码和C#代码之间存在关键差异。数据库代码自动并行运行。因此,如果您的数据库服务器是多线程的,那么单独的线程可能同时进行这些字符串操作(没有承诺,这里的关键字是“可能”)。
通常,在考虑拆分时,您希望最小化来回传递的数据量。这种情况的差异似乎很小。
因此,如果这是具有此逻辑的应用程序中的一个位置,则在应用程序中执行此操作。如果应用程序填充了对此表的需要此逻辑的引用,那么请考虑计算列。如果应用程序在不同的表上有很多类似的请求,那么请考虑一个标量值函数,尽管这可能会影响查询利用并行性的能力。
答案 5 :(得分:4)
这实际上取决于你正在做什么。
不要忘记SQL CLR。有许多操作,T-SQL代码只是速度较慢。
答案 6 :(得分:2)
通常在生产环境中,数据库基础结构层的两倍,有时是应用程序层的三倍。
此外,对于针对数据库本机运行的SQL代码,将具有在应用程序上运行SQL代码并通过数据库驱动程序传递的SQL代码的强大优势。