最常见的SQL反模式是什么?[关闭]


232

我们所有使用关系数据库的人都已经知道(或正在学习)SQL是不同的。选出想要的结果并有效地进行,涉及到一个乏味的过程,其部分特征是学习不熟悉的范例,并发现我们最熟悉的编程模式在这里不起作用。您见过(或自己犯过)的常见反模式是什么?


此问题不符合关于哪种类型的问题适用于Stack Overflow的新标准。当被问到时,可能并非如此。
David Manheim

@casperOne难道没有一些“历史意义”条款会使这个问题成为可以接受的吗?
艾米·B

26
我感到遗憾的是,在孔洞站点上最有用的问题之一被关闭,因为它不是建设性的。
HLGEM 2015年

11
@HLGEM我完全同意。这个问题是StackExchange所有错误的完美示例
Kevin Morse

1
该主题绝对重要且相关。但是问题太开放了,这就是为什么每个答案都描述了单个工程师的反模式错误。
Shane

Answers:


156

对于大多数程序员在数据访问层中混合使用其UI逻辑的趋势,我始终感到失望:

SELECT
    FirstName + ' ' + LastName as "Full Name",
    case UserRole
        when 2 then "Admin"
        when 1 then "Moderator"
        else "User"
    end as "User's Role",
    case SignedIn
        when 0 then "Logged in"
        else "Logged out"
    end as "User signed in?",
    Convert(varchar(100), LastSignOn, 101) as "Last Sign On",
    DateDiff('d', LastSignOn, getDate()) as "Days since last sign on",
    AddrLine1 + ' ' + AddrLine2 + ' ' + AddrLine3 + ' ' +
        City + ', ' + State + ' ' + Zip as "Address",
    'XXX-XX-' + Substring(
        Convert(varchar(9), SSN), 6, 4) as "Social Security #"
FROM Users

通常,程序员之所以这样做,是因为他们打算将其数据集直接绑定到网格,并且使SQL Server格式的服务器端比客户端的格式更方便。

上面显示的查询非常脆弱,因为它们将数据层紧密耦合到UI层。最重要的是,这种编程风格彻底阻止了存储过程的重用。


10
一个良好的张贴子模式,可以在最大数量的层/抽象层之间实现最大耦合。
dkretz

3
尽管出于性能原因,我经常这样做,但SQL Server进行的迭代更改比中间层代码执行的速度要快,尽管这样做可能不利于解耦。我不了解您的可重用性,如果您愿意,没有什么可以阻止您运行SP并重命名cols。
乔·皮内达

54
我最喜欢的是人们嵌入HTML和javascript时,例如SELECT'<a href=... onclick="">'+名称'</a>'
Matt Rogish 09年

15
通过这样的查询,您可以使用简单的alter语句在网站中编辑网格。或更改导出的内容,或在报告中重新格式化日期。这使客户满意,并节省了我的时间。所以,谢谢,但是不,我会坚持这样的查询。
安多玛尔

4
@Matt Rogish-耶稣,实际上有人这样做吗?
Axarydax

118

这是我的前3名。

数字1.无法指定字段列表。(编辑:为避免混淆:这是一条生产代码规则。它不适用于一次性分析脚本-除非我是作者。)

SELECT *
Insert Into blah SELECT *

应该

SELECT fieldlist
Insert Into blah (fieldlist) SELECT fieldlist

数字2。使用游标和while循环,当使用带有循环变量的while循环时即可。

DECLARE @LoopVar int

SET @LoopVar = (SELECT MIN(TheKey) FROM TheTable)
WHILE @LoopVar is not null
BEGIN
  -- Do Stuff with current value of @LoopVar
  ...
  --Ok, done, now get the next value
  SET @LoopVar = (SELECT MIN(TheKey) FROM TheTable
    WHERE @LoopVar < TheKey)
END

数字3。通过字符串类型的DateLogic。

--Trim the time
Convert(Convert(theDate, varchar(10), 121), datetime)

应该

--Trim the time
DateAdd(dd, DateDiff(dd, 0, theDate), 0)

我最近看到过一个高峰,“一个查询比两个查询好吗?”

SELECT *
FROM blah
WHERE (blah.Name = @name OR @name is null)
  AND (blah.Purpose = @Purpose OR @Purpose is null)

此查询需要两个或三个不同的执行计划,具体取决于参数的值。对于此sql文本,仅生成一个执行计划并将其粘贴到缓存中。无论参数值如何,都将使用该计划。这会导致间歇性的性能下降。最好编写两个查询(每个预期的执行计划一个查询)。


7
嗯,我将单独为点2和点3给您+1,但开发人员会忽略规则1。
annakata

1
#1背后的原因是什么?
jalf

29
使用select *时,您将获得表中的所有内容。这些列可能会更改名称和顺序。客户端代码经常依赖于名称和顺序。每隔6个月,系统会询问我如何在修改表时保留列顺序。如果遵循规则,那就没关系了。
艾米B

有时我使用了#2,其他人则使用了游标路线(尽管然后我首先将查询结果保存在表var中,在其上打开游标)。我一直想知道是否有人对两者都进行了性能测试。
乔·皮内达

4
...但是当然,在无法弄清楚如何使用基于集合的SQL来完成工作之后,游标几乎应该永远是最后的选择。我曾经花了大约45分钟的时间仔细地在存储过程中剖析了一个巨大的PL / SQL游标(绘制了烂东西的图表),该游标填充了一个大的临时表,然后选择了临时表的内容返回给调用方以呈现一个报告。在坚固的硬件上运行需要8.5分钟。绘制完整个图之后,我可以用一个查询替换它,在2秒内返回相同的结果。游标,伙计……
克雷格2014年

71
  • 可读的密码字段,例如:adad。自我解释。

  • 对索引 列使用LIKE,而我几乎很想一般地说一下LIKE。

  • 回收SQL生成的PK值。

  • 惊讶的是,没有人提到神桌。没有什么比100列位标志,大字符串和整数更像“有机”了。

  • 然后是“我想念.ini文件”模式:在大型文本字段中存储CSV,管道分隔字符串或其他解析所需数据。

  • 对于MS SQL Server来说,根本就不能使用游标。有一种更好的方法可以执行任何给定的游标任务。

编辑,因为有太多!


19
关于游标的错误,我会犹豫地说做某件事是100%正确还是100%错误
Shawn

4
到目前为止,我所看到的每个游标防御示例都在使用错误的工具来完成这项工作。但是,如果您只知道SQL,那么您要么不适当地使用它,要么学习编写其他类型的软件。
dkretz

3
@tuinstoel:LIKE'%blah%'如何使用索引?索引依赖于排序,本示例搜索字符串的随机中间位置。(索引按第一个字符1st排序,因此看中间的4个字符实际上是随机的顺序...)
MatBailie

12
在大多数数据库服务器(至少是我使用过的服务器)上,LIKE可以使用索引..只要它是前缀搜索(LIKE'xxx%'),即只要通配符不在搜索字符串中排在第一位。我认为您可能在这里有点交叉用途。
考恩

10
就像你不喜欢LIKE '%LIKE'
约翰

62

不必对此进行深入研究:不使用准备好的语句。


3
对。以我的经验,在相同的上下文中,紧随其后的是“没有陷阱错误”。
dkretz

1
@stesch:与使用视图和具有可变的报告日期相比,这没什么。如果您有可变的报告日期,则视图是一种反模式(我假设大多数应用程序都有)。要将其添加到单独的答案中,但是很遗憾,它已关闭。
Stefan Steiger 2013年

56

使用无意义的表别名:

from employee t1,
department t2,
job t3,
...

使读取大型SQL语句比需要的难得多


49
别名?地狱,我已经看到了像这样的实际列名
annakata

10
简短的别名是好的。如果您想要一个有意义的名称,那么根本不要使用别名。
Joel Coehoorn

43
他没有说“简洁”,而是说“毫无意义”。在我的书中,在示例查询中使用e,d和j作为别名不会有任何问题。
罗伯特·罗斯尼

11
绝对,罗伯特-e,d和j对我很好。
托尼·安德鲁斯

8
我会用emp来表示雇员,将dep用作部门,对工作要使用工作(或也许是jb):)
AndreiRînea08年

53
var query = "select COUNT(*) from Users where UserName = '" 
            + tbUser.Text 
            + "' and Password = '" 
            + tbPassword.Text +"'";
  1. 盲目信任用户输入
  2. 不使用参数化查询
  3. 明文密码

通过使用某种(任何)种类的数据库抽象层,可以有效地处理所有这些问题。
dkretz

@doofledorfer:同意,在这样的情况下,中间层肯定会更好,再加上提供结果缓存作为很好的副作用。
乔·皮内达

很棒的例子。如果开发人员对如何用一个好的解决方案替代它感到困惑,那么他们就成为了一个体面的SQL开发人员。
史蒂夫·麦克劳德

46

我的烦恼是450列Access表,这些表是由8岁的董事总经理最好的朋友狗美容师的儿子整理而成的,而狡猾的查找表由于某些人不知道如何正确地标准化数据结构而存在。

通常,此查找表如下所示:

ID INT,
名称NVARCHAR(132),
IntValue1 INT,
IntValue2 INT,
CharValue1 NVARCHAR(255),
CharValue2 NVARCHAR(255),
日期1 DATETIME,
日期2 DATETIME

我已经失去了我所见过的拥有这样的可恶系统的客户数量。


1
更糟糕的是,我读到实际上自动支持的最新版本的Access,我担心它将鼓励更多的Value1,Value2,Value3 ...专栏作家
Joe Pineda

等待-8岁的儿子是狗美容师的儿子吗?
barrypicker

28

我最不喜欢的是

  1. 在创建表,存储过程等时使用空格。我对CamelCase或under_scores以及单数或复数以及大写或小写字母都很好,但是必须引用表或列[带空格],尤其是如果[奇数行距](是的,我碰到这个)真的让我很生气。

  2. 非规范化数据。一张表不一定要完全规范化,但是当我遇到一个雇员表时,该表具有有关其当前评估分数或主要指标的信息,它告诉我在某个时候我可能需要制作一个单独的表,并且然后尝试使其保持同步。我将首先对数据进行规范化,然后,如果我看到非规范化有帮助的地方,我会考虑的。

  3. 过度使用视图或光标。视图是有目的的,但是当每个表都包装在视图中时,它就太多了。我不得不使用游标几次,但是通常您可以使用其他机制。

  4. 访问。程序可以是反模式吗?我们正在使用SQL Server,但是由于非技术用户具有可用性,“易用性”和“友好性”,因此许多人都在使用访问权限。这里有太多要讨论的内容,但是如果您曾经在类似的环境中工作,您就会知道。


2
#4-还有一个仅用于<ahref=' stackoverflow.com/ questions/327199/… >:)的线程。
dkretz

4
访问不是DBMS。这是一个RAD环境,其中包含一个非常简单的数据库管理器。SQL Server,Oracle等。除非添加类似VB的语言和类似Crystal Reports的工具,否则它将永远不会替代它。
乔·皮内达

26

使用SP作为存储过程名称的前缀,因为它将首先在系统过程位置而不是自定义位置中搜索。


1
也可以扩展为对所有存储过程使用任何其他公共前缀,这使得选择排序列表更加困难。
dkretz

7
+1 for doofledorfer评论!!我已经看过很多了,我发现这很愚蠢,确实确实使搜索特定的SP 非常困难!!!还扩展为视图的“ vw_”,表等的“ tbl_”,我多么讨厌它们!
乔·皮内达

1
如果要将对象脚本化为文件(例如,用于源代码控制,部署或迁移),则前缀可能很有用
Rick

1
为什么在地球上给每个存储过程加上sp usp 前缀会很有用?这只会使您难以扫描列表中所需的列表。
Ryan Lundy

25

过度使用临时表和游标。


2
有充分的证据表明“我所知道的只是程序语言”。
dkretz

2
根据定义,任何事情的过度使用都是不希望的。一个不需要在哪里使用临时表/游标的示例将很有帮助。
杰斯·瑞亚

6
通常,我看到临时表使用不足。使用SQL Server,通常可以通过使用一堆临时表而不是一个整体查询来获得性能提升。
Cervo 2010年

24

为了存储时间值,仅应使用UTC时区。不应使用当地时间。


3
对于过去必须考虑夏令时的UTC转换为本地时间的方法,我仍然没有找到一个很好的简单解决方案,因为跨年和国家/地区的更改日期各不相同,以及国家/地区内的所有例外情况。因此,UTC不会使您摆脱转换的复杂性。但是,重要的是要有一种方法来知道每个存储的日期时间的时区。
ckarras

1
@CsongorHalmai许多地方都实行夏令时,因此时移一小时内的时间值可能会模棱两可。
Frank Schwieterman

对于现在和过去,这当然是正确的,但是对于未来,尤其是在相当遥远的未来,明确的时区通常是必要的。如果您有一份30年的选择权,该选择权仅在纽约时间2049-09-27T17:00:00到期,那么您就不能盲目地将其设为21:00:00Z。美国国会很可能会更改DST规则。您必须将当地时间和真实时区(America / New_York)分开。
约翰·科万(John Cowan)

23

使用@@ IDENTITY而不是SCOPE_IDENTITY()

引用此答案

  • @@ IDENTITY返回在所有范围内为当前会话中的任何表生成的最后一个标识值。您需要小心,因为它是跨作用域的。您可以从触发器而不是当前语句中获取值。
  • SCOPE_IDENTITY返回为当前会话和当前范围中的任何表生成的最后一个标识值。通常您要使用什么。
  • IDENT_CURRENT返回在任何会话和任何作用域中为特定表生成的最后一个标识值。这可以让您指定要从哪个表中获取值,以防上述两个表不是您真正需要的表(非常罕见)。如果要获取未在其中插入记录的表的当前IDENTITY值,可以使用此方法。

+1非常正确,可能会导致难以清除的错误
-Axarydax

23

将“死”字段重新用于非预期的内容(例如,将用户数据存储在“传真”字段中)-极具诱惑力的快速修复方法!


21
select some_column, ...
from some_table
group by some_column

并假设结果将按some_column排序。我已经在假设成立的Sybase身上看到了这一点。


1
为EVER假设排序排序,只是因为那是它一次出现在查询工具中的方式
Joel Coehoorn

3
我什至不只一次看到这个错误报告。
dkretz

6
在MySQL中,已记录排序。< dev.mysql.com/doc/refman/5.0/en/select.html >。因此,再次责怪MySQL。
derobert

1
在Oracle中,未排序的结果(几乎)始终与分组匹配-直到版本10G。对于以前省去ORDER BY的开发人员,需要大量的返工!
托尼·安德鲁斯

1
我什至在培训课程中,这被认为是SQL Server的事实。我不得不大声抗议。仅保存20个字符就是您依赖模糊或不明文的行为。
erikkallen

20
SELECT FirstName + ' ' + LastName as "Full Name", case UserRole when 2 then "Admin" when 1 then "Moderator" else "User" end as "User's Role", case SignedIn when 0 then "Logged in" else "Logged out" end as "User signed in?", Convert(varchar(100), LastSignOn, 101) as "Last Sign On", DateDiff('d', LastSignOn, getDate()) as "Days since last sign on", AddrLine1 + ' ' + AddrLine2 + ' ' + AddrLine3 + ' ' + City + ', ' + State + ' ' + Zip as "Address", 'XXX-XX-' + Substring(Convert(varchar(9), SSN), 6, 4) as "Social Security #" FROM Users

或者,将所有内容塞入一行。


使用前一个注释的查询,只是因为那是我可用的第一个SQL语句。
贾斯珀·贝克斯

17
  • FROM TableA, TableB WHEREJOINS 的语法,而不是FROM TableA INNER JOIN TableB ON

  • 假设查询将被返回将以某种方式排序,而无需放入ORDER BY子句,这仅仅是因为它是在查询工具中进行测试时所显示的方式。


5
我的Oracle DBA总是抱怨我使用“ ANSI连接”,即您以正确的方式显示的内容。但是我一直在这样做,而且我怀疑他们内心深处对它的了解更好。
史蒂夫·麦克劳德

1
我怀疑甲骨文希望标准SQL会消失。:-)另外,您不能在MySQL 5中混合使用隐式和显式JOINS(又名ANSI JOIN)-它不起作用。这是显式JION的另一个论点。
staticsan

3
我会说,即使A INNER JOIN B ON也是一种反模式。我更喜欢使用A INNER JOINB。
约翰尼尔森2009年

Oracle现在支持ANSI语法,但是过去他们曾经对外部联接使用这种非常奇怪的语法,并且仍然有太多人在使用它。
Cervo 2010年

好吧... Oracle仍然不允许您将ANSI联接用于快速可刷新的,提交的物化视图
Gerrat,

14

在其职业生涯的前六个月学习SQL,在接下来的十年中再也没有学习过其他任何知识。特别是不学习或没有有效使用开窗/分析SQL功能。特别是over()和partition by的使用。

窗口函数与聚合函数一样,对一组已定义的行(一组)执行聚合,但是窗口函数可以为每个组返回多个值,而不是每组返回一个值。

有关窗口函数的详细概述,请参见《O'Reilly SQL Cookbook附录A》。


12

我需要在此处放入我自己当前的收藏夹,以使列表完整。我最喜欢的反模式不是测试您的查询

在以下情况下适用:

  1. 您的查询涉及多个表。
  2. 您认为您有一个针对查询的最佳设计,但不必费心去检验您的假设。
  3. 您接受第一个有效的查询,不知道它是否接近优化。

并且针对非典型或不足数据进行的任何测试均不计算在内。如果是存储过程,则将测试语句放入注释中,并将其与结果一起保存。否则,将其放入带有结果的代码注释中。


最小化T-SQL测试的一种非常有用的技术:在.SQL文件中定义SP,UDF等,之后立即创建一个块测试,例如IF 1 = 2 BEGIN(代码示例,带有预期结果)结束语)
乔·派

即使从未执行过,SQL Server也会解析测试块中的代码。因此,当您的对象被修改并接收更多参数或不同类型等,或者其依赖的对象被修改时,仅通过执行执行计划,您就会收到错误!
乔·皮内达

并非总是可以用真实数据进行测试。开发服务器/“测试”服务器通常报酬低廉,仅占实时服务器的一小部分。通常,对实时服务器不进行测试。有些地方比较好,并具有带有实时数据的测试或登台服务器。
Cervo 2010年

11

临时表滥用。

具体来说就是这样的事情:

SELECT personid, firstname, lastname, age
INTO #tmpPeople
FROM People
WHERE lastname like 's%'

DELETE FROM #tmpPeople
WHERE firstname = 'John'

DELETE FROM #tmpPeople
WHERE firstname = 'Jon'

DELETE FROM #tmpPeople
WHERE age > 35

UPDATE People
SET firstname = 'Fred'
WHERE personid IN (SELECT personid from #tmpPeople)

不要从查询中构建临时表,而只是删除不需要的行。

是的,我已经在生产数据库中看到了这种形式的代码页。


1
+1,我同意。虽然,我发现至少有一种或两种情况可以改善这项技术的性能-至少可以说,所涉及的查询很复杂。
2010年

1
没错-他们确实有位置,只是在每个查询中都没有:)
geofftnz 2010年

1
如果条件非常复杂,有时您必须这样做。没错,它可能被滥用到极致。但是很多时候,简单的删除要比在初始查询中获取大小写的逻辑简单得多。同样,有时如果该子句不可更改,则初始查询将变慢。但是,仅在较小的临时表上执行此操作效率更高。在其他情况下,您会继续添加事例,而事后商人会继续添加。
Cervo 2010年

9

相反的观点:过度迷恋标准化。

大多数SQL / RBDBs系统提供了很多功能(事务,复制),这些功能即使对非规范化数据也非常有用。磁盘空间便宜,与编写1NF模式并处理其中的所有麻烦(复杂的连接,讨厌的子选择)相比,操作/过滤/搜索获取的数据有时更简单(更轻松的代码,更快的开发时间)。等)。

我发现过度标准化的系统通常是过早的优化,尤其是在早期开发阶段。

(对此有更多的想法... http://writeonly.wordpress.com/2008/12/05/simple-object-db-using-json-and-python-sqlite/


22
我认为非规范化通常是过早的优化。
tuinstoel

有时是,有时不是。幸运的是,它通常很容易测试,并且不同的选项可以满足不同的数据库需求。
Gregg Lind)

17
标准化不仅是为了节省磁盘空间。它还是为数据创建权威来源。如果数据仅存储在一个位置,则一致性不是经过仔细编码的副产品,而是设计的副产品。
格兰特·约翰逊

以JSON格式存储复合数据是一回事:对它的支持越来越多,这是有意识的折衷。使用逗号分隔的值(或其他值)来尝试保存一个联接是明智的和愚蠢的。
约翰·考恩

通过消除多表查找,noSQL解决方案以重复数据为代价显示了性能优势。把整个规范化的事情放在首位。在某些示例中,数据在多个位置收集,以确保一个过程具有最快的响应时间。当然,有关权威资源的问题也会发挥作用。
barrypicker

9

我只是基于SO上的一些SQL响应将这一部分放在一起。

认为触发器是针对数据库的,就像事件处理程序是针对OOP的那样,是一种严重的反模式。有人认为,只有任何旧的逻辑都可以放入触发器中,以便在表上发生事务(事件)时将其触发。

不对。最大的区别之一是触发器是同步的-具有复仇性,因为它们在设置操作上而不是在行操作上是同步的。在OOP方面,正好相反-事件是实现异步事务的有效方法。



7

1)我不知道这是一个“官方的”反模式,但我不喜欢并尝试避免将字符串文字作为数据库列中的魔术值使用。

MediaWiki表“ image”的一个示例:

img_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", 
    "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE") default NULL,
img_major_mime ENUM("unknown", "application", "audio", "image", "text", 
    "video", "message", "model", "multipart") NOT NULL default "unknown",

(我只是注意到不同的大小写,要避免的另一件事)

我设计这样的情况,例如使用int主键对表ImageMediaType和ImageMajorMime进行int查找。

2)取决于特定NLS设置的日期/字符串转换

CONVERT(NVARCHAR, GETDATE())

没有格式标识符


也没有语法缩进。啊
dkretz

2
为什么这样不好?当然,如果您要表达一组值,那么它和查找表一样有效,并且更适合调用它的代码。我宁愿在我的应用程序代码中有一个枚举映射到我的数据库中的枚举约束,而不是我的应用程序代码中的一个枚举映射到查找表的特定行。感觉更干净。
杰克·瑞安

@JackRyan:这很糟糕,因为稍后您更改枚举列表时,需要记住现在在两个位置进行更改。它违反了DRY。数据库应该是事实的唯一来源。
Gerrat

7

查询中的相同子查询。


10
不幸的是,有时您只是无法避免-在SQL 2000中没有“ WITH”关键字,并且使用UDF封装通用子查询有时会导致性能下降,这归咎于MS ...
Joe Pineda

好吧,希望他们能在这些日子之一中添加它。
EvilTeach

在SQL 2000中,可以使用表变量。
递归

@递归:您不能在表变量上有索引,这通常会使它比子查询慢。但是,您可以使用带有自定义索引的临时表。
Rick

很酷,已经使用SQL多年了,甚至不知道Common Table Expressions存在(尽管我会需要它们)。现在我知道了!谢谢!
sleske

7
  • 更改视图-更改频率太高而没有通知或理由的视图。更改将在最不合适的时间被注意到,或者更糟的是错误并且永远不会被注意到。也许您的应用程序会中断,因为有人认为该列的名称更好。通常,视图应扩展基表的用途,同时保持与消费者的合同。解决问题,但不要添加功能或更糟的更改行为,因为那样会创建新视图。为了减轻不与其他项目共享视图的可能性,请在平台允许时使用CTE。如果您的商店有DBA,则您可能无法更改视图,但是在这种情况下,所有视图都将过时或无用。

  • !Paramed-查询是否可以有多个目的?可能是这样,但是下一个阅读它的人要等到深沉的冥想才能知道。即使您现在不需要它们,即使调试只是“公正”的,您也有机会。添加参数可以减少维护时间并使设备保持干燥。如果有where子句,则应该有参数。

  • 没有案例的情况-

    SELECT  
    CASE @problem  
      WHEN 'Need to replace column A with this medium to large collection of strings hanging out in my code.'  
        THEN 'Create a table for lookup and add to your from clause.'  
      WHEN 'Scrubbing values in the result set based on some business rules.'  
        THEN 'Fix the data in the database'  
      WHEN 'Formating dates or numbers.'   
        THEN 'Apply formating in the presentation layer.'  
      WHEN 'Createing a cross tab'  
        THEN 'Good, but in reporting you should probably be using cross tab, matrix or pivot templates'   
    ELSE 'You probably found another case for no CASE but now I have to edit my code instead of enriching the data...' END  

爱那个第三个人。我已经在本地使用它了……
alphadogg


5

我发现最多的两个,在性能方面可能会产生重大损失的是:

  • 使用游标而不是基于集合的表达式。我猜想这种情况经常在程序员有步骤地思考时发生。

  • 当关联到派生表时,可以使用相关子查询来完成这项工作。


我同意你的意思。尽管相关子查询是派生表IIRC的一种。
dkretz

1
派生表是set操作,而相关查询在外部查询中的每一行运行,从而使其效率较低(10
Mitch Wheat

几年前,令我惊讶的是,SQL S.在某种程度上针对关联查询进行了优化:对于简单的查询,您将获得与使用JOIN在逻辑上等效的查询相同的执行计划!而且,使Oracle屈服的相关查询在SQL S上的运行速度很慢。
乔·皮内达

这就是为什么我总是同时进行两种测试。我通常会同时尝试两种方法。实际上,无论如何对于SQL Server,我通常都发现相关的sq不会变慢。
dkretz

3
请理解,关联的子查询和联接是相同的(在大多数情况下)。它们甚至不是彼此优化的不同事物,而只是同一操作的不同文本表示形式。
erikkallen

5

将内容放入临时表中,尤其是从SQL Server切换到Oracle的人,有过度使用临时表的习惯。只需使用嵌套的select语句即可。


5

编写查询而对如何使SQL应用程序(无论是单个查询还是多用户系统)的速度或速度不怎么了解的开发人员。这包括对以下方面的无知:

  • 物理I / O最小化策略,因为大多数查询的瓶颈是I / O而不是CPU
  • 不同种类的物理存储访问的性能影响(例如,大量的顺序I / O会比许多小型的随机I / O更快,但是如果您的物理存储是SSD,则速度会更快!)
  • 如果DBMS产生不良的查询计划,如何手动调整查询
  • 如何诊断数据库性能不佳,如何“调试”慢速查询以及如何读取查询计划(或根据所选择的DBMS进行解释)
  • 锁定策略可优化吞吐量并避免多用户应用程序中的死锁
  • 批处理和其他技巧处理数据集的重要性
  • 表和索引设计,以最佳平衡空间和性能(例如,覆盖索引,在可能的情况下保持索引较小,将数据类型减少到所需的最小大小等)

3

使用SQL作为美化的ISAM(索引顺序访问方法)包。特别是,嵌套游标而不是将SQL语句合并为单个(尽管较大)语句。这也算是“滥用优化器”,因为实际上优化器无能为力。可以将其与未准备好的语句结合使用以最大程度地降低效率:

DECLARE c1 CURSOR FOR SELECT Col1, Col2, Col3 FROM Table1

FOREACH c1 INTO a.col1, a.col2, a.col3
    DECLARE c2 CURSOR FOR
        SELECT Item1, Item2, Item3
            FROM Table2
            WHERE Table2.Item1 = a.col2
    FOREACH c2 INTO b.item1, b.item2, b.item3
        ...process data from records a and b...
    END FOREACH
END FOREACH

正确的解决方案(几乎总是)是将两个SELECT语句组合为一个:

DECLARE c1 CURSOR FOR
    SELECT Col1, Col2, Col3, Item1, Item2, Item3
        FROM Table1, Table2
        WHERE Table2.Item1 = Table1.Col2
        -- ORDER BY Table1.Col1, Table2.Item1

FOREACH c1 INTO a.col1, a.col2, a.col3, b.item1, b.item2, b.item3
    ...process data from records a and b...
END FOREACH

双循环版本的唯一优点是,由于内部循环结束,因此您可以轻松地在Table1中发现值之间的中断。这可能是控制中断报告中的一个因素。

同样,在应用程序中排序通常是禁止的。


根据我的经验,这种风格虽然不是这种语法,但在PHP中尤其猖ramp。
dkretz

语法实际上是IBM Informix-4GL-但很明显,不需要太多的解释方式(我认为)。而且在许多SQL程序中,样式都很普遍-不管使用哪种编程语言。
乔纳森·莱夫勒

除了您使用众所周知的反模式(隐式联接)来说明您的反模式的事实外,还有其他缺点。
约翰

当然,所有游标的使用都是SQl反模式。几乎所有游标都可以重写为基于集合的操作。只有少数具有多年经验并且了解datbase内部工作原理的DBA才可以编写这种代码。任何应用程序开发人员都无需编写SQL游标。
HLGEM 2015年

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.