Oracle
 sql >> Database >  >> RDS >> Oracle

Oracle Select * restituisce le righe ma Select count(1) restituisce 0

I risultati errati possono essere causati da danneggiamenti, bug e funzionalità che modificano silenziosamente le istruzioni SQL.

  1. Indice danneggiato. Molto raramente un indice viene danneggiato ei dati di un indice non corrispondono ai dati di una tabella. Ciò provoca risultati imprevisti quando il piano di query cambia e viene utilizzato un indice, ma tutto sembra normale per query diverse che utilizzano l'accesso alla tabella. A volte semplicemente ricostruire oggetti può risolvere questo problema. In caso contrario, dovrai creare un test case completamente riproducibile (inclusi i dati); pubblicalo qui o invialo a Oracle Support. Possono essere necessarie molte ore per rintracciarlo.
  2. Blocco. Molto raramente un bug può causare il fallimento delle query durante la restituzione o la modifica dei dati. Anche in questo caso, per diagnosticare questo è necessario un test case completamente riproducibile e può volerci del tempo.
  3. Funzione che cambia SQL Esistono alcuni modi per modificare in modo trasparente le istruzioni SQL. Esamina Virtual Private Database (VPD), DBMS_ADVANCED_REWRITE e SQL Translation Framework.

Per escludere il numero 3, il codice seguente mostra uno dei modi malvagi per farlo e come rilevarlo. Innanzitutto, crea lo schema e alcuni dati:

CREATE TABLE TRACKING (
  A_ID NUMBER,
  D_CODE NUMBER,
  HOD NUMBER,
  ADR_CNT NUMBER,
  TTL_CNT NUMBER,
  CREATED DATE,
  MODIFIED DATE
);
CREATE INDEX HOD_D_CODE_IDX ON TRACKING (HOD, D_CODE);
CREATE UNIQUE INDEX TRACKING_PK ON TRACKING (A_ID, D_CODE, HOD);
CREATE INDEX MOD_DATE_IDX ON TRACKING (MODIFIED);
ALTER TABLE TRACKING ADD CONSTRAINT TRACKING_PK PRIMARY KEY (A_ID, D_CODE, HOD);

insert into tracking values (1,2,3,4,5,sysdate,sysdate);
commit;

All'inizio, tutto funziona come previsto:

SQL> SELECT * FROM TRACKING;

      A_ID     D_CODE        HOD    ADR_CNT    TTL_CNT CREATED   MODIFIED
---------- ---------- ---------- ---------- ---------- --------- ---------
         1          2          3          4          5 17-JUN-16 17-JUN-16

SQL> SELECT COUNT(1) FROM TRACKING;

  COUNT(1)
----------
         1

Poi qualcuno fa questo:

begin
  sys.dbms_advanced_rewrite.declare_rewrite_equivalence(
    'april_fools',
    'SELECT COUNT(1) FROM TRACKING',
    'SELECT 0 FROM TRACKING WHERE ROWNUM = 1',
    false);
end;
/

Ora i risultati sono "sbagliati":

SQL> ALTER SESSION SET query_rewrite_integrity = trusted;

Session altered.

SQL> SELECT COUNT(1) FROM TRACKING;

  COUNT(1)
----------
         0

Questo può essere probabilmente rilevato guardando il piano di spiegazione. Nell'esempio seguente, il predicato 2 - filter(ROWNUM=1) è un indizio che qualcosa non va, poiché quel predicato non è nella query originale. A volte la sezione "Note" del piano di spiegazione ti dirà esattamente perché è stato trasformato, ma a volte fornisce solo indizi.

SQL> explain plan for SELECT COUNT(1) FROM TRACKING;

Explained.

SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------
Plan hash value: 1761840423

------------------------------------------------------------------------------------
| Id  | Operation         | Name           | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |                |     1 |     2 |     1   (0)| 00:00:01 |
|   1 |  VIEW             |                |     1 |     2 |     1   (0)| 00:00:01 |
|*  2 |   COUNT STOPKEY   |                |       |       |            |          |
|   3 |    INDEX FULL SCAN| HOD_D_CODE_IDX |     1 |       |     1   (0)| 00:00:01 |
------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter(ROWNUM=1)

15 rows selected.

(In una nota non correlata:usa sempre COUNT(*) invece di COUNT(1) . COUNT(1) è un vecchio mito che sembra una programmazione cult del cargo.)