Oracle 减号查询。如果顶部 SQL 和底部 SQL 不包含 NULL,我如何获得带有 NULLS 的结果?

Posted

技术标签:

【中文标题】Oracle 减号查询。如果顶部 SQL 和底部 SQL 不包含 NULL,我如何获得带有 NULLS 的结果?【英文标题】:Oracle Minus query. How can I get a result with NULLS if the top SQL and the bottom SQL don't contain NULLs? 【发布时间】:2010-06-02 15:26:58 【问题描述】:

这个 SQL 怎么会……

CREATE TABLE NewTable AS
    SELECT A,B,C FROM Table1
    minus
    SELECT A, B, C From Table2

...在 A 列中创建一个具有 NULL 值的新表 当 Table1 或 Table2 在 A 列中都没有 NULL 值时?

但另一方面,这个 SQL...

SELECT * FROM
(
   SELECT A,B,C FROM Table1
    minus
    SELECT A, B, C From Table2
) 
WHERE A IS NULL 

不返回任何行!

好像不一致!

我认为这是 Oracle 中的一个错误。

当然,真正的 SQL 要复杂得多,但我相信这准确地说明了问题的本质。

更新

这是实际的 SQL:

我执行了这个语句:

CREATE TABLE MyMinus
AS
select 
*
FROM
---begin main query 
(
SELECT expenditure_item_date, expenditure_org, expenditure_type,
       f_amount_billed, f_amount_billed_fc, f_amount_billed_us,
       f_bl_creation_date, f_catalog_source, f_catalog_type, f_company,
       f_company_code, f_cost_center_num, f_cuic, f_currency_code,
       f_destination_type_code, f_distribution_id, f_distribution_num,
       f_exchange_rate, f_extract_date, f_gl_account,
       f_isms_jamis_project_num, f_line_id, f_local_use, f_location_num,
       f_need_by_date, f_org_id, f_po_line_num, f_po_num, f_po_release_num,
       f_project, f_project_num, f_promised_date, f_quantity_billed,
       f_quantity_cancelled, f_quantity_delivered, f_quantity_ordered,
       f_rel_approved_flag, f_rel_cancelled_flag, f_rel_cancel_date,
       f_rel_closed_code, f_rel_hold_flag, f_rel_revision_num, f_task_num
  FROM dw_mgr.po_distributions_curr_fct a
 WHERE EXISTS (
          SELECT 1
            FROM dw_mgr.po_distributions_curr_fct b,
                 dw_mgr.po_lines_curr_fct,
                 dw_mgr.po_header_curr_fct
           WHERE a.ROWID = b.ROWID
             AND b.f_cuic = dw_mgr.po_lines_curr_fct.f_cuic
             AND b.f_line_id = dw_mgr.po_lines_curr_fct.f_line_id
             AND dw_mgr.po_lines_curr_fct.f_cuic =
                                              dw_mgr.po_header_curr_fct.f_cuic
             AND dw_mgr.po_lines_curr_fct.f_header_id =
                                         dw_mgr.po_header_curr_fct.f_header_id
             AND dw_mgr.po_header_curr_fct.f_header_creation_date <
                                      ADD_MONTHS (TRUNC (SYSDATE, 'YEAR'),
                                                  -48)
             AND dw_mgr.po_header_curr_fct.f_po_status IN
                                                 ('CLOSED', 'FINALLY CLOSED'))

MINUS 

SELECT expenditure_item_date, expenditure_org, expenditure_type,
       f_amount_billed, f_amount_billed_fc, f_amount_billed_us,
       f_bl_creation_date, f_catalog_source, f_catalog_type, f_company,
       f_company_code, f_cost_center_num, f_cuic, f_currency_code,
       f_destination_type_code, f_distribution_id, f_distribution_num,
       f_exchange_rate, f_extract_date, f_gl_account,
       f_isms_jamis_project_num, f_line_id, f_local_use, f_location_num,
       f_need_by_date, f_org_id, f_po_line_num, f_po_num, f_po_release_num,
       f_project, f_project_num, f_promised_date, f_quantity_billed,
       f_quantity_cancelled, f_quantity_delivered, f_quantity_ordered,
       f_rel_approved_flag, f_rel_cancelled_flag, f_rel_cancel_date,
       f_rel_closed_code, f_rel_hold_flag, f_rel_revision_num, f_task_num
  FROM arch_fct.po_distributions_curr_fct a
 WHERE EXISTS (
          SELECT 1
            FROM arch_fct.po_distributions_curr_fct b,
                 arch_fct.po_lines_curr_fct,
                 arch_fct.po_header_curr_fct
           WHERE a.ROWID = b.ROWID
             AND b.f_cuic = arch_fct.po_lines_curr_fct.f_cuic
             AND b.f_line_id = arch_fct.po_lines_curr_fct.f_line_id
             AND arch_fct.po_lines_curr_fct.f_cuic =
                                            arch_fct.po_header_curr_fct.f_cuic
             AND arch_fct.po_lines_curr_fct.f_header_id =
                                       arch_fct.po_header_curr_fct.f_header_id
             AND arch_fct.po_header_curr_fct.f_header_creation_date <
                                      ADD_MONTHS (TRUNC (SYSDATE, 'YEAR'),
                                                  -48)
             AND arch_fct.po_header_curr_fct.f_po_status IN
                                                 ('CLOSED', 'FINALLY CLOSED'))

) 

然后这个。请注意,已将 F_DISTRIBUTION_ID 值为 NULL 的行插入到创建的表中。

SELECT COUNT(*) from MyMinus WHERE F_DISTRIBUTION_ID IS NULL

--17 行

但是当我执行这个时:

select 
*
FROM
---begin main query 
(
SELECT expenditure_item_date, expenditure_org, expenditure_type,
       f_amount_billed, f_amount_billed_fc, f_amount_billed_us,
       f_bl_creation_date, f_catalog_source, f_catalog_type, f_company,
       f_company_code, f_cost_center_num, f_cuic, f_currency_code,
       f_destination_type_code, f_distribution_id, f_distribution_num,
       f_exchange_rate, f_extract_date, f_gl_account,
       f_isms_jamis_project_num, f_line_id, f_local_use, f_location_num,
       f_need_by_date, f_org_id, f_po_line_num, f_po_num, f_po_release_num,
       f_project, f_project_num, f_promised_date, f_quantity_billed,
       f_quantity_cancelled, f_quantity_delivered, f_quantity_ordered,
       f_rel_approved_flag, f_rel_cancelled_flag, f_rel_cancel_date,
       f_rel_closed_code, f_rel_hold_flag, f_rel_revision_num, f_task_num
  FROM dw_mgr.po_distributions_curr_fct a
 WHERE EXISTS (
          SELECT 1
            FROM dw_mgr.po_distributions_curr_fct b,
                 dw_mgr.po_lines_curr_fct,
                 dw_mgr.po_header_curr_fct
           WHERE a.ROWID = b.ROWID
             AND b.f_cuic = dw_mgr.po_lines_curr_fct.f_cuic
             AND b.f_line_id = dw_mgr.po_lines_curr_fct.f_line_id
             AND dw_mgr.po_lines_curr_fct.f_cuic =
                                              dw_mgr.po_header_curr_fct.f_cuic
             AND dw_mgr.po_lines_curr_fct.f_header_id =
                                         dw_mgr.po_header_curr_fct.f_header_id
             AND dw_mgr.po_header_curr_fct.f_header_creation_date <
                                      ADD_MONTHS (TRUNC (SYSDATE, 'YEAR'),
                                                  -48)
             AND dw_mgr.po_header_curr_fct.f_po_status IN
                                                 ('CLOSED', 'FINALLY CLOSED'))

MINUS 

SELECT expenditure_item_date, expenditure_org, expenditure_type,
       f_amount_billed, f_amount_billed_fc, f_amount_billed_us,
       f_bl_creation_date, f_catalog_source, f_catalog_type, f_company,
       f_company_code, f_cost_center_num, f_cuic, f_currency_code,
       f_destination_type_code, f_distribution_id, f_distribution_num,
       f_exchange_rate, f_extract_date, f_gl_account,
       f_isms_jamis_project_num, f_line_id, f_local_use, f_location_num,
       f_need_by_date, f_org_id, f_po_line_num, f_po_num, f_po_release_num,
       f_project, f_project_num, f_promised_date, f_quantity_billed,
       f_quantity_cancelled, f_quantity_delivered, f_quantity_ordered,
       f_rel_approved_flag, f_rel_cancelled_flag, f_rel_cancel_date,
       f_rel_closed_code, f_rel_hold_flag, f_rel_revision_num, f_task_num
  FROM arch_fct.po_distributions_curr_fct a
 WHERE EXISTS (
          SELECT 1
            FROM arch_fct.po_distributions_curr_fct b,
                 arch_fct.po_lines_curr_fct,
                 arch_fct.po_header_curr_fct
           WHERE a.ROWID = b.ROWID
             AND b.f_cuic = arch_fct.po_lines_curr_fct.f_cuic
             AND b.f_line_id = arch_fct.po_lines_curr_fct.f_line_id
             AND arch_fct.po_lines_curr_fct.f_cuic =
                                            arch_fct.po_header_curr_fct.f_cuic
             AND arch_fct.po_lines_curr_fct.f_header_id =
                                       arch_fct.po_header_curr_fct.f_header_id
             AND arch_fct.po_header_curr_fct.f_header_creation_date <
                                      ADD_MONTHS (TRUNC (SYSDATE, 'YEAR'),
                                                  -48)
             AND arch_fct.po_header_curr_fct.f_po_status IN
                                                 ('CLOSED', 'FINALLY CLOSED'))

) 
WHERE

f_distribution_id is null

我得到 0 行。

为什么将记录插入临时表似乎会引入具有 NULL DIST ID 的行?

这个由自定义数据归档程序动态生成的减号查询 SQL 尝试验证应该归档在 DW_MGR 模式中的数据实际上是否已复制到 ARCH_FCT(归档)模式。它返回的差异包括 17 条记录,其中 MyMinus 临时表中的 F_DISTRIBUTION_ID 与源 DW_MG.PO_DISTRIBUTIONS_CURR_FCT 表中的记录不匹配,因为它们为 NULL。因此,归档过程是在发现差异时设计的。问题是为什么会有差异,即当 NULL 值不在 SOURCE PO_DISTRIBUTIONS_CURR_FCT 表中时,它们是如何进入 MyMinus 表的?

编辑:

具有 Oracle META 访问权限的人能否在 Oracle 错误之后在 thd 上发布信息。我被转介给他们,但我找到了我公司的某个人,他可以告诉我我们的支持 ID # 是什么。我最终会知道的,但如果能早点知道就好了。如果您不想发布它,请考虑以下错误参考作为我的问题的潜在相关信息:

Bug 8209309: MINUS IS SHOWING DIFFERENCES WITH CTAS + INSERT 
Bug 7834950: WRONG RESULTS WITH MINUS OPERATOR

【问题讨论】:

简短的回答是“它不能”,所以你一定做了一些微妙的不同。你能创建一个这个问题的(简单)工作示例吗? 我同意你的观点,我需要检查我的假设。让我们看看我是否可以做到这一点或提供更多信息来说服您和我相信以上是实际发生的情况。 我 99.99% 确信这是一个 Oracle 错误。我想我们可能会让 Oracle 参与确认。 能贴出真正的SQL吗?尽管总是有机会找到产品错误,但这里可能还有其他事情在起作用。并了解哪个版本的 Oracle 会有所帮助(如果知道,请包括主要和次要版本以及补丁级别)。谢谢, 只是想知道 - table1.a 的数据类型是否与 table2.a 的数据类型匹配? 【参考方案1】:

别再折腾了。这是一个甲骨文错误。我会证明给你看的:

首先,它必须是第一个为 DISTRIBUTION ID 返回 NULLS 的 SQL,因此隔离该 SQL,我们称其为“SQL1”。

好的,为了讨论,让我们简化 SQL1 并说它是这种格式:

CREATE TABLE TempTable AS 
SELECT
   F_DISTRIBUTION_ID,
   FIELD2,
   FIELD3,...FIELD99

FROM WHATEVER 
WHERE WHATEVER

然后,您会发现在执行此操作时,您会发现具有 NULL DIST ID 的行:

SELECT COUNT(*) FROM TempTable WHERE F_DISTRIBUTION_ID IS NULL
--Some positive number of rows returned.

如果 Oracle 不是废话,您可以更改所选字段的数量,以便仅选择 F_DISTRIBUTION_ID,并且当您计算 F_DISTRIBUTION_ID 为 NULL 值的行数时,您会得到相同的结果,对吧?对!但事实并非如此,因为甲骨文是不可靠的恐龙。

试试这个:

CREATE TABLE TempTable AS 
SELECT
   F_DISTRIBUTION_ID
FROM WHATEVER 
WHERE WHATEVER

SELECT COUNT(*) FROM TempTable WHERE F_DISTRIBUTION_ID IS NULL

我向甜甜圈打赌,你会得到 0 行返回。

现在,打电话给微软,告诉他们你想升级到 SQL Server 2008 R2。

【讨论】:

你所说的一切都检查过了。你证实了我最初的印象。 这真的很奇怪 - 我无法在我可以访问的任何 Oracle 版本中重现此问题......伙计,你手上遇到了什么错误!【参考方案2】:

首先,我将摆脱 ROWID 到 ROWID 的加入。 然后我会让表 aiases 唯一(不在 MINUS 上方的查询和 MINUS 下方的查询中重复使用 'a' 和 'b')。

最后,我会查看这 17 行并尝试在“dw_mgr.po_distributions_curr_fct”中找到匹配的记录,然后使用 DUMP(F_DISTRIBUTION_ID) 查看列值有什么奇怪之处。

【讨论】:

对 f_distribution_id 列使用 DUMP 函数显示值“NULL”。使用唯一别名不会改变结果 如果 dw_mgr.po_distributions_curr_fct 中这些行的 DUMP(f_distribution_id) 报告 NULL,那么这是因为这些列包含 NULL。如果有一个约束,它可能是 NOT ENABLED 或 NOT VALIDATED。【参考方案3】:

通常不应该。

唯一可能的情况是,如果您有一些高级安全功能(细粒度访问控制),优化器可以看到 A 在 table1/table2 中不能为空,因此返回零行,但 FGAC 会阻止您通过返回 null 查看列中的实际值。


编辑。 “通过 [Virtual Private Database] 列屏蔽行为,所有行都会显示,即使是那些引用敏感列的行。但是,敏感列会显示为 NULL 值。”

http://download.oracle.com/docs/cd/E11882_01/network.112/e10574/vpd.htm#i1014682

【讨论】:

啊,我明白了。不,这里不是这样。我们没有在此表上设置此类安全性。【参考方案4】:

我认为 F_DISTRIBUTION_ID 在插入 MyMinus 时可能为 NULL 的唯一方法是它以某种方式返回 NULL,以某种方式在第一个查询中。

要重现这个(在 9i 和 10g 上):

SQL> INSERT INTO table1 VALUES (NULL, 2, 3);

1 row created.

SQL> INSERT INTO table2 VALUES (1, 2, 3);

1 row created.

SQL> SELECT * 
  2  FROM (
  3    SELECT a, b, c FROM table1
  4    MINUS
  5    SELECT a, b, c FROM table2);

         A          B          C
---------- ---------- ----------
                    2          3

但是,关于查询在自身运行时不返回任何行......这是另一回事。一个错误不会让我感到惊讶......但你有没有试过取出那些存在?当然,有许多不同的方法,但也许所有这些子查询都会导致内存中发生一些有趣的事情。

例如:

SELECT expenditure_item_date, expenditure_org, expenditure_type,
       f_amount_billed, f_amount_billed_fc, f_amount_billed_us,
       f_bl_creation_date, f_catalog_source, f_catalog_type, f_company,
       f_company_code, f_cost_center_num, f_cuic, f_currency_code,
       f_destination_type_code, f_distribution_id, f_distribution_num,
       f_exchange_rate, f_extract_date, f_gl_account,
       f_isms_jamis_project_num, f_line_id, f_local_use, f_location_num,
       f_need_by_date, f_org_id, f_po_line_num, f_po_num, f_po_release_num,
       f_project, f_project_num, f_promised_date, f_quantity_billed,
       f_quantity_cancelled, f_quantity_delivered, f_quantity_ordered,
       f_rel_approved_flag, f_rel_cancelled_flag, f_rel_cancel_date,
       f_rel_closed_code, f_rel_hold_flag, f_rel_revision_num, f_task_num
  FROM dw_mgr.po_distributions_curr_fct a
       dw_mgr.po_lines_curr_fct,
       dw_mgr.po_header_curr_fct
  WHERE a.f_cuic = dw_mgr.po_lines_curr_fct.f_cuic
    AND a.f_line_id = dw_mgr.po_lines_curr_fct.f_line_id
    AND dw_mgr.po_lines_curr_fct.f_cuic = dw_mgr.po_header_curr_fct.f_cuic
    AND dw_mgr.po_lines_curr_fct.f_header_id = dw_mgr.po_header_curr_fct.f_header_id
    AND dw_mgr.po_header_curr_fct.f_header_creation_date < ADD_MONTHS (TRUNC (SYSDATE, 'YEAR'), -48)
    AND dw_mgr.po_header_curr_fct.f_po_status IN ('CLOSED', 'FINALLY CLOSED')
MINUS 
SELECT expenditure_item_date, expenditure_org, expenditure_type,
       f_amount_billed, f_amount_billed_fc, f_amount_billed_us,
       f_bl_creation_date, f_catalog_source, f_catalog_type, f_company,
       f_company_code, f_cost_center_num, f_cuic, f_currency_code,
       f_destination_type_code, f_distribution_id, f_distribution_num,
       f_exchange_rate, f_extract_date, f_gl_account,
       f_isms_jamis_project_num, f_line_id, f_local_use, f_location_num,
       f_need_by_date, f_org_id, f_po_line_num, f_po_num, f_po_release_num,
       f_project, f_project_num, f_promised_date, f_quantity_billed,
       f_quantity_cancelled, f_quantity_delivered, f_quantity_ordered,
       f_rel_approved_flag, f_rel_cancelled_flag, f_rel_cancel_date,
       f_rel_closed_code, f_rel_hold_flag, f_rel_revision_num, f_task_num
  FROM arch_fct.po_distributions_curr_fct a,
       arch_fct.po_lines_curr_fct,
       arch_fct.po_header_curr_fct
 WHERE a.f_cuic = arch_fct.po_lines_curr_fct.f_cuic
   AND a.f_line_id = arch_fct.po_lines_curr_fct.f_line_id
   AND arch_fct.po_lines_curr_fct.f_cuic = arch_fct.po_header_curr_fct.f_cuic
   AND arch_fct.po_lines_curr_fct.f_header_id = arch_fct.po_header_curr_fct.f_header_id
   AND arch_fct.po_header_curr_fct.f_header_creation_date < ADD_MONTHS (TRUNC (SYSDATE, 'YEAR'), -48)
   AND arch_fct.po_header_curr_fct.f_po_status IN ('CLOSED', 'FINALLY CLOSED')   

【讨论】:

你是对的。第一个查询返回 NULL。在我执行 CREATE MyMinus1 AS First Query 的地方,我在结果表的 F_DIST_ID 中得到空值。然而,源表没有 NULL,如果我要执行:SELECT * FROM (First Query) WHERE F_DISTRIBUTION_ID IS NULL,则不会返回任何行。就好像 CREATE TABLE 子句负责引入 NULLS 而不是简单地将结果放入表中。如果我们只进行 INNER JOINS 并且源 po_distributions_curr_fct 表没有 F_DISTRIBUTION_ID 的空值,那么第一个查询如何返回 NULLS? 顺便说一句,我也同意删除那些“EXISTS”条款并按照您的指示重写它似乎是明智的。我试过了,结果一样。【参考方案5】:

我遇到了一个可能相关的问题,最初是 MINUS 的问题,当时使用了一堆复杂的视图并且存在。我将其缩小到一个可能的优化器问题 - 您可以通过使用“upper(F_DISTRIBUTION_ID) IS NULL”之类的东西来阻止优化器处理基于“F_DISTRIBUTION_ID IS NULL”的事情来解决它。

在这些情况下,几乎不可能为错误报告创建一个简化的测试用例——它可能只发生在非常特定的场景中(它不像 MINUS 那样毕竟会被完全破坏)。对于我的问题,我根本无法减少查询并且仍然会发生。

仅供参考,我的问题是一个查询,它基本上将一堆东西加入到源表中,称之为员工。我在 Employee 的主键上有一个 where 子句 - 如果我做了 where EmployeeId = foo,它将返回一个额外的行,其中不应该是空值(来自内部连接的表中的列) - 如果我这样做了a where upper(EmployeeId) = foo 那么我会得到正确的结果。显然,EmployeeId 值来自与谓词匹配的所有行中的同一单元格 - 所以这显然是一个错误。

【讨论】:

【参考方案6】:

我刚刚遇到同样的错误。并建立一个 if else 条件来获取这些值:D

表 1

A      !      B

null          35

'if else' 条件:P

select 
Case
WHEN  a."Add" >= '0' THEN to_number(a."Add" - b."Cease" )

ELSE (b."Cease")*-1
End  as "X"
from table 1

回答

-35

【讨论】:

以上是关于Oracle 减号查询。如果顶部 SQL 和底部 SQL 不包含 NULL,我如何获得带有 NULLS 的结果?的主要内容,如果未能解决你的问题,请参考以下文章

在 Redshift sql 查询中选择表的顶部/底部 50%

SQL Server 顶部与顶部和底部的关系

使用减号 oracle 优化插入查询

Oracle 判断表字段里字符串是不是含有汉字

如果所需的列值重复 [重复],则 SQL 查询以获取顶部记录

SQL CASE 语句和减号