Det som är fel med markörer är att de ofta missbrukas, både i Oracle
och i MS SQL
.
Markören är till för att hålla en stabil resultatuppsättning som du kan hämta rad för rad. De skapas implicit när din fråga körs och stängs när den är klar.
Att behålla en sådan resultatuppsättning kräver naturligtvis vissa resurser:locks
, locks
, memory
, även disk space
.
Ju snabbare dessa resurser frigörs, desto bättre.
Att hålla en markör öppen är som att hålla en kylskåpsdörr öppen
Du gör det inte i timmar utan att det är nödvändigt, men det betyder inte att du aldrig ska öppna ditt kylskåp.
Det betyder att:
- Du får inte dina resultat rad för rad och summerar dem:du anropar
SQL
sSUM
istället. - Du kör inte hela frågan och får de första resultaten från markören:du lägger till ett
rownum <= 10
skick för din fråga
osv.
När det gäller Oracle
, att bearbeta dina markörer i en procedur kräver ökända SQL/PLSQL context switch
vilket händer varje gång du får ett resultat av en SQL
fråga ut från markören.
Det innebär att skicka stora mängder data mellan trådar och synkronisera trådarna.
Detta är en av de mest irriterande sakerna i Oracle
.
En av de mindre uppenbara konsekvenserna av det beteendet är att utlösare i Oracle bör undvikas om möjligt.
Skapa en utlösare och anropa en DML
funktionen är lika med att öppna markören genom att välja de uppdaterade raderna och anropa triggerkoden för varje rad på denna markör.
Bara existensen av utlösaren (även den tomma utlösaren) kan bromsa en DML
operation 10 times
eller mer.
Ett testskript på 10g
:
SQL> CREATE TABLE trigger_test (id INT NOT NULL)
2 /
Table created
Executed in 0,031 seconds
SQL> INSERT
2 INTO trigger_test
3 SELECT level
4 FROM dual
5 CONNECT BY
6 level <= 1000000
7 /
1000000 rows inserted
Executed in 1,469 seconds
SQL> COMMIT
2 /
Commit complete
Executed in 0 seconds
SQL> TRUNCATE TABLE trigger_test
2 /
Table truncated
Executed in 3 seconds
SQL> CREATE TRIGGER trg_test_ai
2 AFTER INSERT
3 ON trigger_test
4 FOR EACH ROW
5 BEGIN
6 NULL;
7 END;
8 /
Trigger created
Executed in 0,094 seconds
SQL> INSERT
2 INTO trigger_test
3 SELECT level
4 FROM dual
5 CONNECT BY
6 level <= 1000000
7 /
1000000 rows inserted
Executed in 17,578 seconds
1.47
sekunder utan trigger, 17.57
sekunder med en tom utlösare gör ingenting.