我在很多地方都听到过这个想法。或多或少地认识到,一旦尝试完全用SQL解决问题超出了一定程度的复杂性,您确实应该在代码中进行处理。在这种情况下,数据库引擎在寻找完成任务的最有效方式方面将比在代码中做得更好。尤其是涉及使结果取决于对数据执行的操作之类的事情时。可以说,对于现代引擎而言,有效地JIT'ing +将查询的编译版本缓存在表面上是很有意义的。练习(以及为什么)。当数据库中所有逻辑都存在并且您只是通过ORM进行访问时,这些线会变得更加模糊。

评论

这是必须深思熟虑的一句话。每当有人发现另一位工程师在执行“从表中选择*”然后梳理结果集而不是使用where子句和指定列的方法时,它就会被淘汰。但是,如果走得太远,最终会陷入混乱。

以“从不”或“总是”开头的短语几乎总是导致不良设计的秘诀。

虽然当然可以尝试在SQL中做太多事情,但老实说,在30多年的开发和咨询中,我从未见过真正的严重案例(少数)。另一方面,我确实看到过数百个严重的开发人员案例,他们试图在“代码”中做很多本应在SQL中做的事情。我仍然看到他们。经常...

@MrEdmundo转到meta。

这个问题是二合一的,我认为应该分开。 1)在SQL中应该做多少? 2)在DBMS中应该做多少?存储过程位于中间。我已经看到了存储过程中编码的整个应用程序。

#1 楼

用外行的话来说:

这些都是SQL要做的,无论是否相信,我都在代码中看到过: /> joins-以代码方式需要复杂的数组操作

过滤数据(在哪里)-以代码方式需要大量插入和删除列表中的项目在代码方面需要大量列表或数组操作

聚合函数-在代码方面需要数组来保存值和复杂的切换情况

外键完整性-在代码方面需要在插入之前需要查询,并假设没有人会使用应用程序外的数据

主键完整性-从代码角度来看,在插入之前需要查询,并假设没有人将使用应用程序外的数据/>做这些事情而不是依靠SQL或RDBMS会导致编写大量无附加值的代码,这意味着要调试和维护的代码更多。并且危险地假设只能通过应用程序访问数据库。

评论


+10000000000,指出它危险地假设一切只会通过应用程序发生。

–HLGEM
2012年10月23日在17:09

@skynorth这会导致错误的数据库设计。最终,您得到一个数据库,由于该数据库进行的所有后处理,该数据库只能由该应用程序有意义地访问。

– Sirex
2012年10月23日在20:42

@skynorth如果您依靠代码来确保密钥保持完整性,那么您将从数据库中删除RDBMS的基本原理。这没有任何意义,因为访问数据库的每个应用程序都必须确保精确复制该功能。为什么不让DB处理它,因为那是它的设计目的。例如,DB可以本地防止重复密钥。

–瓶Butkus
2012年10月23日在22:18

不要忘记交易!

–Sklivvz
2012年10月24日7:49

@skynorth:tl; dr:应在数据库中实施使数据保持一致的规则。也就是说,对于曾经编写的99%的应用程序,数据(以及数据库)在您的应用程序死掉之后就可以生存。多年来,我已经看过很多次了(嘿,我们需要在Windows / iPhone / Android /无论什么新事物上部署一个版本,因为{insert old platform here}快要消失了,在此处托管或Oracle数据库,然后在其中创建新的UI)。这种趋势没有理由在今天或不久的将来停止。

–二进制担忧者
2012年10月24日9:53



#2 楼

我将其改写为“永远不要在代码中执行SQL Server可以为您做的好事”。

上面的内容倾向于谈论诸如连接,设置操作和查询之类的事情。其背后的目的是将大部分繁重的工作委托给SQL Server(在它擅长的事情上),并尽可能减少IO的数量(因此,让SQL执行连接并使用WHERE子句进行筛选,返回a数据集比其他数据集要小得多。)

评论


如果将所有SQL都比应用程序代码做的更好的事情都放到了SQL层中,那么无论好坏,都会有很多业务逻辑最终存入数据库。我已经看到了,是的,性能非常出色。但是幸运的是,开发团队都非常了解应用程序开发和SQL,因为两者之间的界限非常混乱。我不建议将其作为起点,而应该作为系统变得非常流行并且性能随时间下降之后的终点。

–吉米·霍法(Jimmy Hoffa)
2012年10月23日15:52



课程设置的马匹

– StuperUser
2012年10月23日在16:19

@NathanLong我不知道为什么仍然有很多人认为您无法将SQL保留在源代码控制中。最初,我们只需要在源代码管理中从头开始创建数据库所需的所有存储过程/表脚本/等,然后再使用Visual Studio数据库项目。没有这些项目,效果很好,与这些项目相比,效果更好。 SQL和创建系统所需的所有其他可更改事物一样,应受版本控制!如果您将创建脚本置于版本控制下,而不维护差异脚本使用工具,则可以使用Redgate diff工具对大多数RDBMS进行部署。

–吉米·霍法(Jimmy Hoffa)
2012-10-23 17:28



如果您的SQL支持REGEX操作和字符串操作,那么在SQL中进行操作可能是一个不错的选择。

–kevin cline
2012年10月23日在18:29

@NathanLong:像这样想,一个DB表是由写在文本文件中的一堆代码定义的,其语法类似于“ create table ...”。现在,您可以将文本文件存储在所需的任何SCM中,就像您使用自己喜欢的应用程序语言中的数据库表创建代码调用所需的任何API一样,并将该文本文件存储在SCM中。我认为一个问题是,有些人认为数据库在某种程度上是魔术兽,他们只知道如何编写VB代码(或其他内容),因此他们仅根据所知道的应用程序语言来思考。

– gbjbaanb
2012年10月26日18:06

#3 楼


绝对不要在代码中做任何让您的SQL Server做好的事情(重点是我的问题)。好事,而不是简单地为您做某事。 SQL是一种非常强大的语言。结合内置功能,它可以做很多事情。但是,可以在SQL中执行某些操作这一事实不应成为在SQL中实际执行操作的借口。和往返次数:如果您可以通过将任务发送到服务器来减少数据量,而又不增加往返次数,则该任务属于服务器;如果数据量保持不变或增加而往返次数没有同时减少,则该任务属于您的代码。

请考虑以下示例:


您存储出生日期,并且需要计算一组用户的年龄。您可以让SQL Server进行减法,也可以在代码中进行。往返次数保持不变,发送回给您的数据量也增加。因此,使用基于代码的解决方案很容易
,您可以存储生日,并需要查找20至30岁之间的用户。您可以将所有用户重新加载到客户端,进行减法以查找年龄,然后然后进行过滤,但是将逻辑传送到SQL Server将减少数据量,而无需进行其他往返操作;因此,基于SQL的解决方案将胜出。


评论


当我在某个地方工作时,业务逻辑在SQL中变得混乱,我们在进行多次往返时没有遇到麻烦。我们只是在一次往返中使用了多个结果集,所以该规则有点儿被打破了,尽管该规则的精髓是针对黄金均值

–吉米·霍法(Jimmy Hoffa)
2012年10月23日在16:18

+1这是一个很棒的答案,因为它提供了支持两个方向的具体示例。

–布兰登
13年6月28日在13:37

在第二个示例中。如果场景如下,您怎么说?用户和bday是缓存,并且说记录大小在1000-2000范围内。在内存中执行此操作不是更快,因为缓存了数据,因此无需进行数据库调用,因此避免了“中间” sql操作。处理过程将遍历内存中的1000多个用户列表,并查找匹配的位置。这样会不会比在db中执行速度更快

–user4677228
2015年6月30日12:38



@ user4677228但尝试按比例放大:-p。如果您的代码必须扫描所有数据以计算所有年龄,而您想要的结果只是“有多少用户至少20岁且不到30岁?”,那么缓存将根本无法帮助您。您仍然会最终将整个表流式传输到客户端,但是数据库服务器可以在其内存/缓存中完成所有操作,并且不管数据库客户端是通过本地套接字还是通过网络远程连接,都可以为您提供快速的答案。您只是愿意在WHERE子句中计算年龄。

– Binki
16 Jul 27 '21:07



#4 楼

简而言之,正确地说:“切勿在代码库中执行数据库特定的操作”,因为它们在数据库中会得到更好的解决。

查看设置基本操作的示例。您可能知道,RDBMS是为处理常见的数据存储和操作而构建的。

此外,数据库的项目选择也起着重要的作用。具有RDBMS(MS SQL,Oracle等)与RavenDB等NoSQL数据库不同。

评论


永远不要在您的代码库中进行设置操作,这绝对意味着在LINQ中对集合(选择,求和,位置,单个)的所有操作(选择,求和,位置,单个)都应该在SQL中而不是在您的应用程序中完成,这会将很多业务逻辑放入数据库中。

–吉米·霍法(Jimmy Hoffa)
2012年10月23日在16:10

您描述的内容不是客户代码。这是一个业务层,您可能在其中拥有自己的操作逻辑。但是,对1M +记录执行此逻辑将使您反感。

–尤苏波夫
2012年10月23日在16:19

@JimmyHoffa:并非如此,有时您会生成瞬态信息,这些瞬态信息需要与应用程序内存中已有的数据进行处理。 Linq为此创造了奇迹。

–Fabricio Araujo
2012年10月24日16:57

@FabricioAraujo我知道linq为什么很棒,但是这个答案指出“从不在应用程序代码中进行基于集合的操作”,如果您从不在应用程序代码中进行基于操作的设置,则您永远不会使用linq,因为这就是linq的全部目的。我要指出的是,从不对应用程序代码进行设置操作是遵循的错误规则

–吉米·霍法(Jimmy Hoffa)
2012年10月24日16:59



@JimmyHoffa:不,规则说“ RDBMS不能在应用程序中为您做的好”。我在说的是瞬态信息,而不是持久存在于数据库中的信息。我在需要满足代码处理要求的系统上工作。我记得在对数据库进行大量处理之后,我对该数据进行了额外处理以生成(非常重要的)报告的业务规则。我可以在上面使用linq(它是在现已不存在的Delphi.Net上完成的)。换句话说,即使遵循该规则,也可以使用linq。

–Fabricio Araujo
2012年10月24日17:14

#5 楼

通常,与应用程序相比,您的数据库具有更多可使用的信息,并且可以更有效地执行常见数据操作。例如,您的数据库维护索引,而您的应用程序则必须动态索引搜索结果。因此,在所有其他条件相同的情况下,可以通过将工作推送到数据库而不是应用程序来减少总体工作量。 D b。在大型安装中,经常会看到应用程序服务器的数量超过数据库服务器10到1或更多倍。添加更多应用服务器通常是将现有服务器克隆到新硬件上的简单问题。另一方面,在大多数情况下,添加新的数据库服务器要困难得多。

因此,这一点成为保护数据库的方法。事实证明,通过在memcached中缓存数据库结果或在应用程序侧日志中对更新进行排队,或者通过一次获取数据并计算应用程序中的统计信息,您可以极大地减少数据库的工作量,从而不必再求助到更加复杂和脆弱的数据库集群配置。

评论


金钱可以解决硬件可扩展性问题,而没有任何金钱可以解决软件复杂性。

–图兰斯·科尔多瓦(TulainsCórdova)
2012年10月23日在18:20

@ user1598390确实:硬件便宜,程序员昂贵。金钱可以解决软件的复杂性。花在程序员身上的钱。但是请注意,我们不是在谈论干净代码与speghetti。我们正在谈论的是在应用程序方面还是在数据库方面进行工作。软件的复杂性仅与边缘相关,因为这两种选择都可以遵循良好的设计原则。一个更好的问题是:“哪种设计成本更高?”。

– tylerl
2012年10月23日18:41

一旦您的代码库繁琐且充满脂肪,其中大部分代码都是在做非商业性的工作,那么您唯一可以做的就是所有重新设计的母亲,这不仅要花费硬件,而且涉及太多不确定性,此外您将永远知道在哪里可以找到好的硬件,但是好的程序员却是另一回事……与此同时,您的竞争对手也正在利用他们的时间进行改进,适应变化并让客户满意。

–图兰斯·科尔多瓦(TulainsCórdova)
2012年10月23日在18:51



+1是唯一在答案中提及缩放的人。

–马特
2012年10月23日在20:16



硬件便宜了,不再是便宜了-在数据中心,电力和硬件占运行成本的88%(由Microsoft引用),因此花更多的钱在程序员上编写高效的代码是非常划算的,直到我们获得无限的使用廉价的融合能力。

– gbjbaanb
2012年11月11日14:48

#6 楼

我认为,不使用数据库来完成它的设计将是一个糟糕的设计。我从来没有见过任何数据库在数据良好的数据库之外强制实施规则的数据库。我查看了数百个数据库。

因此必须在数据库中完成的事情:


审核(仅应用程序审核不会跟踪对数据库的所有更改,因此可以
数据敏感度约束,包括默认值,外键
约束和必须始终应用于所有数据的规则。所有
数据不会总是通过应用程序更改或插入,
有一次性数据修复,尤其是大型数据集,这些数据一次
一次不做一个记录(请更新)这些100,000条
记录由于应用代码错误而应被标记为状态1时应被标记为2,或者由于公司B购买了公司A,请将客户A的所有记录更新为客户B的记录和数据导入以及可能接触同一数据库的其他应用程序。
JOINS和where子句过滤(以减少通过网络发送的记录数



#7 楼


“过早的优化是计算机编程中所有邪恶的根源(无论如何,绝大部分)”-Donald Knuth


数据库就是这样;应用程序的数据层。它的工作是为您的应用程序提供所需的数据,并存储提供给它的数据。您的应用程序是放置实际可用于数据的代码的地方。显示,验证等。

标题行中的情绪令人钦佩,并且精确到一定程度(在绝大多数情况下,过滤,投影,分组等的本质都是如此)留给数据库),则“井”的定义可能会适当。 SQL Server可以执行的高性能任务很多,但是可以证明SQL Server以隔离,可重复的方式正确运行的任务很少。 SQL Management Studio是一个很棒的数据库IDE(特别是给了我使用过的其他选项,例如TOAD),但是它有其局限性,首先是要使用它执行的几乎所有操作(或在其中执行的任何过程代码)根据定义,下面的DB)是“副作用”(更改状态位于进程的内存空间范围之外)。此外,SQL Server中的过程代码只是最新的功能,并且可以使用最新的IDE和工具来衡量托管代码使用覆盖率指标和路径分析的方式(因此,您可以证明测试X遇到此特定if语句) ,Y和Z,以及测试X旨在使条件成立,并在Y和Z执行“ else”时执行一半条件,这又假设您有一个测试可以从特定的开始设置数据库状态,通过一些操作执行数据库过程代码,并声明预期结果。

与大多数数据访问层提供的解决方案相比,所有这些都更加困难和复杂。假设数据层(以及DAL)知道如何在给出正确输入的情况下完成工作,然后测试您的代码是否提供正确输入。通过将诸如SP和触发器之类的过程代码保留在数据库之外,而在应用程序代码中执行这些类型的操作,可以更轻松地实现所述应用程序代码。

评论


等等等等您如何从正确性证明到测试,可以证明存在错误,但永远不能证明代码是正确的?

–梅森·惠勒
2012年10月23日17:30

存储过程不是过程代码。 SP是在数据库内部存储并运行的预先计算的SQL查询。它不是应用程序代码。

– gbjbaanb
2012年11月11日14:56

如果SP仅限于SQL查询,那么您是对的。如果它是T-SQL或PL / SQL,包括条件中断,循环,游标和/或其他非查询逻辑,那么您错了。网络空间中数据库中的许多SP,功能和触发器都具有这些额外的元素。

– KeithS
2012年11月12日下午2:09

#8 楼

人们似乎没有意识到的一件事是,不管对代码质量的影响如何,在SQL Server上进行所有处理不一定都很好。

例如,如果您需要获取一些数据,然后从该数据计算出一些数据,然后将该数据存储在数据库中。有两种选择:


将数据收集到您的应用程序中,在您的应用程序中计算,然后将数据发送回数据库中。数据,进行计算,然后通过一次对SQL Server的调用将所有数据存储起来。即使SQL非常不适合该问题(例如正则表达式和字符串操作),我也忽略了。假设您使用SQL CLR或类似的东西甚至在数据库中拥有强大的语言。如果需要1秒钟来回往返并获取数据,然后花1秒钟来存储数据,然后需要10秒钟来对它进行计算。如果您在数据库中全部都做错了,那就错了。

当然,您要剃掉2秒钟。但是,您是将100%的(至少)一个CPU内核浪费了10秒,还是在Web服务器上浪费了该时间?

Web服务器易于扩展,而数据库则非常昂贵,尤其是SQL数据库。大多数情况下,Web服务器也是“无状态的”,可以随时添加和删除,而无需对负载平衡器进行任何其他配置。

因此,不仅要考虑将操作减少2秒钟,还要考虑可伸缩性。当您可以使用便宜得多的Web服务器资源且对性能的影响较小时,为什么要浪费数据库服务器资源之类的昂贵资源

评论


您还忘记了网络旅行-您无法通过添加服务器来水平扩展而不会影响效率。因此,通过添加where子句来减少数据负载是显而易见的-但其他sql操作不一定会降低性能。虽然您的观点总体上是正确的,但您不能将DB视为愚蠢的数据存储。我曾经研究过的最具扩展性的应用程序,它为每个数据调用使用了存储过程(2个复杂的查询除外)。第三个解决方案是最好的-“存储过程仅获取必要的数据”,不确定是否要“计算”。

– gbjbaanb
2012年11月11日14:54

#9 楼

我喜欢看它,因为SQL应该只处理数据本身。决定查询外观的业务规则可以在代码中发生。信息的正则表达式或验证应通过代码完成。应该留给SQL仅用于联接表,查询数据,插入干净的数据等。进行存储,更新,删除或检索某些内容。我已经看到太多的开发人员希望将其业务逻辑和编码用SQL编写,因为他们将数据视为业务。将逻辑与数据脱钩,您会发现代码变得更整洁,更易于管理。

虽然我的$ 0.02。

评论


为什么要对数据库中已有的数据运行正则表达式或验证?约束应该防止不良数据到达那里,而使用正则表达式可能意味着您需要更多有用的列。

–布伦丹·朗(Brendan Long)
2012年10月23日在23:13

我并不是说我将对来自数据库的数据使用正则表达式或验证。我想我应该已经澄清了这是关于去数据库的数据。我的观点是,数据在到达DAL之前应先进行清理和验证。

–小斯坦利·格拉斯(Stanley Glass Jr)
2012年10月24日12:24

#10 楼

通常,我同意代码应控制业务逻辑,而数据库应为无逻辑哈希。但是这里有一些要点:

主代码,外键和必需的(不是null)约束可以由代码强制执行。约束是业务逻辑。因为它们重复了什么代码,是否应该将它们排除在数据库之外?如果这样的话,在数据附近强制执行约束是很好的。可以将访问限制为实现逻辑的Web服务,但这假定您在“第一”并且有权在其他各方上强制使用该服务。每个对象单独的插入/更新?如果是,那么在批处理大数据集时将遇到严重的性能问题。设置操作是必经之路。 ORM在准确建模所有可能对它们进行操作的可能的联接集方面会遇到麻烦。

您是否认为“层”是服务器的物理拆分还是逻辑拆分?从理论上讲,任何服务器上运行的逻辑都可能仍属于其逻辑层。您可以通过编译到不同的DLL中来组织拆分,而不是专门拆分服务器。这可以显着增加响应时间(但会牺牲吞吐量),同时保持关注点分离。拆分的DLL以后可以在没有新版本的情况下移动到其他服务器,以增加吞吐量(以响应时间为代价)。

评论


为什么要下票?

–mike30
2012年10月23日在16:54

我没有投票,但是任何数据库专家都会告诉您,考虑对数据库进行无逻辑哈希是一个非常糟糕的主意。它会导致数据完整性问题或性能问题,或同时引起这两个问题。

–HLGEM
2012年10月23日在17:12

@HLGEM。答案描述了将逻辑保留在数据库中或放置在DB服务器上的原因。仍然没有解释。

–mike30
2012年10月23日在17:45

他们可能没有像我所做的那样对立,这就是为什么我没有投票。

–HLGEM
2012年10月23日17:50

#11 楼

这种习惯用法更多地与保持业务规则,与数据以及关系(数据,结构和关系)有关。它不是解决所有问题的一站式服务,但有助于避免手动操作如果在数据库级别可用,则维护记录计数器,手动维护关系完整性等。因此,如果其他人出现并扩展程序或编写与数据库交互的另一个程序,他们将不必从以前的代码中找出如何维护数据库完整性。当其他人想要编写一个新程序与同一数据库进行交互时,手动维护记录计数器的情况尤为重要。即使新创建的程序具有正确的计数器代码,原始程序和大约同时运行的新程序也可能会破坏它。甚至还有代码可以检索记录并在写入新的或更新的记录之前(通过代码或作为单独的查询)检查条件,如果可能的话,通常可以在insert或update语句中实现。数据损坏可能再次导致。数据库引擎保证原子性;确保带有条件的更新或插入查询仅影响满足条件的记录,并且没有外部查询可以在更新进行到一半时更改数据。在许多其他情况下,最好使用数据库引擎来使用代码。这完全是关于数据完整性而不是性能。

所以这实际上是一个好的设计习惯或经验法则。在数据损坏的系统中,没有任何性能上的帮助。

#12 楼

如前所述,目标是尽可能少地发送到数据库和从数据库接收数据,因为往返时间非常昂贵。反复发送SQL语句是浪费时间,尤其是在更复杂的查询中。背部。由于仅发送名称和一些参数,因此它也减少了发送到服务器的数据。在这种情况下,大多数商务逻辑仍然可以在代码中,但不能以SQL形式。该代码实质上将准备从数据库发送或请求的内容。

#13 楼

有几件事要记住:



关系数据库应通过外键确保引用完整性。扩展一个数据库可能既困难又昂贵。只需添加更多Web服务器,即可轻松扩展Web服务器。尝试增加SQL Server的功能会很有趣。 >

#14 楼


“过早的优化是万恶之源”-Donald Knuth


使用最适合该工作的工具。为了数据完整性,通常是数据库。对于高级业务规则,这是一个基于规则的系统,例如JBoss Drools。对于数据可视化,这将是一个报告框架。等。

如果遇到任何性能问题,则应随后查看是否可以缓存任何数据,或者是否可以更快地实现数据库中的实现。通常,购买额外的服务器或额外的云功能的成本将远远低于增加的维护成本和额外错误的影响。