Oracle隐式转换是否依赖于已连接的表或视图

时间:2013-01-30 06:48:03

标签: oracle implicit-conversion

我现在遇到了一个奇怪的问题。查询本身是巨大的,所以我不打算在这里发布(我可以发布,但万一有人需要看到)。现在我有一个表TABLE1,其中包含CHAR(1)列COL1。此表列作为查询的一部分进行查询。当我过滤此列的记录集时,我说:

WHERE TAB1.COL1=1

这样查询运行并返回一个非常大的结果集。我最近更新了其中一个子查询以加快查询速度。但在此之后,当我写WHERE TAB1.COL1 = 1时,它不会返回任何内容,但如果我将其更改为WHERE TAB1.COL1 ='1',它会给我我需要的记录。注意WHERE子句带引号和w / o它们。所以为了更清楚,在更新其中一个子查询之前,我没有必要使用引号来检查COL1值,但在更新后我必须这样做。甲骨文的哪些特性是我不知道的?

编辑:我发布了查询的两个版本,以防有人发现它有用

版本1:

SELECT p.ssn,
  pss.pin,
  pd.doc_number,
  p.surname,
  p.name,
  p.patronymic,
  to_number(p.sex, '9') as sex,
  citiz_c.short_name citizenship,
  p.birth_place,
  p.birth_day as birth_date,
  coun_c.short_name as country,
  di.name as leg_city,
  trim( pa.settlement
  || ' '
  || pa.street) AS leg_street,
  pd.issue_date,
  pd.issuing_body,
  irs.irn,
  irs.tpn,
  irs.reg_office,
  to_number(irs.insurer_type, '9') as insurer_type,
  TO_CHAR(sa.REG_CODE)
  ||CONVERT_INT_TO_DOUBLE_LETTER(TO_NUMBER(SUBSTR(TO_CHAR(sa.DOSSIER_NR, '0999999'), 2, 3)))
  ||SUBSTR(TO_CHAR(sa.DOSSIER_NR, '0999999'), 5, 4) CONVERTED_SSN_DOSSIER_NR,
  fa.snr
FROM
  (SELECT pss_t.pin,
    pss_t.ssn
  FROM EHDIS_INSURANCE.pin_ssn_status pss_t
  WHERE pss_t.difference_status < 5
  ) pss
INNER JOIN SSPF_CENTRE.file_archive fa
ON fa.ssn = pss.ssn
INNER JOIN SSPF_CENTRE.persons p
ON p.ssn = fa.ssn
INNER JOIN
  (SELECT pd_2.ssn,
    pd_2.type,
    pd_2.series,
    pd_2.doc_number,
    pd_2.issue_date,
    pd_2.issuing_body
  FROM

--The changed subquery starts here
    (SELECT ssn,
      MIN(type) AS type
    FROM SSPF_CENTRE.person_documents
    GROUP BY ssn
    ) pd_1
  INNER JOIN SSPF_CENTRE.person_documents pd_2
  ON pd_2.type       = pd_1.type
  AND pd_2.ssn       = pd_1.ssn
  ) pd
--The changed subquery ends here


ON pd.ssn = p.ssn
INNER JOIN SSPF_CENTRE.ssn_archive sa
ON p.ssn = sa.ssn
INNER JOIN SSPF_CENTRE.person_addresses pa
ON p.ssn = pa.ssn
INNER JOIN
  (SELECT i_t.irn,
    irs_t.ssn,
    i_t.tpn,
    i_t.reg_office,
    (
    CASE i_t.insurer_type
      WHEN '4'
      THEN '1'
      ELSE i_t.insurer_type
    END) AS insurer_type
  FROM sspf_centre.irn_registered_ssn irs_t
  INNER JOIN SSPF_CENTRE.insurers i_t
  ON i_t.irn                   = irs_t.new_irn
  OR i_t.old_irn               = irs_t.old_irn
  WHERE irs_t.is_registration IS NOT NULL
  AND i_t.is_real             IS NOT NULL
  ) irs ON irs.ssn             = p.ssn
LEFT OUTER JOIN SSPF_CENTRE.districts di
ON di.code = pa.city
LEFT OUTER JOIN SSPF_CENTRE.countries citiz_c
ON p.citizenship = citiz_c.numeric_code
LEFT OUTER JOIN SSPF_CENTRE.countries coun_c
ON pa.country_code  = coun_c.numeric_code
WHERE pa.address_flag = '1'--Here's the column value with quotes
AND fa.form_type    = 'Q3';

版本2:

SELECT p.ssn,
  pss.pin,
  pd.doc_number,
  p.surname,
  p.name,
  p.patronymic,
  to_number(p.sex, '9') as sex,
  citiz_c.short_name citizenship,
  p.birth_place,
  p.birth_day as birth_date,
  coun_c.short_name as country,
  di.name as leg_city,
  trim( pa.settlement
  || ' '
  || pa.street) AS leg_street,
  pd.issue_date,
  pd.issuing_body,
  irs.irn,
  irs.tpn,
  irs.reg_office,
  to_number(irs.insurer_type, '9') as insurer_type,
  TO_CHAR(sa.REG_CODE)
  ||CONVERT_INT_TO_DOUBLE_LETTER(TO_NUMBER(SUBSTR(TO_CHAR(sa.DOSSIER_NR, '0999999'), 2, 3)))
  ||SUBSTR(TO_CHAR(sa.DOSSIER_NR, '0999999'), 5, 4) CONVERTED_SSN_DOSSIER_NR,
  fa.snr
FROM
  (SELECT pss_t.pin,
    pss_t.ssn
  FROM EHDIS_INSURANCE.pin_ssn_status pss_t
  WHERE pss_t.difference_status < 5
  ) pss
INNER JOIN SSPF_CENTRE.file_archive fa
ON fa.ssn = pss.ssn
INNER JOIN SSPF_CENTRE.persons p
ON p.ssn = fa.ssn
INNER JOIN

 --The changed subquery starts here
 (SELECT ssn,
    type,
    series,
    doc_number,
    issue_date,
    issuing_body
  FROM
    (SELECT ssn,
      type,
      series,
      doc_number,
      issue_date,
      issuing_body,
      ROW_NUMBER() OVER (partition BY ssn order by type) rn
    FROM SSPF_CENTRE.person_documents
    )
  WHERE rn = 1
  ) pd --
 --The changed subquery ends here

ON pd.ssn = p.ssn
INNER JOIN SSPF_CENTRE.ssn_archive sa
ON p.ssn = sa.ssn
INNER JOIN SSPF_CENTRE.person_addresses pa
ON p.ssn = pa.ssn
INNER JOIN
  (SELECT i_t.irn,
    irs_t.ssn,
    i_t.tpn,
    i_t.reg_office,
    (
    CASE i_t.insurer_type
      WHEN '4'
      THEN '1'
      ELSE i_t.insurer_type
    END) AS insurer_type
  FROM sspf_centre.irn_registered_ssn irs_t
  INNER JOIN SSPF_CENTRE.insurers i_t
  ON i_t.irn                   = irs_t.new_irn
  OR i_t.old_irn               = irs_t.old_irn
  WHERE irs_t.is_registration IS NOT NULL
  AND i_t.is_real             IS NOT NULL
  ) irs ON irs.ssn             = p.ssn
LEFT OUTER JOIN SSPF_CENTRE.districts di
ON di.code = pa.city
LEFT OUTER JOIN SSPF_CENTRE.countries citiz_c
ON p.citizenship = citiz_c.numeric_code
LEFT OUTER JOIN SSPF_CENTRE.countries coun_c
ON pa.country_code  = coun_c.numeric_code
WHERE pa.address_flag = 1--Here's the column value without quotes
AND fa.form_type    = 'Q3';

我在两个查询中都为已更改的子查询和WHERE子句分别添加了注释。两个版本的子查询都返回相同的结果,其中一个只是速度较慢,这就是我决定更新它的原因。

2 个答案:

答案 0 :(得分:2)

使用最简单的例子,我无法在11.2.0.3.0或11.2.0.1.0上重现您的问题。

SQL> create table tmp_test ( a char(1) );

Table created.

SQL> insert into tmp_test values ('1');

1 row created.

SQL> select *
  2    from tmp_test
  3   where a = 1;

A
-
1

如果我然后在表格中插入非数字值,我可以确认Chris的评论“Oracle会将tab1.col1 = 1重写为to_number(tab1.col1) = 1”,这意味着您只在列中包含数字字符

SQL> insert into tmp_test values ('a');

1 row created.

SQL> select *
  2    from tmp_test
  3   where a = 1;
ERROR:
ORA-01722: invalid number



no rows selected

如果您对跟踪此问题感兴趣,则应逐渐降低查询的复杂性,直到找到最小的,可重现的示例为止。 Oracle可以预先计算要在JOIN中使用的转换,因为您的查询很复杂,似乎可以解释正在发生的事情。

Oracle explicitly recommends against using implicit conversion因此,根本不使用它更明智;正如你发现的那样。首先,无法保证您的索引将被正确使用。

  

Oracle建议您指定显式转换,而不是依赖隐式或自动转换,原因如下:

     
      
  • 使用显式数据类型转换函数时,SQL语句更容易理解。

  •   
  • 隐式数据类型转换可能会对性能产生负面影响,尤其是当列值的数据类型转换为常量的数据类型而不是相反时。

  •   
  • 隐式转换取决于它发生的上下文,并且在每种情况下可能无法以相同的方式工作。例如,从datetime值到VARCHAR2值的隐式转换可能会返回意外的年份,具体取决于NLS_DATE_FORMAT的值   参数。

  •   
  • 隐式转换的算法可能会在软件版本和Oracle产品之间发生变化。显式转换的行为更具可预测性。

  •   

如果您在列中只有数字字符,我强烈建议将其更改为NUMBER(1)列,我总是建议进行显式转换,以避免长时间内出现很多痛苦。

答案 1 :(得分:1)

没有实际查询就很难分辨。我期望的是TAB1.COL1在重构之前和之后在某种程度上是不同的。

候选人差异是数字与CHAR(1)与CHAR(x> 1)对比VARCHAR2

很容易在子查询中引入这样的差异,在子查询中连接两个在连接列中具有不同类型的表,并在子查询中返回不同的列。

要查找该问题,您可能需要检查查询的确切数据类型。不知道现在该怎么做..但一个想法是将它放在视图中并使用sqlplus desc。