A számított oszlopok használata

Bevezetés

A számított oszlopok csak virtuális oszlopok, amelyek tartalma egy kifejezés eredménye. Általában a táblázat többi oszlopán alapuló adatok tárolására szolgálnak. A kifejezés tartalmazhatja a táblázat más, nem számított oszlopait, konstansokat, operátorokat és függvényeket, de egy számított oszlophoz nem adható meg kifejezésként lekérdezés.

Mivel ezek “virtuális” oszlopok, nem tárolódnak a lemezen, mint a táblázat többi része. Valójában nem tárolódnak, hanem kiszámításra kerülnek minden alkalommal, amikor az oszlopot egy lekérdezésben elérjük. Mint látni fogjuk, bizonyos korlátozásokkal rákényszeríthetjük az SQL Servert, hogy az oszlopot a táblázatban tárolja (“tartósítsa”).

A számított oszlopok működését legjobban a minták segítségével érthetjük meg. A végén talál egy fájlt, amely a cikkben használt összes szkriptet tartalmazza, és a szövegben mutatunk belőlük néhányat a magyarázatok illusztrálására. Kezdetnek két táblázatot hozunk létre: az elsőben a számlák adatait tároljuk, a másikban pedig az említett számlák részletező sorait. A szkriptfájlban talál néhány beillesztést is a mintaadatok létrehozásához.

CREATE TABLE invoices( id_invoice INT PRIMARY KEY IDENTITY , customer_name VARCHAR(25));CREATE TABLE detail_lines( id_detail INT PRIMARY KEY IDENTITY , id_invoice_detail INT , product VARCHAR(30) , unit_price MONEY , quantity INT , FOREIGN KEY (id_invoice_detail) REFERENCES invoices (id_invoice));

A számított oszlop létrehozásának módja ugyanaz, mint ahogyan más oszlopokat is létrehoznánk egy táblázatban, CREATE TABLE vagy ALTER TABLE utasítással. Egy számított oszlop esetében az oszlop adattípusát azzal a kifejezéssel helyettesítjük, amelyet az oszlop tartalmának kinyerésére fogunk használni. A szintaxis a következő: az oszlop neve, majd az “as” kulcsszó, végül a kifejezés. Hozzunk létre egy számított oszlopot a detail_line táblában a sor végösszegének tárolására, amelyet az unit_price és a quantity szorzatával számolunk ki.

ALTER TABLE detail_lines ADD total_amount AS unit_price * quantity;

Számított oszlop ellenőrzése

Többféleképpen is meggyőződhetünk arról, hogy egy oszlop valóban számított oszlop. Az egyik a columnproperty() függvény használata az “IsComputed” tulajdonság megadásával.

SELECT COLUMNPROPERTY(OBJECT_ID('dbo.detail_lines'),'total_amount','IsComputed')

Egy másik módja a számított oszlopokra vonatkozó információk megszerzésének a sys.computed_columns rendszernézet segítségével. Ez a nézet a sys.columns nézet kiterjesztése. Ez azt jelenti, hogy a sys.computed_columns a sys.columns nézet összes oszlopát örökli, és további, az adott oszloptípusra jellemző oszlopokat is hozzáad. A cikk során látni fogunk néhány oszlopot ebben a nézetben, mivel a számított oszlopok különböző jellemzőit látjuk. Egyelőre elég annyit tudni, hogy ez a nézet csak a számított oszlopokat mutatja, és rendelkezik egy is_computed nevű oszloppal, amely megmondja, hogy az oszlop számított-e vagy sem. Nyilvánvaló, hogy ennek a nézetnek az összes rekordja egyes lesz ebben az oszlopban.

SELECT name, is_computed FROM sys.computed_columns;

Mivel az oszlop tartalma minden alkalommal kiszámításra kerül, amikor az oszlopra egy lekérdezésben hivatkozunk, a tartalma mindig frissül. A kifejezésben szereplő oszlopok bármilyen változása automatikusan megjelenik az oszlop értékében. Erről meggyőződhetünk, ha megváltoztatjuk a mennyiséget a minta részletes_vonalak táblázatának egyik nyilvántartásában, és ellenőrizzük az eredményt.

UPDATE detail_lines SET quantity = 4 WHERE product = 'Cup'SELECT product, unit_price, quantity, total_amount FROM detail_lines WHERE product = 'Cup'

Egy lépés előre

Amit eddig a számított oszlopokkal kapcsolatban láttunk, az nagyon egyszerű, mivel csak más oszlopokkal való számítások elvégzéséről van szó. A számított oszlopok kifejezése azonban tartalmazhat függvényeket, mind a T-SQL szabványos függvényeket, mind a felhasználó által definiált függvényeket (UDF). Így ezeknek az oszlopoknak a funkcionalitása sokkal tovább bővíthető.

Lássunk erre egy példát. Hozzáadunk a számlák táblához egy számított oszlopot, amely kiszámítja a számla teljes összegét. Ehhez meg kell szereznünk a számla számát, és le kell kérdeznünk a detail_lines táblát, hogy összegezzük a teljes_összeget minden olyan rekordból, amely az adott számlaazonosítóval rendelkezik. A legjobb megoldás erre egy olyan függvény használata, amely paraméterként megkapja a számla azonosítóját, és visszaadja az összeget. Ezután létre kell hoznunk az oszlopot, amely ezt a függvényt használja.

CREATE FUNCTION fn_total_invoice (@invoice_number INT)RETURNS MONEYASBEGIN DECLARE @total MONEY SELECT @total=SUM(total_amount) FROM detail_lines WHERE id_invoice_detail = @invoice_number RETURN @totalENDALTER TABLE invoices ADD total_invoice AS dbo.fn_total_invoice(id_invoice)

Ellenőrizhetjük, hogy ez az oszlop helyesen működik-e, ha új rekordot adunk hozzá a detail_lines táblához, így a total_billnek változnia kell.

INSERT INTO detail_lines (id_invoice_detail,product,unit_price, quantity) VALUES (2,'Cup',9.90,1)SELECT id_invoice, customer_name, total_invoice FROM invoices WHERE id_invoice=2

Az oszlop módosítása

Előfordulhat olyan helyzet, hogy módosítani kell egy számított oszlopot. Ez sajnos nem lehetséges. A módosításhoz törölni kell az oszlopot, és az új kifejezéssel újra létrehozni.

Abban az esetben, ha a számított oszlop külső függvényt használ, akkor ezt a függvényt nem módosíthatjuk. Ha megpróbáljuk, hibaüzenetet kapunk arra vonatkozóan, hogy ez a függvény a táblához kapcsolódik. A függvény módosításához törölni kell az oszlopot, el kell végezni a függvény módosítását, végül újra létre kell hozni az oszlopot a függvény új verziójával.

Az oszlop definícióját a sys.computed_columns nézetben a “definition” oszlopnál kaphatjuk meg.

SEECT name, definition FROM sys.computed_columns

Számított oszlop tárolása

Mint már említettük, ezek az oszlopok “virtuálisak”, tehát fizikailag nem tárolódnak a táblázatban. Van azonban lehetőség arra, hogy kikényszerítsük a számítás fizikai tárolását a táblában, ezt nevezzük az oszlop “tartósításának”. Ez javíthatja a SELECT utasítások teljesítményét, mivel így elkerülhető, hogy minden egyes hivatkozáskor el kelljen végezni az oszlop számítását.

Ezenkívül az oszlop tartósításához az oszlop létrehozásához használt kifejezésnek “determinisztikusnak” kell lennie. Ahogy a Microsoft honlapján olvashatjuk, “a determinisztikus függvények mindig ugyanazt az eredményt adják vissza minden alkalommal, amikor egy adott bemeneti értékkészlettel és az adatbázis azonos állapotával hívják meg őket”. (https://docs.microsoft.com/en-us/sql/relational-databases/user-defined-functions/deterministic-and-nondeterministic-functions?view=sql-server-2017). Ha tudni akarjuk, hogy az SQL Server egy számított oszlop kifejezését determinisztikusnak tekinti-e vagy sem, akkor a columnproperty() függvényt használhatjuk az “IsDeterministic” tulajdonsággal.

SELECT COLUMNPROPERTY(OBJECT_ID('dbo.detail_lines'),'total_amount','IsDeterministic')SELECT COLUMNPROPERTY(OBJECT_ID('dbo.invoices'),'total_invoice','IsDeterministic')

Ha az oszlop definíciója egy felhasználó által definiált függvény, akkor azt is ellenőrizhetjük, hogy maga a függvény determinisztikus-e vagy sem. Ehhez az objectproperty() függvényt kell használnunk az IsDeterministic tulajdonsággal.

SELECT OBJCETPROPERTY(OBJECT_ID('dbo.fn_total_invoice'),'IsDeterministic')

Mint a lekérdezésekből látható, az első példa oszlopát, amelyben a részlet teljes árát számoljuk ki, determinisztikusnak tekintjük. A számla teljes árát kiszámító függvényt azonban nemdeterminisztikusnak tekintjük. Így a detail_table táblában csak a total_price oszlopot lehet tárolni.

ALTER TABLE detail_lines DROP COLUMN total_amount;ALTER TABLE detail_lines ADD total_amount AS unit_price * quantity PERSISTED;

Ismét a sys.computed_columns nézetben láthatjuk az is_persisted mezőt, amely jelzi, hogy az oszlop perzisztens vagy nem perzisztens a táblában.

Indexek számított oszlopokkal

Lehetőség van számított oszlopok használatára indexekben, bár azoknak több követelménynek is meg kell felelniük:

  • Tulajdonjog:
  • Determinizmus: A számított oszlop definíciójában használt összes függvénynek ugyanazon felhasználó tulajdonában kell lennie, mint a táblának.
  • Determinizmus: A számított oszlopnak determinisztikusnak kell lennie. Továbbá, ha az oszlop CLR-kifejezéseket tartalmaz, akkor a determinisztikusság mellett az oszlopnak perzisztensnek is kell lennie.
  • Pontosság: A számított oszlop kifejezésének pontosnak kell lennie. Ez azt jelenti, hogy nem lehet “float” vagy “real” adattípusú. A definícióban sem használhat ilyen típusú adatokat. Ez a tulajdonság a columnproperty() függvénnyel ellenőrizhető az IsPrecise tulajdonság megadásával.
  • Adattípus: A számított oszlop nem lehet text, ntext vagy image típusú. Továbbá, ha a kifejezés image, ntext, text, varchar (max), nvarchar (max), varbinary (max) vagy xml adattípusokat tartalmaz, csak akkor használható, ha a kifejezésből eredő adattípus indexben megengedett.

Ezeken a megfontolásokon kívül az oszlop létrehozásához használt kapcsolatoknak és az index létrehozásához használt kapcsolatoknak bizonyos konfigurációkkal kell rendelkezniük ahhoz, hogy ezeket a műveleteket végre lehessen hajtani.

A számított oszlop létrehozásához használt kapcsolatnak aktívnak kell lennie az ANSI_NULLS opciónak. Ez a columnproperty() függvénnyel ellenőrizhető az IsAnsiNullsOn tulajdonság megadásával.

Az index létrehozására szolgáló kapcsolatnak, valamint az indexet befolyásoló rekordok beszúrását, frissítését és törlését végző kapcsolatoknak az ANSI_NULLS, ANSI_PADDING, ANSI_WARNINGS, ARITHABORT, CONCAT_NULL_YIELDS_NULL, QUOTED_IDENTIFIER opciónak aktívnak kell lennie. Ezenkívül a NUMERIC_ROUNDABORT opciót ki kell kapcsolni.

Utolsó szempontok

Végezetül áttekintünk néhány további szempontot, amelyeket a számított oszlopok helyes használatához szükséges tudni.

Nyilvánvaló, hogy a számított oszlopok nem frissíthetők, és nem szerepelhetnek egy INSERT művelet értéklistájában. Bár a számított oszlopok a select utasítás eredménylistájának részét képezhetik, de a WHERE, ORDER BY vagy mindazokban a záradékokban is használhatók, amelyekben egy kifejezés szerepelhet.

SELECT product, unit_price, quantity, total_amount FROM detail_lines WHERE total_amount > 10 ORDER BY total_amount

A fentiek ellenére a számított oszlop nem használható DEFAULT vagy FOREIGN KEY megkötés definíciójában. NOT NULL megkötésdefinícióval sem lehet.

Másrészt a kiszámított oszlopok PRIMARY KEY vagy UNIQUE megkötések részeként is használhatók. Ehhez a számított oszlop definíciójának determinisztikus kifejezésnek kell lennie.

Következtetés

A számított oszlopok használata bizonyos helyzetekben nagyon hasznos lehet. Alaposan meg kell vizsgálni, hogy hol használjuk őket, mert korlátozásokkal rendelkeznek, különösen az index létrehozására és a perzisztenciájukra vonatkozóan. Ez még csak a kezdet. Nyugodtan próbáljon ki új dolgokat, és kísérletezzen a számított oszlopokkal, hogy új lehetőségeket találjon.

Vélemény, hozzászólás?

Az e-mail-címet nem tesszük közzé.