SQL*Plus BREAK 的行为如何/为什么取决于列顺序?

How / why does behavior of SQL*Plus BREAK depend on column order?

背景

我对数据库很熟悉,但从未特别使用过 Oracle。在尝试帮助某人完成家庭作业的过程中,我在试图理解 BREAK 命令的行为时遇到了障碍。特别是,使用 BREAK 命令的结果似乎取决于我正在使用的查询中列的顺序,据我所知,这并没有以任何方式反映在文档中。

示例设置

我创建了一个 table,其中有几种不同的颜色,每种颜色都有一些该颜色的项目,如下所示:

CREATE TABLE products(product_id NUMBER, product_color VARCHAR(10), product_name VARCHAR(20));
INSERT INTO products(product_id, product_color, product_name) VALUES(1, 'Green', 'Green baseball cap');
INSERT INTO products(product_id, product_color, product_name) VALUES(2, 'Green', 'Green shirt');
INSERT INTO products(product_id, product_color, product_name) VALUES(3, 'Green', 'Grapes');
INSERT INTO products(product_id, product_color, product_name) VALUES(4, 'Orange', 'Orange baseball cap');
INSERT INTO products(product_id, product_color, product_name) VALUES(5, 'Orange', 'Traffic cone');

显示预期行为的示例

当我运行以下命令时,

CLEAR COLUMNS;
CLEAR BREAKS;
CLEAR SCREEN;

SET LINESIZE 120;

COLUMN product_color NOPRINT NEW_VALUE the_color;
BREAK ON product_color SKIP PAGE;

TTITLE CENTER the_color;

SELECT product_id, product_color, product_name FROM products ORDER BY product_color;

这似乎产生了合理的输出,每种颜色一个“页面”在顶部显示颜色并在下方显示相关记录:

                                                        Green
PRODUCT_ID PRODUCT_NAME        
---------- --------------------
         1 Green baseball cap  
         2 Green shirt         
         3 Grapes              

                                                        Orange  
PRODUCT_ID PRODUCT_NAME        
---------- --------------------
         4 Orange baseball cap 
         5 Traffic cone        

表现出意外行为的看似相同的示例

但是,如果我只是更改查询中列的顺序,即将上面脚本的最后一行替换为:

SELECT product_color, product_id, product_name FROM products ORDER BY product_color;

那么结果就完全不同了,在每条记录后无缘无故地生成一个分页符:

                                                        Green  
PRODUCT_ID PRODUCT_NAME        
---------- --------------------
         1 Green baseball cap  

                                                        Green  
PRODUCT_ID PRODUCT_NAME        
---------- --------------------
         2 Green shirt         

                                                        Green  
PRODUCT_ID PRODUCT_NAME        
---------- --------------------
         3 Grapes              

                                                        Orange 
PRODUCT_ID PRODUCT_NAME        
---------- --------------------
         4 Orange baseball cap 

                                                        Orange 
PRODUCT_ID PRODUCT_NAME        
---------- --------------------
         5 Traffic cone

问题

可能的线索

我已经通读了位于 https://docs.oracle.com/cd/E11882_01/server.112/e16604/ch_twelve009.htm#SQPUG030 的 SQL*Plus BREAK 命令的文档,但我没有看到任何与列出现顺序相关的内容。

我认为这个问题可能与抑制干扰字段更改检测的重复项有关,但是将 DUPLICATES 添加到 BREAK 命令没有任何效果。

在六种可能的排列中,看起来导致问题的是其中 product_color 是第一列的两个排列。

同样,将查询更改为

SELECT 1, product_color, product_id, product_name FROM products ORDER BY product_color;

产生了预期的行为,表明最左边的列可能在这里扮演了一些特殊的角色。

此外,我发现从 COLUMN 命令中删除 NOPRINT 可以消除这种行为,原因我不明白。

环境信息

oracle 服务器报告它是

Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
PL/SQL Release 11.2.0.4.0 - Production
"CORE   11.2.0.4.0  Production"
TNS for Linux: Version 11.2.0.4.0 - Production
NLSRTL Version 11.2.0.4.0 - Production

我正在使用 Oracle SQL Developer version 21.2.1.204,Build 204.1703 进行连接。

您示例中 BREAK 的行为不正确。感谢@Littlefoot,我更新了这个 post,这是因为您使用的是 SqlDeveloper,一个不适合此类报告的 java 工具。

我创建了您的 table 并将记录插入到两个数据库中,然后 运行 收集您所做的报告。

演示 12cR2

SQL> select banner from v$version ;

BANNER
--------------------------------------------------------------------------------
Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production
PL/SQL Release 12.2.0.1.0 - Production
CORE    12.2.0.1.0      Production
TNS for Linux: Version 12.2.0.1.0 - Production
NLSRTL Version 12.2.0.1.0 - Production

SQL> CREATE TABLE products(product_id NUMBER, product_color VARCHAR(10), product_name VARCHAR(20));

Table created.

INSERT INTO products(product_id, product_color, product_name) VALUES(1, 'Green', 'Green baseball cap');

1 row created.

INSERT INTO products(product_id, product_color, product_name) VALUES(2, 'Green', 'Green shirt');

1 row created.

INSERT INTO products(product_id, product_color, product_name) VALUES(3, 'Green', 'Grapes');

1 row created.

INSERT INTO products(product_id, product_color, product_name) VALUES(4, 'Orange', 'Orange baseball cap');

1 row created.

SQL> INSERT INTO products(product_id, product_color, product_name) VALUES(5, 'Orange', 'Traffic cone');

1 row created.

现在让我们运行报告

SQL> CLEAR COLUMNS;
columns cleared
SQL> CLEAR breaks;
breaks cleared
SQL> COLUMN product_color NOPRINT NEW_VALUE the_color;
SQL> BREAK ON product_color SKIP PAGE;
SQL> TTITLE CENTER the_color;
SQL> SELECT product_id, product_color, product_name FROM products ORDER BY product_color;

                                      Green
PRODUCT_ID PRODUCT_NAME
---------- --------------------
         1 Green baseball cap
         2 Green shirt
         3 Grapes

                                     Orange
PRODUCT_ID PRODUCT_NAME
---------- --------------------
         4 Orange baseball cap
         5 Traffic cone

SQL> SELECT product_color, product_id, product_name FROM products ORDER BY product_color;

                                      Green
PRODUCT_ID PRODUCT_NAME
---------- --------------------
         1 Green baseball cap
         2 Green shirt
         3 Grapes

                                     Orange
PRODUCT_ID PRODUCT_NAME
---------- --------------------
         4 Orange baseball cap
         5 Traffic cone

如您所见,在 12.2 中,BREAK 命令没有意外行为,它可以正常工作。

演示 19c

SQL> select banner_full from v$version ;
                                                            
BANNER_FULL
------------------------------------------------------------------------------------------------------------------------
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.6.0.0.0

SQL> CREATE TABLE products(product_id NUMBER, product_color VARCHAR(10), product_name VARCHAR(20));

Table created.

INSERT INTO products(product_id, product_color, product_name) VALUES(1, 'Green', 'Green baseball cap');

1 row created.

INSERT INTO products(product_id, product_color, product_name) VALUES(2, 'Green', 'Green shirt');

1 row created.

INSERT INTO products(product_id, product_color, product_name) VALUES(3, 'Green', 'Grapes');

1 row created.

INSERT INTO products(product_id, product_color, product_name) VALUES(4, 'Orange', 'Orange baseball cap');

1 row created.

SQL> INSERT INTO products(product_id, product_color, product_name) VALUES(5, 'Orange', 'Traffic cone');

1 row created.

现在,让我们运行报告

SQL> CLEAR COLUMNS;
columns cleared
SQL> CLEAR BREAKS; 
breaks cleared
SQL> COLUMN product_color NOPRINT NEW_VALUE the_color;
SQL> BREAK ON product_color SKIP PAGE;
SQL> TTITLE CENTER the_color;
SQL>  SELECT product_id, product_color, product_name FROM products ORDER BY product_color;

                                                          Green
PRODUCT_ID PRODUCT_NAME
---------- --------------------
         1 Green baseball cap
         2 Green shirt
         3 Grapes

                                                         Orange
PRODUCT_ID PRODUCT_NAME
---------- --------------------
         4 Orange baseball cap
         5 Traffic cone

SQL> SELECT product_color, product_id, product_name FROM products ORDER BY product_color;

                                                          Green
PRODUCT_ID PRODUCT_NAME
---------- --------------------
         1 Green baseball cap
         2 Green shirt
         3 Grapes

                                                         Orange
PRODUCT_ID PRODUCT_NAME
---------- --------------------
         4 Orange baseball cap
         5 Traffic cone

因此,BREAK 命令的预期行为相同。

回答您的问题

为什么更改查询输出中列的顺序会导致与 BREAK 语句不同的行为? (或者 BREAK 命令是一个转移注意力的问题,第二个示例中的分页符是出于某种不相关的原因而创建的吗?)

它不会。正如我之前所说,这种行为在 12c 和 19c 中都无法重现(如 @Littlefoot 所示,在 11g 中无法重现)。我向您保证 BREAK 的正常行为不受订单的影响。

这是否实际上以某种方式记录在文档中,但在某些方面我不理解?

不是真的,因为您了解命令的工作原理,但不了解您获得的行为,这显然无法在 12c 或更高版本中重现。我无法在 11g 中进行任何测试,因为这是一个非常旧且不受支持的版本,但在@Littlefoot 的另一个答案中,您可以看到行为是相同的。

有没有比官方 Oracle 站点更好的 SQLPlus 文档?它似乎相当参差不齐,很少明确指定命令的作用。*

参考资料是您能找到的最好的。显然,与其他所有主要软件供应商一样,Oracle 的文档编制并不完美。老实说,我不能为此责怪他们,因为它无处不在。事实上,我确实认为他们有最好的之一。

我的建议:使用 sqlplus 并升级您的数据库。 11g 多年前就被弃用了。您可以尝试使用 sqlcl sql 开发人员命令行界面,但我从未使用过 11g,尽管我认为这应该不是问题

顺便说一句,关于 我发现从 COLUMN 命令中删除 NOPRINT 会使这种行为消失,原因我不明白

PRINT|NOPRINT Controls the printing of the column (the column heading and all the selected values). NOPRINT turns off the screen output and printing of the column. PRINT turns the printing of the column ON.

不完全是。如果将 NOPRINT 更改为 PRINT,它会正常工作,因此您可以打印属于 BREAK 的列。但是,它只在结果中显示一次,因为你打破了它。

SQL> COLUMN product_color PRINT NEW_VALUE the_color;
SQL> SELECT product_id, product_color, product_name FROM products ORDER BY product_color;
                                                          Green
PRODUCT_ID PRODUCT_CO PRODUCT_NAME
---------- ---------- --------------------
         1 Green      Green baseball cap
         2            Green shirt
         3            Grapes

                                                         Orange
PRODUCT_ID PRODUCT_CO PRODUCT_NAME
---------- ---------- --------------------
         4 Orange     Orange baseball cap
         5            Traffic cone

希望这个冗长的回答能以某种方式帮助到您。随时提出您可能有的任何问题或疑问。但我很确定原因是你的版本。

更新

正如@Littlefoot 在下面的回答中所述,该问题可能与 SQLDeveloper 有关,这是一个 Java 工具。我没有意识到你在使用 SQL Developer,即使你可以 运行 脚本,但对于那些 sqlplus 特定的报告命令来说并不相同。我依赖于您指向 sqlplus 的文档链接,所以我自然假设您正在使用它。

奇怪。我的环境和你的一样,我没有看到这样的行为。我刚刚从您发布的代码中删除了 clear screen

这是一个 returns 为您(和我)提供正确结果的查询。

客户端 11.2.0.2.0

C:\Oracle\oraclexe112_64bit\app\oracle\product.2.0\server\bin>sqlplus scott/tiger@orcl

SQL*Plus: Release 11.2.0.2.0 Production on ╚et Stu 11 08:03:58 2021

Copyright (c) 1982, 2014, Oracle.  All rights reserved.


Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
With the Partitioning, Real Application Clusters, Automatic Storage Management, OLAP,
Data Mining and Real Application Testing options

SQL> CLEAR COLUMNS;
columns cleared
SQL> CLEAR BREAKS;
breaks cleared
SQL>
SQL> SET LINESIZE 120;
SQL>
SQL> COLUMN product_color NOPRINT NEW_VALUE the_color;
SQL> BREAK ON product_color SKIP PAGE;
SQL>
SQL> TTITLE CENTER the_color;
SQL>
SQL> SELECT product_id, product_color, product_name FROM products ORDER BY product_color;

                                                          Green
PRODUCT_ID PRODUCT_NAME
---------- --------------------
         1 Green baseball cap
         2 Green shirt
         3 Grapes

                                                         Orange
PRODUCT_ID PRODUCT_NAME
---------- --------------------
         4 Orange baseball cap
         5 Traffic cone

SQL>

现在,在您的数据库中产生意外中断的查询(但在我的数据库中没有),product_colorselect 列列表中的第一列:

SQL> SELECT product_color, product_id, product_name FROM products ORDER BY product_color;

                                                          Green
PRODUCT_ID PRODUCT_NAME
---------- --------------------
         1 Green baseball cap
         2 Green shirt
         3 Grapes

                                                         Orange
PRODUCT_ID PRODUCT_NAME
---------- --------------------
         4 Orange baseball cap
         5 Traffic cone

SQL>

我也尝试了不同的客户端,没问题:

客户端 11.2.0.1.0

SQL*Plus: Release 11.2.0.1.0 Production on ╚et Stu 11 08:10:56 2021
  
SQL> CLEAR COLUMNS;
columns cleared
SQL> CLEAR BREAKS;
breaks cleared
SQL>
SQL> SET LINESIZE 120;
SQL>
SQL> COLUMN product_color NOPRINT NEW_VALUE the_color;
SQL> BREAK ON product_color SKIP PAGE;
SQL>
SQL> TTITLE CENTER the_color;
SQL>
SQL> SELECT product_color, product_id, product_name FROM products ORDER BY product_color;

                                                          Green
PRODUCT_ID PRODUCT_NAME
---------- --------------------
         1 Green baseball cap
         2 Green shirt
         3 Grapes

                                                         Orange
PRODUCT_ID PRODUCT_NAME
---------- --------------------
         4 Orange baseball cap
         5 Traffic cone

SQL>

客户端 18.5.0.0.0

SQL*Plus: Release 18.0.0.0.0 - Production on Thu Nov 11 08:12:20 2021
Version 18.5.0.0.0

SQL> CLEAR COLUMNS;
columns cleared
SQL> CLEAR BREAKS;
breaks cleared
SQL>
SQL> SET LINESIZE 120;
SQL>
SQL> COLUMN product_color NOPRINT NEW_VALUE the_color;
SQL> BREAK ON product_color SKIP PAGE;
SQL>
SQL> TTITLE CENTER the_color;
SQL>
SQL> SELECT product_color, product_id, product_name FROM products ORDER BY product_color;

                                                          Green
PRODUCT_ID PRODUCT_NAME
---------- --------------------
         1 Green baseball cap
         2 Green shirt
         3 Grapes

                                                         Orange
PRODUCT_ID PRODUCT_NAME
---------- --------------------
         4 Orange baseball cap
         5 Traffic cone

SQL>

我认为这很重要:

I am using Oracle SQL Developer version 21.2.1.204, Build 204.1703 to connect.

SQL 开发人员。我和罗伯托使用 SQL Plus,Oracle 的命令行工具。

当我测试你的代码时,returns 结果是“无效”,这是真的!

columns cleared
breaks cleared

                                                        Green                                                         
PRODUCT_ID PRODUCT_NAME        
---------- --------------------
         1 Green baseball cap  

                                                        Green                                                         
PRODUCT_ID PRODUCT_NAME        
---------- --------------------
         2 Green shirt         

                                                        Green                                                         
PRODUCT_ID PRODUCT_NAME        
---------- --------------------
         3 Grapes              

                                                        Orange                                                        
PRODUCT_ID PRODUCT_NAME        
---------- --------------------
         4 Orange baseball cap 

                                                        Orange                                                        
PRODUCT_ID PRODUCT_NAME        
---------- --------------------
         5 Traffic cone        

SQL 开发人员 产生了“错误”的结果。与数据库版本无关,而是工具.

那么,怎么办?请改用 SQL*Plus :) 或者,下载 Oracle 提供的最新命令行工具 SQLcl