3

私は今、奇妙な問題に直面しています。クエリ自体は巨大なので、ここに投稿するつもりはありません (ただし、誰かが見る必要がある場合に備えて投稿できます)。これで、CHAR(1) カラム COL1 を持つテーブル TABLE1 ができました。このテーブル列は、クエリの一部としてクエリされます。この列のレコードセットをフィルターすると、次のようになります。

WHERE TAB1.COL1=1

このようにしてクエリが実行され、非常に大きな結果セットが返されます。最近、クエリを高速化するためにサブクエリの 1 つを更新しました。しかし、この後、 WHERE TAB1.COL1=1 と書いても何も返されませんが、 WHERE TAB1.COL1='1' に変更すると、必要なレコードが得られます。引用符付きの WHERE 句と引用符なしの WHERE 句に注意してください。より明確にするために、サブクエリの1つを更新する前に、COL1値をチェックするために引用符を付ける必要はありませんでしたが、更新後には必要です。私が知らない Oracle の機能は何ですか?

編集:誰かが役に立つと思うかもしれない場合に備えて、クエリの tw バージョンを投稿しています

バージョン 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 句にコメントを分けました。どちらのバージョンのサブクエリも同じ結果を返します。そのうちの 1 つは単に遅いため、更新することにしました。

4

2 に答える 2

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

次に、数値以外の値をテーブルに挿入すると、クリスのコメント「オラクルは次のよう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は暗黙的な変換を使用しないことを明示的に推奨しているため、まったく使用しない方が賢明です。あなたが見つけているように。まず、インデックスが正しく使用されるという保証はありません。

次の理由から、暗黙的または自動変換に依存するのではなく、明示的な変換を指定することをお薦めします:

  • 明示的なデータ型変換関数を使用すると、SQL ステートメントが理解しやすくなります。

  • 暗黙的なデータ型変換は、特に列値のデータ型が定数のデータ型に変換される場合、逆ではなく、パフォーマンスに悪影響を与える可能性があります。

  • 暗黙的な変換は、それが発生するコンテキストに依存し、すべての場合で同じように機能するとは限りません。たとえば、日時値から VARCHAR2 値への暗黙的な変換では、 NLS_DATE_FORMAT パラメータの値によっては予期しない年が返される場合があります。

  • 暗黙的な変換のアルゴリズムは、ソフトウェア リリース間および Oracle 製品間で変更される可能性があります。明示的な変換の動作はより予測可能です。

列に数字しかない場合は、これを NUMBER(1) 列に変更することを強くお勧めします。長期的には多くの苦痛を避けるために、明示的な変換を常にお勧めします。

于 2013-01-30T09:15:39.333 に答える
1

実際のクエリなしではわかりにくいです。私が期待するのは、リファクタリングの前後で TAB1.COL1 が何らかの形で異なることです。

候補の違いは、Number と CHAR(1) と CHAR(x>1) と VARCHAR2 です。

このような違いは、結合列で異なる型を持つ 2 つのテーブルを結合し、サブクエリで異なる列を返すサブクエリで簡単に導入できます。

その問題を突き止めるには、クエリの正確なデータ型を確認することをお勧めします。今それを行う方法がわからない..しかし、アイデアはそれをビューに入れて、それにsqlplus descを使用することです。

于 2013-01-30T08:19:09.930 に答える