我们所有使用关系数据库的人都知道(或正在学习)SQL是不同的。获得期望的结果,并有效地这样做,涉及到一个乏味的过程,其部分特征是学习不熟悉的范例,并发现一些我们最熟悉的编程模式在这里不起作用。常见的反模式是什么?
当前回答
使用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
双循环版本的唯一优点是,您可以很容易地发现表1中值之间的中断,因为内部循环结束了。这可能是控制中断报告中的一个因素。
此外,应用程序中的排序通常是不允许的。
其他回答
对于存储时间值,应该只使用UTC时区。不应使用当地时间。
使用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
双循环版本的唯一优点是,您可以很容易地发现表1中值之间的中断,因为内部循环结束了。这可能是控制中断报告中的一个因素。
此外,应用程序中的排序通常是不允许的。
我发现,在性能方面,有两点是最重要的,并且可能会有很大的成本:
使用游标而不是基于集合 表达式。我想当程序员以过程的方式思考时,这种情况经常发生。 使用相关子查询,当a 连接到派生表可以执行 的工作。
临时表滥用。
特别是这类事情:
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)
不要从查询中构建临时表,只是为了删除不需要的行。
是的,我在生产db中看到过这种形式的代码页。
以下是我的前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文本,只生成一个执行计划并保存在缓存中。无论参数的值是多少,都将使用该计划。这会导致间歇性的性能不佳。最好编写两个查询(每个预期的执行计划一个查询)。
推荐文章
- 选项(RECOMPILE)总是更快;为什么?
- 设置数据库从单用户模式到多用户
- oracle中的RANK()和DENSE_RANK()函数有什么区别?
- 的类型不能用作索引中的键列
- SQL逻辑运算符优先级:And和Or
- 如何检查一个表是否存在于给定的模式中
- 添加一个复合主键
- 如何在SQL Server Management Studio中查看查询历史
- 可以为公共表表达式创建嵌套WITH子句吗?
- 什么时候我需要在Oracle SQL中使用分号vs斜杠?
- SQL Server的NOW()?
- 在SQL中,count(列)和count(*)之间的区别是什么?
- 在SQL Server中截断(不是四舍五入)小数位
- 仅在Datetime列上按日期分组
- PostgreSQL通配符LIKE用于单词列表中的任何一个