Warning: Declaration of Suffusion_MM_Walker::start_el(&$output, $item, $depth, $args) should be compatible with Walker_Nav_Menu::start_el(&$output, $item, $depth = 0, $args = Array, $id = 0) in /DISK2/WWW/plsql.cz/www/wp-content/themes/suffusion/library/suffusion-walkers.php on line 39
Jul 302015
 

Godiva, dnes jeden krátký tip – nedávno jsme objevil mě neznámou část příkazu ANALYZE TABLE, která umožní uložit ROWID chainovaných řádků – LIST CHAINED ROWS INTO. Takže si pojďme zkusit :) Pro spuštění tohoto příkazu existuje pouze jedna jediná pre-rekvizita a tou je mít tabulku do které se to uloží (alá constrain). Tabulka se dá získat spuštěním scriptů UTLCHAIN.SQL či UTLCHN1.SQL nebo stačí založit růčo se správnou strukturou, to jest:

CREATE TABLE CHAINED_ROWS (
owner_name varchar2(30),
table_name varchar2(30),
cluster_name varchar2(30),
partition_name varchar2(30),
subpartition_name varchar2(30),
head_rowid urowid,
analyze_timestamp date
);

A ted ještě jak to hezky nafixlovat, aby to bylo vidět – velikost bloku mam 8192 (8 kb), so udělejme 10 řádků po 790ti (overhead bloku by měl být daleko menší, ale nechme si raději buffer):

CREATE TABLE TST_CHAIN (
S_DATA VARCHAR2 (3000)
) PCTFREE 0;

INSERT INTO TST_CHAIN VALUES (RPAD('A',790,'A'));
INSERT INTO TST_CHAIN VALUES (RPAD('B',790,'B'));
INSERT INTO TST_CHAIN VALUES (RPAD('C',790,'C'));
INSERT INTO TST_CHAIN VALUES (RPAD('D',790,'D'));
INSERT INTO TST_CHAIN VALUES (RPAD('E',790,'E'));
INSERT INTO TST_CHAIN VALUES (RPAD('F',790,'F'));
INSERT INTO TST_CHAIN VALUES (RPAD('G',790,'G'));
INSERT INTO TST_CHAIN VALUES (RPAD('H',790,'H'));
INSERT INTO TST_CHAIN VALUES (RPAD('I',790,'I'));
INSERT INTO TST_CHAIN VALUES (RPAD('J',790,'J'));
commit;

Zanalyzujme:

ANALYZE TABLE TST_CHAIN compute statistics;

Fajn a nyní update nějakého řádku tak, aby se nevešel do datového bloku. Dejme mu třebas 3000x písmeno ‘X’, tedy 9*790+300=10110 a to se zajisté již do bloku 8kb nevejde. So, update náhodného řádku:

UPDATE TST_CHAIN SET s_data=RPAD('X',3000,'X') 
WHERE instr(s_data,chr(ascii('A')+MOD(abs(dbms_random.random),10)))<>0;
commit;

Zanalyzujeme:

ANALYZE TABLE TST_CHAIN compute statistics;

Naprosto dle očekávání trhu 2 datové bloky – jeden původní s daty a druhý, který vznikl updatem náhodného řádku – a jeden zřetězený rádek. A nyní konečně ke zkoušené klauzuli:

ANALYZE TABLE TST_CHAIN LIST CHAINED ROWS INTO CHAINED_ROWS;

Když mrknu na vrácené rowid (AAAHQWAABAAAN6RAAH), tak mi LIST CHAINED ROWS INTO tvrdí, že jsem updatoval řádek, který je 7 v pořadí:

Což je řádek, kdy byla původně H a krásně to koresponduje s posledním písmenem rowid :-) A nyní poslední krok, jen ověření, že to opravdu byl řádek s ‘H’áčkama:

Fajn, řádek s H chybí a místo něho je poslední řádek s Xkama  :)  So, enjoy LIST CHAINED ROWS INTO. Mimochodem v dokumentaci je uvedeno, že daný příkaz funguje jen pro fanoušky metalu.. ale možná teď tak trošku kecám ;)

 Posted by at 21:12
Oct 242014
 

Kolega si kdys postěžoval, že blog moc často neaktualizuji a má pravdu ;) Nicméně dneska si ten post doslova vynutil, když mi potrápil s jedním selectem. Prvně bych chtěl všem připomenout, že to v Oracle funguje (alespon v zapatchované 11.2g) tak, že pokud Vám select vrací něco, co vypadá jako nesmysl – máte to špatně vy, ne Oracle. V mé první práci mě tohle myšlení naučil kolega, který nebyl ochoten přemýšlet o bugu Oracle dříve než za několik hodin doumání nad selectem. A já jsem tohle posunul ještě dál – tak 14 dní. Pokud jste daným selectem neztrávili alespoň 14 dní, pak není racionální přemýšlet nad tím, že je to bug Oracle. Za svůj život jsem potkal přesně 3 selecty, které vrátily špatný výsledek a byl to bug (hashjoin u subpartitions , left join na dual a to poslední si nepamauji – všechno již dávno fixnuto Oraclem). Zatímco já jsem na svůj select koukal asitak přesně bžilionkrát s tím, že “to není možné” a vždycky jsem končil přiznáním vlastní chyby a chutí ukousknout si vlastní ucho, když jsem zjistil, že to celou dobu přehlížím..

Jedinou vyjímkou, kdy bych přemýšlel nad chybou Oracle jsou ještě chvíle, kdy jste si hráli s datovým slovníkem (updaty pod sysem apod.) či je to chyba, kterou by mohlo vyvolat poškození datového bloku či indexu/objektu na mediu. Ale to je jen tak mé doporučení.

A nyní čím mi potrápil kolega (vypadalo nechtěně ačkoliv v průběhu zjišťování, v čem je zakopaná ponorka jsem ho podezíral se spousty věcí včetně VPD policy). Nejprve když mi popisoval svůj problém mluvil o constrainech a o tom, že mu “nefunguje cizí klíč”, což neznělo moc zajímavě, upřímně ;) Do chvíle, než mi ukázal výsledky těchto dvou selectů:

Jiný výsledek

Všimněte, že výsledek je pokaždý jiný (počet řádků – sloupce jsou vždy z jiné tabulky) ačkoliv jediné co jsem změnil jsou sloupce v projection – join a predikát je naprosto stejný. Takže kde, že je ta zakopaná ponorka? Většinou v takovýhle případech zkouším exekuční plány – minimálně pro svou představu a oživení nápadů:

(explain plan)

Tohle píšu doma, takže to nemá naprosto stejný exekuční plán jako to mělo u nás v práci (speciálně to dělám na 12.1c tudíš tam mám navíc krok COLLECTORU a mám naprosto jiné tabulky), nicméně jako v práci to není ničím zajímavým, zkusme to pro b.*:

Tohle již vypadá zajímavěji  – mám tam join na dvou tabulkách, ale exekuční plán jde jen do jedné tabulky. Krásná síla Oracle, který zjistí, že join na tabulku A vůbec přístup do ní není třeba. Důvodem je FK constrain. Ten zaručuje, že join by zafungoval všude, zároveň na a.id je primární klíč, který zamezuje “pronásobení řádků”.  Takže nakonec po optimaizaci zbyde jen čtení z tabulky na predikát dsk=1 a join Oracle ztransformoval na  b.album_id is not null.

Nicméně to je “co je v exekučním plánu” a úplně to nenasvěčuje, proč dva selecty vrací jiné výsledky. Takže jsem si řekl, co to udělá, když Oracle donutím si tu tabulku vzít a fyzicky je na sebe najoinovat – zkusil jsem hinty na full, use_hash. – a odnáším si z toho ponaučení: tohle se ohintovat nedá, jakmile to Oracle vykydlí hned na pravidlech, neni žádná šance na to šahat hintem. Což je věc nad kterou jsem nikdy nepřemýšlel – většinou člověk hintuje, to co drhne v exekučním plánu, nikoliv takhle od pohledu do selectu. Každopádně nejde. Co mi ale napadlo až doma je vypnout tuhle featuru kydlení pomocí hintu OPT_PARAM. Což má nevýhodu v tom, že já vlastně ani nevím, co přesně vypnout – je to nějaký ze bžilonu zkrytých parametrů. Takže jsem nahodil brutálnější hint OPTIMALIZER_FEATURE_ENABLE, který umožní zadat pouze verzi ;) A tak se stačí jen modlit, aby to nebylo hned od první verze, ale nebylo:

Fajn zanfungovalo – hned na poprvé ;) Taky jsem teda zbaběle střelil nízkou verzi Oracle. Nicméně případně hrubou silou by šlo zjistit od kdy to začne zase vracet dva řádky a pak případně udělat rozdíl hidden parametrů a tipnout si, co přesně kde je třeba vypnout. Ale to bylo jen tak pro zajímavost.

Každopádně to visí a padá na constrainu, mrknu jsem co kolega psal ve scriptech a s úsměvěm jsem na tváři objevil slovo “novalidate”. Řikám si ha, má tam bordel v datech nedodržuje FK/špinavá data a přidal constrain novalidate, takže zvalidovat:

ALTER TABLE track MODIFY CONSTRAINT fk_track ENABLE VALIDATE;
table TRACK altered

Po té, co mi tenhle příkaz nevyfuckoval, mi to začalo postupně kazit pátek :-( Každopádně po delší odmlce jsme přišli, na to, že je problém s attributem RELY na constrainu. A to je můj druhý nový poznatek pro dnešek – pokud je na constrainu RELY, tak vás následné VALIDATE nevyhodí a normálně proběhne a co horší, proběhne pak i zpětné NORELY, jinými slovy, udělá to tahle kombinace:

ALTER TABLE track MODIFY CONSTRAINT fk_track RELY;
ALTER TABLE track MODIFY CONSTRAINT fk_track ENABLE VALIDATE;
ALTER TABLE track MODIFY CONSTRAINT fk_track NORELY;

Pekelné ;) Takže je třebas to zpravit:

ALTER TABLE track MODIFY CONSTRAINT fk_track ENABLE NOVALIDATE;
ALTER TABLE track MODIFY CONSTRAINT fk_track ENABLE VALIDATE;

SQL Error: ORA-02298: není možno zkontrolovat platnost (AZOR6.FK_TRACK) - nebyly nalezeny nadřízené klíče
02298. 00000 - "cannot validate (%s.%s) - parent keys not found"
*Cause: an alter table validating constraint failed because the table has
child records.
*Action: Obvious

A od téhle chvíle již to není vůbec zajímavé, v  tabulce je nějaký bordel, který je třebas promazat nebo se rozloučit sconstrainem. Takže asi tak, hébičkám zdar a pěkný víkend.

 Posted by at 21:35
Dec 192011
 

Po delší době jsem se podíval do statistik tohoto blogu a s potěšením zjistil, že již tento blog navšívilo krásných 3000+ unikátních návštěvníků, což možná nezní jako ohromné číslo, ale i tak to potěší (a děkuji i za maily).

Již dvakrát jsem vytáhl z hledaných výrazů na této stránce ty, které jste zadaly, ale patrně jste neobdrželi uspokojivou odpověď, což se snažím postupně napravit. Dnes vybírám dalších 5 “dotazů”:

1) oracle for loop from min to max

PL/SQL umožňuje (narozdíl od většiny jazyků) loopovat pouze po jednotkovém kroku a v případě, že je potřeba nějaký jiný krok je třeba si pomoci vynásobením interační proměnné jak doporučuje dokumentace Oracle.  Samotný loop pak vypadá takto:

BEGIN
FOR I IN 1..100 LOOP
dbms_output.put_line(i);
END LOOP;
END;

Což je nejjednodušší forma cyklu FOR (bez labelu atd.), o proti jiným jazykům lze zadat slovo REVERSE, které zajístí, že cyklus bude interovat odzadu.
2) oracle jak zjistím velikost tabulky

To je trošku tricky otázka – záleží na tom, co je myšleno velikostí tabulky – od počtu řádků, počtu datových bloků až po počet využitých datových bloků až po velikost tabulky po shrinknutí. Kolik aktuálně tabula zabírá lze zjistit z *_extends:

SELECT
segment_name           tabulka,
SUM(bytes)/1024 table_size_kb
FROM
dba_extents
WHERE
segment_name='EMPLOYEES'
GROUP BY segment_name;

3) Zjištění jestli tabulka existuje:

Pomocí selectu do systémových view : ALL_TABLES, USER_TABLES případne DBA_TABLES  (psáno *_TABLES) a jiných, například ALL_TAB_COLS (tabulka má alespoň jeden sloupec), nebo přímo do core tabulky sys.tab$ / sys.obj$

SELECT decode(count(table_name),0,'neexistuje',1,'existuje') FROM ALL_TABLES WHERE TABLE_NAME LIKE '%MY_TABLE%' AND OWNER='AZOR';

Co je asi duležité připomenout je, že pokud jste si to dvojuvozovkami nevyžádali, pak jména v systémových tabulkách jsou vždy velká.

Nicméně způsobů jak zjistit jestli existuje tabulka je spousta – od příkazu desc|ribe až po prostou zkoušku selectu do ní.

4) zjisteni scn

Možnost první nepřesné SCN (mapování na čas s periodou 3sekundy, do historie 180h):
SELECT TIMESTAMP_TO_SCN(SYSTIMESTAMP) FROM DUAL;
Možnost druhá, přesné SCN:
SELECT DBMS_FLASHBACK.GET_SYSTEM_CHANGE_NUMBER FROM DUAL;
Možnost třetí, přesné SCN:
SELECT CURRENT_SCN FROM V$DATABASE;

možností je pochopitelně více..

5) zjištění aktuální minuty
Například pomocí
SELECT TO_CHAR(sysdate,'MI') FROM DUAL;

 Posted by at 20:35
Mar 022011
 

Často se setkávám s použitím SQLERRM a SQLCODE a to třeba nejen u kolegů, ale často i v některých knihách u Oracle.  Použití je jednoduché – SQLERRM vrací poslední chybovou hlášku (bohužel nelze návrat z této funkce použít všude, kde by by měl jít použít výraz) a SQLCODE vrací číslo chyby Oracle.  Pokud to situace dovolí snažím se vždy tlačit na použití DBMS_UTILITY.FORMAT_ERROR_BACKTRACE – tedy přímo výpis ze stacku (TOAD nebo SQL Developer například poskytuje tento výstup) a výpisem volaných procedur/funkcí včetně řádků kódu na kterých došlo v vyjímce.  Pokud logujete (a logujete, že? logovat se musí) pak je dobré nechat si v případě chyby zapsat do logu i tuto informaci, která je velice užitečná při odstraňovní chyby – speciálně, když jedná o runtime chybu na základě hodnoty některých dat a je problematické danou situaci znovu navodit.

  • DBMS_UTILITY.FORMAT_ERROR_BACKTRACE - funkce vrací textový výpis chyby v VARCHAR2
 Posted by at 23:58
Feb 262011
 

Pokud potřebujete zjistit aktuální schéma svoje či uživatele, který pouští váš PL/SQL kód,  je možné použít

select sys_context( 'userenv', 'current_schema' ) from dual;

Zjištění aktuálního schématu se velice hodí pokud použiváte  ve svém programu authid user v některé z funkcí.

 Posted by at 20:52