你不会写一个有200行的函数应用程序,你会将那些长函数分解为更小的函数,每个函数都有一个明确定义的单一职责。
为什么不像这样编写SQL呢?
像分解函数一样分解查询语句, 这使它们更短、更简单、更容易理解、更容易测试和重构。并且允许你在它们之间添加"垫片"和"包装器",就像在过程性代码中所做的那样。
如何实现这一点?通过将每个重要的查询操作变成视图。然后你可以像组合原始函数一样,从这些简单的视图组合出更复杂的查询。
最好的事情是,对于大多数视图组合,您将从您的RDBMS获得完全相同的性能。(对于一些视图组合,您可能不会得到相同的性能;那又怎样?过早优化是万恶之源。首先正确编码,如果需要再进行优化。)
这里有一个使用几个视图分解复杂查询的示例。
在这个例子中,因为每个视图只添加了一种转换,所以可以独立测试每个视图以查找错误,并且测试非常简单。
这是示例中的基本表:
create table month_value(
eid int not null, month int, year int, value int );
这张表格存在缺陷,因为它使用了两列——月份和年份——来代表一个数据,即一个绝对月份。以下是我们针对新的计算列的规定:
我们将进行线性转换,使其与 (年份, 月份) 相同,并且对于任何 (年份, 月份) 元组只有一个值,所有值都是连续的:
create view cm_absolute_month as
select *, year * 12 + month as absolute_month from month_value;
现在我们需要测试的内容与我们的规范有关,即对于任何元组(年份、月份),都有一个且仅有一个(absolute_month),而且(absolute_month)是连续的。让我们编写一些测试。
我们的测试将是一个 SQL select
查询,具有以下结构:测试名称和一个拼接在一起的 case 语句。测试名称只是一个任意的字符串。case 语句只是一个 case when test statements then 'passed' else 'failed' end
的条件语句。
测试语句将只是必须为测试通过而为真的 SQL select(子查询)。
这是我们的第一个测试:
select concat(
'For every (year, month) there is one and only one (absolute_month): ',
case when
( select count(distinct year, month) from month_value)
= ( select count(distinct absolute_month) from cm_absolute_month)
then 'passed' else 'failed' end
);
运行该查询会产生以下结果:对于每个(年份,月份),都存在一个且仅存在一个(绝对月份):通过
只要month_value中有足够的测试数据,此测试就可以工作。
我们也可以为足够的测试数据添加一个测试:
select concat( 'Sufficient and sufficiently varied month_value test data: ',
case when
( select count(distinct year, month) from month_value) > 10
and ( select count(distinct year) from month_value) > 3
and ... more tests
then 'passed' else 'failed' end );
现在让我们测试它的连续性:
select concat( '(absolute_month)s are consecutive: ',
case when ( select count(*) from cm_absolute_month a join cm_absolute_month b
on ( (a.month + 1 = b.month and a.year = b.year)
or (a.month = 12 and b.month = 1 and a.year + 1 = b.year) )
where a.absolute_month + 1 <> b.absolute_month ) = 0
then 'passed' else 'failed' end );
现在让我们将测试(仅为查询)放入一个文件中,并对该脚本运行数据库。如果我们将视图定义存储在脚本(或多个脚本,我建议每个相关视图一个文件)中以针对数据库运行,则可以将每个视图的测试添加到同一脚本中,从而重新创建视图时同时运行视图的测试。这样,当我们重新创建视图时,就可以得到回归测试,而当视图创建针对生产运行时,该视图也将在生产中进行测试。