添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

将光标置于此图标上以加载和查看本教程的所有屏幕截图。(警告:此操作会同时加载所有屏幕截图,所以网速较慢时,响应时间可能会比较长。)

注意: 此外,您可以将光标放在下列步骤的单个图标上,以便仅加载和查看与该步骤相关的屏幕截图。您可以通过单击单个屏幕截图将其隐藏。

利用 SQL MODEL 子句,您可以根据查询结果定义多维数组,然后将规则应用于该数组以计算新值。这些规则可以是复杂的相互依赖的计算。与外部解决方案相比,通过将高级计算集成到数据库中,可以大幅度提升性能、可伸缩性以及可管理性。用户可以将数据保留在 Oracle 环境内,而无需将数据复制到单独的应用程序或 PC 电子表格中。

MODEL 子句通过将查询列映射到以下三组来定义多维数组:分区列、维列和度量列。这些元素执行以下任务:

要针对这些多维数组创建规则,您需要定义以维值形式表达的计算规则。规则灵活且简洁,并且可以使用通配符和 FOR 循环,以最大限度地表达您的意图。利用 MODEL 子句构建的计算通过将分析集成到数据库中改善了传统的电子表格计算,通过符号引用提高了可读性,并提供了可伸缩性和更好的可管理性。

下图使用假设的销售表格从概念的角度概述了该模型的特征。该表格具有四列:国家/地区、产品、年份和销售量。该图分为三个部分。上段阐释了将表格划分为分区、维和度量三列的概念。中段给出了两个假想规则,以预测 Prod1 和 Prod2 的销售,因为产品销售的计算值来自前两年。最后,第三部分显示了将规则应用于这个包含假设数据的表格后得出的查询输出。黑色输出是从数据库检索的数据,而蓝色输出表示根据规则计算出的行。请注意,这些规则是在每个分区内应用的。

映射到分区、维和度量的列

COUNTRY PRODUCT SALES
sales('prod1', 2002) = sales('prod1', 2000) + sales('prod1', 2001)
sales('prod2', 2002) = sales('prod2', 2000) + sales('prod2', 2001)

MODEL 子句的输出:

COUNTRY PRODUCT SALES prod1 prod1 prod2 prod2 prod1 prod1 prod2 prod2 prod1 prod2 prod1 prod2

请注意,MODEL 子句没有更新表格中的现有数据,也没有向表格中插入新数据 — 要更改表格中的值,必须将模型结果提供给 INSERT、UPDATE 或 MERGE 语句。

返回主题列表

通过使用 MODEL 子句,您可以将电子表格计算引入数据库。 您将使用 Sales History(销售历史)模式数据并通过包含新 MODEL 子句的 SELECT 语句来执行类似电子表格的计算。您需要标识规则,以找出诸如销售预测之类的信息。

返回主题列表

设置示例数据

使用 SH 模式创建视图。该视图将按国家/地区提供产品销售的年度总计(总款和总量),并跨所有渠道进行聚集。

首先,确保环境简洁。从终端窗口中,执行以下命:

cd /home/oracle/wkdir
sqlplus sh/sh
@cleanup.sql

cleanup.sql 脚本包含以下内容:

DROP VIEW sales_view;
DROP TABLE dollar_conv;
DROP TABLE growth_rate;
DROP TABLE ledger;

现在,您可以创建 SALES_VIEW 视图。从 SQL*Plus 会话中,执行以下脚本:

@sample_data.sql

sample_data.sql 脚本包含以下内容:

CREATE VIEW sales_view AS
SELECT country_name country, prod_name prod, calendar_year year,
SUM(amount_sold) sale, COUNT(amount_sold) cnt
FROM sales, times, customers, countries, products
WHERE sales.time_id = times.time_id AND
sales.prod_id = products.prod_id 
AND sales.cust_id = customers.cust_id
AND customers.country_id = countries.country_id
GROUP BY country_name, prod_name, calendar_year

作为模型的初始示例,请考虑以下语句:

SELECT SUBSTR(country,1,20) country, 
SUBSTR(prod,1,15) prod, year, sales
FROM   sales_view
WHERE country IN ('Italy','Japan')
MODEL RETURN UPDATED ROWS 
PARTITION BY (country) 
DIMENSION BY (prod, year)
MEASURES (sale sales) 
RULES (
sales['Bounce', 2002] = sales['Bounce', 2001] + sales['Bounce', 2000],
sales['Y Box', 2002] = sales['Y Box', 2001],
sales['2_Products', 2002] = sales['Bounce', 2002] + sales['Y Box', 2002])
ORDER BY country, prod, year;
COUNTRY              PROD                  YEAR      SALES
-------------------- --------------- ---------- ----------
Italy                2_Products            2002   90387.54
Italy                Bounce                2002    9179.99
Italy                Y Box                 2002   81207.55
Japan                2_Products            2002  101071.96
Japan                Bounce                2002   11437.13
Japan                Y Box                 2002   89634.83 

因为该语句按照国家/地区划分,所以这些规则一次应用于一个国家/地区的数据。请注意,数据结束于 2001 年,因此为 2002 年或之后年份定义值的任何规则都将插入新单元格。第一个规则将 2002 年 Bounce 的销售定义为 2000 年和 2001 年的销售总和。第二个规则将 2002 年 Y Box 的销售定义为 2001 年的销售值。第三个规则定义了一个名为 2_Products 的类别,它是 2002 年的 Bounce 与 Y Box 值相加所得的总和。请注意,2_Products 的值派生自前两个规则的结果,因此这两个规则必须在 2_Products 规则之前执行。

请注意,MODEL 关键字后面的 RETURN UPDATED ROWS 子句将结果限制为在该查询中创建或更新的那些行。使用该子句是使结果集只包含新计算的值的简便方法。在整个示例中,都将用到 RETURN UPDATED ROWS 子句。 示例中显示在规则开头处的 RULES 关键字是可选的,但是建议您使用以方便阅读。

要查看产品 Bounce 于 2000 年在意大利的 SALES 值,并将其设为 10,可使用“位置单元格引用”。单元格引用的值将根据其在表达式中的位置与相应的维匹配。模型的 DIMENSION BY 子句决定指定给每个维 — 在本例中,第一个位置是产品 (PROD),第二个位置是 YEAR。从 SQL*Plus 会话中,执行以下脚本:

@pos_cell1.sql

pos_cell1.sql 脚本包含以下内容:

COLUMN country FORMAT a20
COLUMN prod FORMAT a20
SELECT SUBSTR(country,1,20) country, 
SUBSTR(prod,1,15) prod, year, sales
FROM   sales_view
WHERE country='Italy'
MODEL RETURN UPDATED ROWS 
PARTITION BY (country) 
DIMENSION BY (prod, year)
MEASURES (sale sales) 
RULES (
sales['Bounce', 2000] = 10 )
ORDER BY country, prod, year

要创建产品 Bounce 于 2005 年在意大利的 SALES 预测值,并将其设为 20,可使用 SELECT 语句中的规则将年份值设为 2005,从而在数组中创建新单元格。从 SQL*Plus 会话中,执行以下脚本:

@pos_cell2.sql

pos_cell2.sql 脚本包含以下内容:

SELECT SUBSTR(country,1,20) country, 
SUBSTR(prod,1,15) prod, year, sales
FROM   sales_view
WHERE country='Italy'
MODEL RETURN UPDATED ROWS 
PARTITION BY (country) 
DIMENSION BY (prod, year)
MEASURES (sale sales) 
RULES (
sales['Bounce', 2005] = 20 )
ORDER BY country, prod, year
/

注意:如果您希望创建新单元格(例如,未来几年的值),则必须使用位置引用或 FOR 循环(本教程稍后讨论)。也就是说,位置引用允许更新数组以及向数组中插入新值。这称为 UPSERT 过程,它由 Oracle SQL MERGE 语句处理。

要更新产品 Bounce 自 1999 年以来针对意大利记录的所有年份的 SALES 值,并将它们设为 10,可以使用“符号单元格引用”。单元格引用的值通过布尔条与相应的维匹配。您可以使用所有常见的运算符,例如 、IN 和 BETWEEN。在本例中,查询将查找等于 Bounce 的产品值和所有大于 1999 的年份值。这展示了单一规则如何访问多个单元格。从 SQL*Plus 会话中,执行以下脚本:

@sym_cell1.sql

sym_cell1.sql 脚本包含以下内容:

SELECT SUBSTR(country,1,20) country, 
SUBSTR(prod,1,15) prod, year, sales
FROM   sales_view
WHERE country='Italy'
MODEL RETURN UPDATED ROWS 
PARTITION BY (country) 
DIMENSION BY (prod, year)
MEASURES (sale sales) 
RULES (
sales[prod='Bounce', year>1999] = 10 )
ORDER BY country, prod, year
/

注意:符号引用功能强大,但它们只能用于更新现有的单元格:它们不能创建新单元格,例如,未来几年的销售规划。

您希望通过单一查询来更新多个国家/地区的多种产品在数年中的销售,并且还希望插入新的单元格。通过将数个规则置于一个查询中,处理会更加高效,因为这减少了需要访问数据的次数。它还允许使用更为简洁的 SQL,以使开发人员的工作效率更高。从 SQL*Plus 会话中,执行以下脚本:

@pos_sym.sql

pos_sym.sql 脚本包含以下内容:

SELECT SUBSTR(country,1,20) country, 
SUBSTR(prod,1,15) prod, year, sales
FROM sales_view WHERE country IN ('Italy','Japan') 
MODEL RETURN UPDATED ROWS 
PARTITION BY (country) 
DIMENSION BY (prod, year)
MEASURES (sale sales) 
RULES (
sales['Bounce', 2002] = sales['Bounce', year = 2001] , 
--positional notation:can insert new cell
sales['Y Box', year>2000] = sales['Y Box', 1999], 
--symbolic notation:can update existing cell
sales['2_Products', 2005] = 
sales['Bounce', 2001] + sales['Y Box', 2000] )
--positional notation:permits insert of new cells 
--for new product
ORDER BY country, prod, year
/

该示例数据没有超出 2001 年的值,因此所有涉及到 2002 年或之后的规则都要求插入新的单元格。对于此处定义的任何新产品名也是如此。在第三条规则中,2_Products 被定义为 2005 年的销售是 2001 年 Bounce 销售与 2000 年 Y Box 销售总和的产品。

对于 2002 年的 Bounce,第一个规则将插入新的单元格,因为这是位置表示法。对于 Y Box,第二个规则使用符号表示法,但是此处已经有了 2001 年的 Y Box 值,因此它将更新这些值。对于 2005 年的 2_Products,第三个规则是位置表示法,因此它可以插入新的单元格,您将在输出中看到这些新单元格。

您希望预测 2005 年 Bounce 在意大利的销售比其在 1999 至 2001 年间的最大销售多 100。为此,您需要在规则右侧使用 BETWEEN 子句来指定多个单元格,并通过 MAX() 函数将其聚合为单一值。从 SQL*Plus 会话中,执行以下脚本:

@multi_c.sql

multi_c.sql 脚本包含以下内容:

SELECT SUBSTR(country,1,20) country, 
SUBSTR(prod,1,15) prod, year, sales
FROM   sales_view
WHERE country='Italy'
MODEL RETURN UPDATED ROWS 
PARTITION BY (country) 
DIMENSION BY (prod, year)
MEASURES (sale sales) 
RULES (
sales['Bounce', 2005] = 
100 + max(sales)['Bounce', year BETWEEN 1998 AND 2002] )
ORDER BY country, prod, year

请注意,聚合函数只出现在规则的右侧。聚合函数的参数可以是常量、约束变量、MODEL 子句的度量或者涉及这三种参数的表达式。

返回主题列表

CV() 函数是一个非常强大的工具,它可以高效地进行规则创建。CV() 用于规则的右侧,以复制左侧指定的当前维值。对于左侧规范引用多个单元格来说,它非常有用。用关系数据库的概念来理解,该函数类似于连接操作。

CV() 允许使用非常灵活的表达式。例如,通过从 CV(year) 值进行减法运算,可以引用数据集中的其他行。如果在单元格引用中使用表达式“CV(year) -2”,则可以访问两年前的数据。CV() 函数通常作为单元格引用的一部分使用,但是也可以在单元格引用外部用作独立的表达式元素。

您希望更新 Bounce 在意大利多年的销售值,使用的规则是 Bounce 每年的销售是 Y Box 当年销售的 20% 与 Mouse Pad 当年销售的总和。从 SQL*Plus 会话中,执行以下脚本:

@cvf1.sql

cvf1.sql 脚本包含以下内容:

SELECT SUBSTR(country,1,20) country, 
SUBSTR(prod,1,15) prod, year, sales
FROM   sales_view
WHERE country='Italy'
MODEL RETURN UPDATED ROWS 
PARTITION BY (country) 
DIMENSION BY (prod, year)
MEASURES (sale sales) 
RULES (
sales['Bounce', year BETWEEN 1995 AND 2002] =
sales['Mouse Pad', cv(year)] + 
0.2 * sales['Y Box', cv(year)])
ORDER BY country, prod, year

请注意,在上述结果中,尽管接受了 1995–2002 年间的所有年份,您也只会看到 1999–2001 年的值。这是为该表格只有这几年的数据。CV() 函数将提供左侧当前引用的单元格的 DIMENSION BY 关键字当前值。当上述规则的左侧引用单元格 Bounce 和 1999 时,右侧表达式将如下所示:

sales['Mouse Pad', 1999] + 0.2 * sales['Y Box', 1999]

同样,当左侧引用单元格 Bounce 和 2000 时,右侧表达式将为:

sales['Mouse Pad', 2000] + 0.2 * sales['Y Box', 2000]

CV() 函数将维关键字作为其参数。还可以使用不带任何参数的 CV()(如 cv()),在这种情况下,暗示了位置引用。以上规则还可以写为:

s['Bounce', year BETWEEN 1995 AND 2002] =
s['Mouse Pad', cv()] + 0.2 * s['Y Box', cv()]

CV() 函数只能在右侧单元格引用中使用。

您希望计算出产品 Y Box、Bounce 和 Mouse Pad 在意大利的销售年增长率。从 SQL*Plus 会话中,执行以下脚本:

@cvf2.sql

cvf2.sql 脚本包含以下内容:

SELECT SUBSTR(country,1,20) country, 
SUBSTR(prod,1,15) prod, year, sales, growth
FROM   sales_view
WHERE country='Italy'
MODEL RETURN UPDATED ROWS 
PARTITION BY (country) 
DIMENSION BY (prod, year)
MEASURES (sale sales, 0 growth)
RULES (
growth[prod in ('Bounce','Y Box','Mouse Pad'), year between 1998 and 2001] =
100* (sales[cv(prod), cv(year)] - 
sales[cv(prod), cv(year) -1] ) / 
sales[cv(prod), cv(year) -1] )
ORDER BY country, prod, year

请注意,结果中的空白单元格都是 NULL。如果没有前两年的产品值,那么规则将生成 NULL。由于没有一种产品有 1998 年的值,因此在任何情况下,1999 年的增长计算均为 NULL。

通配符操作符对于指定单元格非常有用,为此,您可以使用 ANY 关键字。您可以将其用于之前的示例,以替换 1998 至 2001 年之间任何指定的年份,如下所示。

ANY 可以用在单元格引用中,以包含所有维值(包括 NULL)。在符号引用表示法中,请使用短语 IS ANY。请注意,ANY 通配符在与位置或符号表示法一起使用时会阻止单元格插入。

从 SQL*Plus 会话中,执行以下脚本:

@any.sql

any.sql 脚本包含以下内容:

SELECT SUBSTR(country,1,20) country, 
SUBSTR(prod,1,15) prod, year, sales, growth
FROM   sales_view
WHERE country='Italy'
MODEL RETURN UPDATED ROWS 
PARTITION BY (country) 
DIMENSION BY (prod, year)
MEASURES (sale sales, 0 growth)
RULES (
growth[prod in ('Bounce','Y Box','Mouse Pad'), ANY] =
100* (sales[cv(prod), cv(year)] - 
sales[cv(prod), cv(year) -1] ) / 
sales[cv(prod), cv(year) -1] )
ORDER BY country, prod, year
/

该查询给出的结果与之前的查询结果相同,因为完整的数据集范围是 1998 至 2001,而这个范围是在之前的查询中指定的。

返回主题列表

MODEL 子句提供了一个 FOR 结构,可用在规则内部以便更简洁地表达计算。规则两侧均可以使用 FOR 结构。例如,请考虑以下规则,该规则估计几种产品在 2005 年的销售将比 2001 年的销售高 30%:

RULES
sales['Mouse Pad', 2005] = 1.3 * sales['Mouse Pad', 2001],
sales['Bounce', 2005] = 1.3 * sales['Bounce', 2001],
sales['Y Box', 2005] = 1.3 * sales['Y Box', 2001]

通过在规则左侧使用位置表示法可以确保,如果数组中没有这些产品在 2005 年的销售单元格,则可以插入这些单元格。这样做非常费事,因为规则的多少取决于产品的数量。这种方法对于具有大量产品的用户很不实用。

您可以改写该计算,以使其简洁并具有完全相同的效果:

SELECT SUBSTR(country,1,20) country, 
SUBSTR(prod,1,15) prod, year, sales
FROM   sales_view
WHERE country='Italy'
MODEL RETURN UPDATED ROWS 
PARTITION BY (country) 
DIMENSION BY (prod, year)
MEASURES (sale sales) 
RULES (
sales[FOR prod in ('Mouse Pad', 'Bounce', 'Y Box'), 2005] =
1.3 * sales[cv(prod), 2001] )
ORDER BY country, prod, year;

结果如下:

COUNTRY              PROD                  YEAR      SALES
-------------------- --------------- ---------- ----------
Italy Bounce 2005 6407.245
Italy Mouse Pad 2005 6402.63
Italy Y Box 2005 108308.304

如果您编写的范类似于以上内容,但是没有 FOR 关键字,那么只会更新已存在的单元格,并且不会插入新单元格。在 SH 数据中,这意味着没有行返回。以下是该查询:

SELECT SUBSTR(country,1,20) country, SUBSTR(prod,1,15) prod, year, sales
FROM   sales_view
WHERE country='Italy'
MODEL RETURN UPDATED ROWS 
PARTITION BY (country) 
DIMENSION BY (prod, year)
MEASURES (sale sales) 
RULES (
sales[prod in ('Mouse Pad', 'Bounce', 'Y Box'), 2005] =
1.3 * sales[cv(prod), 2001] )
ORDER BY country, prod, year;
no rows selected

您可以看到 FOR 结构从单一规则生成了多个带有位置引用的规则,从而实现了新单元格的创建(UPSERT 行为)。

请注意,MODEL 子句对于单一模型中支持的规则数量有限制,并且 FOR 循环可以创建足够的虚拟规则来满足这些限制要求。其中所涉及的限制和技术在 Oracle 的数据仓库指南 第 22 节“用于建模的 SQL”中有述。

如果知晓所需的维值来自间隔均匀的序列,您可以使用另一种形式的 FOR 结构:

   FOR dimension FROM value1 TO value2  [INCREMENT | DECREMENT] value3

此规范通过从 value1 开始并以 value3 为增量递增(或递减),来得到 value1 和 value2 之间的值。

要指定 Mouse Pad 于 2005 至 2012 年间的预计销售值,以使这些值等于 2001 年销售值的 120%,请从 SQL*Plus 会话中,执行以下脚本:

@for.sql

for.sql 脚本包含以下内容:

SELECT SUBSTR(country,1,20) country, 
SUBSTR(prod,1,15) prod, year, sales
FROM   sales_view
WHERE country='Italy'
MODEL RETURN UPDATED ROWS 
PARTITION BY (country) 
DIMENSION BY (prod, year)
MEASURES (sale sales) 
RULES (
sales['Mouse Pad', FOR year FROM 2005 TO 2012 INCREMENT 1] =
1.2 * sales[cv(prod), 2001] )
ORDER BY country, prod, year
/

这种 FOR 结构可用于数值、日期和日期时间数据类型的维。递增/递减表达式 value3 应是如此:对于数值型维是数值,对于日期或日期时间类型维,可以是数值或间隔。还有其他使用 FOR 结构的方法,这在“数据仓库指南”中有详细描述。其他方法中最重要的一个是,使用 SQL 子查询作为 IN 操作符的参数。在使用带有子查询的 FOR 结构时,检查 FOR 结构生成的规则总数,并确保该数量不会超过 10,000 的规则限制至关重要。

返回主题列表

默认情况下,规则以其出现在 MODEL 子句中的顺序进行评估。在 MODEL 子句中,可以指定可选关键字 SEQUENTIAL ORDER,以明确评估顺序。带有连续的评估规则顺序的 SQL 模型称为 Sequential Order 模型。

要计算模型,以便以正确的顺序考虑和处理所有规则相关性,请使用 AUTOMATIC ORDER 关键字。对于具有大量规则的模型,使用 AUTOMATIC ORDER 选项要比手动检查规则是否以逻辑正确的顺序列出更为有效。这使得模型的开发和维护更高效。

具有许多规则的模型可以根据其他产品创建新的产品值。要确保这些规则以正确的顺序执行,以防丢失任何相关性,请使用 AUTOMATIC ORDER 关键字。以下示例包含了三条规则以说明这个概念。从 SQL*Plus 会话中,执行以下脚本:

@s_o.sql

s_o.sql 脚本包含以下内容:

SELECT SUBSTR(country,1,20) country, 
SUBSTR(prod,1,15) prod, year, sales
FROM sales_view
WHERE country IN ('Italy','Japan')
MODEL RETURN UPDATED ROWS
PARTITION BY (country)
DIMENSION BY (prod, year)
MEASURES (sale sales)
RULES SEQUENTIAL ORDER (
sales['2_Products', 2002] = sales['Bounce', 2002] + sales['Y Box', 2002],
sales['Bounce', 2002] = sales['Bounce', 2001] + sales['Bounce', 2000],
sales['Y Box', 2002] = sales['Y Box', 2001] )
ORDER BY country, prod, year
/


该查询将返回新创建的 2_Products 产品的结果,并在 2_Products 之前计算 Bounce 和 Y Box 的值:

该查询不应在 2_Products 之前计算 Bounce 和 Y Box 的值,并将空值指定给 2_Products。

返回主题列表

对于单元格度量的非确定性值,使用 SQL MODEL 的应用程序不仅会通过存储 NULL 项的形式,还会通过缺失单元格的形式来处理。在查询数据中缺失并由单一单元格引用所引用的单元格称为缺失单元格。MODEL 子句提供了一种处理空值和缺失单元格的默认方法,还提供了一些选项,以便应用程序可以根据其业务逻辑来处理非确定性值。默认情况下,NULL 单元格度量值的处理方式与空值在 SQL 其他位置的处理方式相同。缺失单元格被视为带有 NULL 度量值的单元格。例如,以下查询会为销售生成 NULL,因为数据集未包含 2004 年的值:

SELECT SUBSTR(country,1,20) country, SUBSTR(prod,1,15) prod, year, sales
FROM sales_view
WHERE country='Italy'
MODEL RETURN UPDATED ROWS
PARTITION BY (country)
DIMENSION BY (prod, year)
MEASURES (sale sales)
RULES (
sales['Mouse Pad', 2005] =
sales['Mouse Pad', 1999] + sales['Mouse Pad', 2004])

ORDER BY country, prod, year;
COUNTRY              PROD                  YEAR      SALES
-------------------- --------------- ---------- ----------
Italy                Mouse Pad             2005

因为 NULL 值导致许多规则返回空值,因此将空值和缺失单元格视为 0 值可能更为有用。这样,空值就不会通过一组计算进行传播了。您可以使用 IGNORE NAV 选项(NAV 表示未提供的值)将空值和缺失单元格默认为以下值:

@i_n.sql

i_n.sql 脚本包含以下内容:

SELECT SUBSTR(country,1,20) country, 
SUBSTR(prod,1,15) prod, year, sales
FROM sales_view
WHERE country='Italy'
MODEL IGNORE NAV RETURN UPDATED ROWS
PARTITION BY (country)
DIMENSION BY (prod, year)
MEASURES (sale sales)
RULES (
sales['Mouse Pad', 2005] =
sales['Mouse Pad', 1999] + sales['Mouse Pad', 2004])
ORDER BY country, prod, year

返回主题列表

除了在其上运行规则的多维数组(称为主 SQL 模型)外,还可以在 MODEL 子句中创建并引用一个或多个只读多维数组(称为引用模型)以充当查询表。使用引用模型,您可以关联不同维度的对象。与主 SQL 模型一样,引用模型通过查询块定义,并使用 DIMENSION BY 和 MEASURE 子句来分别指示它的维和度量。引用模型由 MODEL 子句的以下从属子句创建:

REFERENCE model_name ON (query) DIMENSION BY (cols) MEASURES (cols) [reference options

引用模型只能用于规则右侧,并且不提供 PARTITION 子句。

将不同国家/地区的预计销售数字(均以各自的货币形式表示)转换为美元,并以两种货币(美元和当地货币)形式显示。您需要创建一个表,其中包含当地货币与美元之间的兑换比率。从 SQL*Plus 会话中,执行以下脚本:

@cre_dc.sql

cre_dc.sql 脚本包含以下内容:

CREATE TABLE dollar_conv(country VARCHAR2(30), exchange_rate NUMBER)
/

向 DOLLAR_CONV 表中插入两行。从 SQL*Plus 会话中,执行以下脚本:

@ins_dc.sql

ins_dc.sql 脚本包含以下内容:

INSERT INTO dollar_conv VALUES('Canada', 0.75)
/
INSERT INTO dollar_conv VALUES('Brazil', 0.14)
/

使销售基于 2001 年的数字,并预计 2005 年加拿大的市场增长为 22%,巴西为 34%。

要将加拿大和巴西在 2005 年的预计销售转换为美元,您可以使用引用模型。从 SQL*Plus 会话中,执行以下脚本:

@rm.sql

rm.sql 脚本包含以下内容:

SELECT SUBSTR(country,1,20) country, year, localsales, dollarsales
FROM   sales_view
WHERE country IN ( 'Canada', 'Brazil')
GROUP BY country, year
MODEL RETURN UPDATED ROWS 
REFERENCE conv_refmodel ON (
SELECT country, exchange_rate AS er FROM dollar_conv)
DIMENSION BY (country) MEASURES (er) IGNORE NAV
MAIN main_model 
DIMENSION BY (country, year)
MEASURES (SUM(sale) sales, 0 localsales, 0 dollarsales) IGNORE NAV
RULES (
/* assuming that sales in Canada grow by 22% */
localsales['Canada', 2005] = sales[cv(country), 2001] * 1.22,
dollarsales['Canada', 2005] = sales[cv(country), 2001] * 1.22 *
conv_refmodel.er['Canada'],
/* assuming that economy in Brazil grows by 34% */
localsales['Brazil', 2005] = sales[cv(country), 2001] * 1.34,
dollarsales['Brazil', 2005] = sales['Brazil', 2001] * 1.34 * er['Brazil']
/

请注意以下内容:

  • 根据 DOLLAR_CONV 表的行创建一个名为 CONV_REFMODEL 的一维引用模型,其名为 ER 的度量 EXCHANGE_RATE 已经在主模型的规则中进行了引用。
  • 主模型在其规范开头处具有可选的关键字 MAIN,因而赋予其别名 MAIN_MODEL。MAIN 关键字使主模型规范的开头处更易于受到注意。MAIN_MODEL 具有 COUNTRY 和 YEAR 两个维,而引用模型 DOLLAR_CONV 只有 COUNTRY 一个维。
  • 您可以使用不同的样式访问引用模型的 EXCHANGE_RATE 度量:对于加拿大,通过表示法 model_name.measure_name 明确表示为 CONV_REFMODEL.ER;而对于巴西,则通过简单的 measure_name 引用表示为 ER。必须使用前一种表示法在主模型和引用模型之间解析列名中的多义性。
  • 在指定新度量 LOCALSALES 和 DOLLARSALES 时使用占位符值 0。其他数字也可以充当占位符值。
  • 本例中的增长率在规则中是硬编码的:加拿大的增长率为 22%,巴西的为 34%。如果您的规则能够使用从单独的增长率表中查询的增长值,那么这些规则将灵活得多。这种表格可能涵盖了许多年份和国家/地区。

    使用汇率引用模型和增长率引用模型来查找分别以地货币和美元表示的 2002 年预计销售。创建一个按国家/地区和年份来存储增长百分比的表格。从 SQL*Plus 会话中,执行以下脚本:

    @cre_gr.sql

    cre_gr.sql 脚本包含以下内容:

    CREATE TABLE growth_rate(country VARCHAR2(30), 
    year NUMBER, 
    growth_rate NUMBER)
    

    编写一个查询以计算巴西和加拿大的销售,然后应用 2002 年的增长数字并将值转换为美元。在查询中使用以下引用模型。从 SQL*Plus 会话中,执行以下脚本:

    @rm2.sql

    rm2.sql 脚本包含以下内容:

    SELECT   SUBSTR(country,1,20) country, year, localsales, dollarsales
    FROM sales_view
    WHERE country IN ('Canada','Brazil')
    GROUP BY country, year
    MODEL RETURN UPDATED ROWS
    REFERENCE conv_refmodel ON (
    SELECT country, exchange_rate FROM dollar_conv)
    DIMENSION BY (country c)
    MEASURES (exchange_rate er) IGNORE NAV
    REFERENCE growth_refmodel ON (
    SELECT country, year, growth_rate FROM growth_rate)
    DIMENSION BY (country c, year y)
    MEASURES (growth_rate gr) IGNORE NAV
    MAIN main_model
    DIMENSION BY (country, year)
    MEASURES (SUM(sale) sales, 0 localsales, 0 dollarsales) IGNORE NAV
    RULES (
    localsales[FOR country IN ('Brazil', 'Canada'), 2002] =
    sales[cv(country), 2001] *
    (100 + gr[cv(country), cv(year)])/100 ,
    dollarsales[FOR country IN ('Brazil', 'Canada'),2002] =
    sales[cv(country), 2001] *
    (100 + gr[cv(country), cv(year)])/100 *
    er[cv(country)]
    )
    /

    请注意以下内容:

  • 该查询展示了 MODEL 子句在处理不同维度对象方面的能力。引用模型 CONV_REFMODEL 具有一个维,而引用模型 GROWTH_REFMODEL 和主 SQL 模型具有两个维。
  • 引用模型上的单一单元格引用中的维是使用 CV() 函数指定的,从而将主 SQL 模型中的单元格与引用模型相关联。实际上,该规范在主模型和引用模型之间执行了关系连接。
  • 通过使用 FOR 结构,每条规则都可以处理多个国家/地区,从而减少了编码量。
  • 如果将 FOR 结构添加到规则左侧的 YEAR 维,并将 CV(year) 表达式置于规则右侧,则可以将规则扩展至多个年份。
  • 通过使用 MODEL 子句的 ITERATE 选项,您可以通过迭代方式对规则进行指定次数的评估。将迭代次数指定为 ITERATE 子句的参数。可以只为 SEQUENTIAL ORDER 模型指定 ITERATE。使用迭代模型计算其规则相互依赖的模型。

    ITERATE 子句的语法是:

    ITERATE (number_of_iterations) [ UNTIL (condition) ]

    ITERATE 子句的 number_of_iterations 参数是正整数常量。或者,您可以指定一个提前终止条件,以便在规则评估达到最大迭代次数前将其停止。该条件可以在 ITERATE 的 UNTIL 从属子句中指定,并在迭代结束时进行检查。因此,在指定 ITERATE 后,您始终至少具有一个迭代。

    在完成指定次数的迭代或终止条件计算为 TRUE 后(二者无论先后),迭代评估将停止。在某些情况下,您可能需要将终止条件基于迭代间单元格值的更改。Oracle 数据库 10g 提供了一种机制,以通过允许访问 UNTIL 条件中当前迭代之前和之后存在的单元格值,来指定这种条件。可以使用 PREVIOUS 函数,该函数使用单一单元格引用作为参数,并返回该单元格的度量值(如果该单元格在上一次迭代后存在)。您也可以使用 ITERATION_NUMBER 系统变量来访问当前迭代次数。ITERATION_NUMBER 以值 0 开始,并在每次迭代后递增。通过使用 PREVIOUS 和 ITERATION_NUMBER,您可以构造复杂的终止条件。

    例如,您想为某人(其薪酬为 10 万美金,资本收益为 1.5 万美金)制定理财计划。他的净收入计算方式为,薪酬减去利息支出再减去赋税。他要为贷款支付扣税后的利息,还要支付以下赋税:28% 的薪酬收入(扣除利息支出后)和 38% 的资本收益。他希望自己的利息支出正好为收入的 30%。您如何计算赋税、利息支出以及由此得出的净收入?

    该示例的所有值均存储在名为 LEDGER 的表中。该表将理财项目标签置于某一列中,并将项目的值置于另一列中。从 SQL*Plus 会话中,执行以下脚本:

    @cre_led.sql

    cre_led.sql 脚本包含以下内容:

    CREATE TABLE  ledger  (account  VARCHAR2(20), balance  NUMBER(10,2) )
    /

    向 LEDGER 表中插入行。从 SQL*Plus 会话中,执行以下脚本:

    @ins_led.sql

    ins_led.sql 脚本包含以下内容:

    INSERT INTO ledger VALUES ('Salary', 100000)
    INSERT INTO ledger VALUES ('Capital_gains', 15000)
    INSERT INTO ledger VALUES ('Net', 0)
    INSERT INTO ledger VALUES ('Tax', 0)
    INSERT INTO ledger VALUES ('Interest', 0)
    

    要执行计算,请使用 ITERATE 选项按照所需次数重复计算。第一轮将存储在 LEDGER 表中的值插入规则的右侧,并为 NET、TAX 和 INTEREST 创建一组新值。第二轮将使用上一轮中计算出的 TAX 和 INTEREST 值为 NET、TAX 和 INTEREST 计算出一组新值。该循环将总共重复 100 次。从 SQL*Plus 会话中,执行以下脚本:

    @it1.sql

    it1.sql 脚本包含以下内容:

    SELECT b, account
    FROM ledger
    MODEL IGNORE NAV
    DIMENSION BY (account)
    MEASURES (balance b)
    RULES ITERATE (100) (
    b['Net'] = b['Salary'] - b['Interest'] - b['Tax'],
    b['Tax'] = (b['Salary'] - b['Interest']) * 0.38 +
    b['Capital_gains'] *0.28,
    b['Interest'] = b['Net'] * 0.30
    )
    /

    编写一个查询,以避免上一示例中不必要的处理时间。每一次循环结束后,都对结果进行监视。如果特定结果的值明显表现出变化终止,那么此时您可以停止循环。从 SQL*Plus 会话中,执行以下脚本:

    @it2.sql

    it2.sql 脚本包含以下内容:

    SELECT b, account
    FROM ledger
    MODEL IGNORE NAV
    DIMENSION BY (account)
    MEASURES (balance b)
    RULES ITERATE (100)
    UNTIL ( ABS( (PREVIOUS(b['Net']) - b['Net']) ) < 0.01 ) (
    b['Net'] = b['Salary'] - b['Interest'] - b['Tax'],
    b['Tax'] = (b['Salary'] - b['Interest']) * 0.38 +
    b['Capital_gains'] *0.28,
    b['Interest'] = b['Net'] * 0.30,
    b['Iteration Count']= ITERATION_NUMBER + 1
    -- the '+1' is needed because the ITERATION_NUMBER starts at 0
    )
    /

  • 将 ABS() 函数用作 UNTIL 子句的一部分。这可确保前一个值和当前值之间的差可以是正数,也可以是负数,只要它小于条件。
  • 通过规则 s['Iteration Count']= ITERATION_NUMBER+1,可以定义一个名为 Iteration Count 的新行。将 ITERATION_NUMBER 变量的值指定给该行,从而跟踪所执行的循环次数。
  • 在本例中,您只需 26 次循环就可以使示例接近稳定状态。在此处停止,可以避免 74 次多余的迭代。
  • 返回主题列表

    顺序规则就是在左侧指定 ORDER BY 的规则。它以 ORDER BY 指定的顺序访问单元格,并应用右侧的计算。这非常重要,因为将位置 ANY 和/或符号引用置于规则左侧后,可能会接收到错误消息,表明规则的结果依赖于访问单元格的顺序,因此是非确定性的。请考虑以下模型:

    SELECT year, sales
    FROM   sales_view
    WHERE  country='Italy' AND prod='Bounce' 
    MODEL
    DIMENSION BY (year )
    MEASURES (sale sales) 
    RULES SEQUENTIAL ORDER (
    sales[ANY] = sales[CV(year)-1]
    ORDER BY year;
    

    由于结果是不确定的,因此这个查询会返回错误消息:值依赖于访问单元格的顺序。该查询试图将某年的销售值设为上一年的销售值(所有年份都如此)。遗憾地是,这条规则的结果依赖于访问单元格的顺序。如果以年份的递增顺序访问单元格,那么结果将显示在下表的第三列中。此处没有 1998 年的值,因此将 NULL 指定给 1999 年的值。该 NULL 将被引入之后的所有指定中。如果以年份的递减顺序访问单元格,那么结果将显示在第四列中。因为 2000 年具有一个可指定给 2001 年的有效值,所以 2000 年和 2001 年的值都不会是空值。因此,在以年份的递减顺序访问单元格时,只有 1999 年的值被指定为 NULL(因为此处没有 1998 年的值)。

                     Current Yr     Prior Yr Sales 
    Year   Sales     if ascending   if descending
    1999 2472.13     NULL           NULL
    2000 4370.43     NULL           2472.13
    2001             NULL           4370.43

    根据以上信息编写查询,以确保以年份的递减顺序访问该查询中的单元格,并返回非空结果。您需要将 ORDER BY 子句添加到规则。从 SQL*Plus 会话中,执行以下脚本:

    @of.sql

    of.sql 脚本包含以下内容:

    SELECT year, sales
    FROM sales_view
    WHERE country='Italy' AND prod='Bounce'
    MODEL
    DIMENSION BY (year )
    MEASURES ( sale sales)
    RULES SEQUENTIAL ORDER (
    sales[ANY] ORDER BY year DESC= sales[cv(year)-1]
    )
    ORDER BY year
    /

    总之,您可以使用任何 ORDER BY 规范,只要该规范能够在符合左侧单元格引用的单元格中生成唯一的顺序。规则的 ORDER BY 中的表达式可以涉及常量、度量以及维关键字,并且您可以指定排序选项 [ASC | DESC] [NULLS FIRST | NULLS LAST] 以获取所需的顺序。

    除上述主题外,Oracle 数据库 10g 第 2 版还提供了更多的 SQL Model 子句扩展,其中包括:

    在 Model 子句内支持分析 SQL 函数 模型可以包含的规则数量具有更大的灵活性 额外的 Upsert 形式