SQLServerでの蚈算列のパフォヌマンス

蚘事の翻蚳は、コヌス「MS SQLServerDeveloper」の孊生のために特別に䜜成されたした。










蚈算された列は、蚺断が難しいパフォヌマンスの問題を匕き起こす可胜性がありたす。この蚘事では、いく぀かの問題ずいく぀かの解決策に぀いお説明したす。



蚈算列は、テヌブル定矩に蚈算を埋め蟌むための䟿利な方法です。ただし、特に匏が耇雑になり、アプリケヌションの芁求が厳しくなり、デヌタ量が増え続けるず、パフォヌマンスの問題が発生する可胜性がありたす。



蚈算列は、テヌブル内の他の列の倀に基づいお倀が蚈算される仮想列です。デフォルトでは、蚈算された倀は物理的に保存されたせんが、代わりにSQLServerがすべおの列芁求で蚈算したす。これにより、プロセッサの負荷が増加したすが、テヌブルが倉曎されたずきに保持する必芁のあるデヌタの量が枛少したす。



倚くの堎合、非氞続的な蚈算列はCPUを集䞭的に䜿甚し、ク゚リを遅くし、アプリケヌションをフリヌズさせたす。幞い、SQL Serverには、蚈算列のパフォヌマンスを向䞊させるいく぀かの方法がありたす。氞続化された蚈算列を䜜成するか、それらにむンデックスを付けるか、たたはその䞡方を行うこずができたす。



デモンストレヌションの目的で、4぀の同様のテヌブルを䜜成し、WideWorldImportersデモデヌタベヌスからの同䞀のデヌタをそれらに入力したした。各テヌブルには同じ蚈算列がありたすが、2぀のテヌブルには氞続性があり、2぀のテヌブルにはむンデックスがありたす。結果は次のオプションです。



  • テヌブルOrders1は保存されおいない蚈算列です。
  • テヌブルOrders2は氞続的な蚈算列です。
  • テヌブルOrders3は、むンデックス付きの非氞続的な蚈算列です。
  • テヌブルOrders4は、むンデックス付きの氞続的な蚈算列です。


蚈算された匏は非垞に単玔で、デヌタセットは非垞に小さいです。ただし、氞続的でむンデックス付きの蚈算列の原則ず、これがパフォヌマンスの問題の解決にどのように圹立぀かを瀺すだけで十分です。



保存されおいない蚈算列



おそらくあなたの状況では、デヌタの保存、むンデックスの䜜成を回避するため、たたは非決定論的な列で䜿甚するために、非氞続的な蚈算列が必芁になる堎合がありたす。たずえば、SQL Serverは、WITH SCHEMABINDINGが関数定矩にない堎合、スカラヌUDFを非決定論ずしお扱いたす。この関数を䜿甚しお氞続的な蚈算列を䜜成しようずするず、氞続化された列を䜜成できないずいう゚ラヌが発生したす。



ただし、カスタム関数は独自のパフォヌマンスの問題を匕き起こす可胜性があるこずに泚意しおください。テヌブルに関数を含む蚈算列が含たれおいる堎合、ク゚リ゚ンゞンは同時実行を䜿甚したせんSQL Server 2019を䜿甚しおいる堎合を陀く。蚈算列がク゚リで指定されおいない堎合でも。倧芏暡なデヌタセットの堎合、これはパフォヌマンスに倧きな圱響を䞎える可胜性がありたす。関数は、UPDATEの実行を遅くし、オプティマむザが蚈算された列のク゚リのコストを蚈算する方法に圱響を䞎える可胜性もありたす。これは、蚈算列で関数を䜿甚しおはならないずいう意味ではありたせんが、慎重に扱う必芁がありたす。



関数を䜿甚するかどうかに関係なく、非氞続的な蚈算列の䜜成は非垞に簡単です。次の指瀺CREATE TABLEOrders1蚈算列を含むテヌブルを定矩したすCost。



USE WideWorldImporters;
GO
DROP TABLE IF EXISTS Orders1;
GO
CREATE TABLE Orders1(
  LineID int IDENTITY PRIMARY KEY,
  ItemID int NOT NULL,
  Quantity int NOT NULL,
  Price decimal(18, 2) NOT NULL,
  Profit decimal(18, 2) NOT NULL,
  Cost AS (Quantity * Price - Profit));

INSERT INTO Orders1 (ItemID, Quantity, Price, Profit)
SELECT StockItemID, Quantity, UnitPrice, LineProfit
FROM Sales.InvoiceLines
WHERE UnitPrice IS NOT NULL
ORDER BY InvoiceLineID;


蚈算列を定矩するには、その名前の埌にASキヌワヌドず匏を指定したす。この䟋では、乗算されおいるQuantityこずにより、Priceおよび枛算したすProfit。テヌブルを䜜成した埌Sales.InvoiceLines、WideWorldImportersデヌタベヌステヌブルのデヌタを䜿甚しおINSERTを入力したす。次に、SELECTを実行したす。



SELECT ItemID, Cost FROM Orders1 WHERE Cost >= 1000;


このク゚リは、22,973行、たたはWideWorldImportersデヌタベヌスにあるすべおの行を返す必芁がありたす。このク゚リの実行蚈画を図1に瀺したす。





図1.Orders1テヌブルに察するク゚リの実行蚈画



最初に泚意するのは、デヌタを取埗する効率的な方法ではないクラスタヌ化むンデックススキャンです。しかし、これだけが問題ではありたせん。 Clustered Index Scanのプロパティで論理読み取り実際の論理読み取りの数を芋おみたしょう図2を参照。





図2.Orders1テヌブルを照䌚するための論理読み取り



論理読み取りの数この堎合は1108は、デヌタキャッシュから読み取られたペヌゞの数です。目暙は、この数を可胜な限り枛らすこずです。したがっお、芚えお他のオプションず比范するず䟿利です。



論理読み取りの数はSET STATISTICS IO ON、SELECTを実行する前にステヌトメントを実行するこずによっおも取埗できたす。 CPUず合蚈時間を衚瀺するには、SET STATISTICS TIME ONたたはク゚リ実行プランのSELECTステヌトメントのプロパティを衚瀺したす。



泚目に倀するもう1぀のポむントは、実行蚈画に2぀のComputeScalarステヌトメントがあるこずです。1぀目右偎は、返された各行の蚈算された列倀の蚈算です。列の倀はその堎で蚈算されるため、その列にむンデックスを䜜成しない限り、非氞続的な蚈算列でこの手順を回避するこずはできたせん。



堎合によっおは、非氞続的な蚈算列は、それを保存したり、むンデックスを䜿甚したりせずに必芁なパフォヌマンスを提䟛したす。これにより、ストレヌゞスペヌスが節玄されるだけでなく、テヌブルたたはむンデックスの蚈算倀を曎新するオヌバヌヘッドも回避されたす。ただし、倚くの堎合、非氞続的な蚈算列はパフォヌマンスの問題に぀ながるため、代替手段を探し始める必芁がありたす。



氞続的な蚈算列



パフォヌマンスの問題を解決するためによく䜿甚される手法の1぀は、蚈算列を氞続化ずしお定矩するこずです。このアプロヌチでは、匏が事前に蚈算され、結果が残りのテヌブルデヌタずずもに保存されたす。



列が氞続的であるためには、それが決定論的である必芁がありたす。぀たり、匏は同じ入力に察しお垞に同じ結果を返す必芁がありたす。たずえば、戻り倀は垞に倉化するため、列匏でGETDATE関数を䜿甚するこずはできたせん。



氞続的な蚈算列を䜜成するにPERSISTEDは、次の䟋に瀺すように、列定矩にキヌワヌドを远加する必芁がありたす。



DROP TABLE IF EXISTS Orders2;
GO
CREATE TABLE Orders2(
  LineID int IDENTITY PRIMARY KEY,
  ItemID int NOT NULL,
  Quantity int NOT NULL,
  Price decimal(18, 2) NOT NULL,
  Profit decimal(18, 2) NOT NULL,
  Cost AS (Quantity * Price - Profit) PERSISTED);

INSERT INTO Orders2 (ItemID, Quantity, Price, Profit)
SELECT StockItemID, Quantity, UnitPrice, LineProfit
FROM Sales.InvoiceLines
WHERE UnitPrice IS NOT NULL
ORDER BY InvoiceLineID;


このテヌブルは、列にキヌワヌドが含たれおいるこずを陀いお、テヌブルずOrders2ほが同じです。 SQL Serverは、行が远加たたは倉曎されるず、この列に自動的にデヌタを入力したす。もちろん、これはテヌブルがテヌブルよりも倚くのスペヌスを占めるこずを意味したす。これは、保存されたプロシヌゞャを䜿甚しお確認できたす。Orders1CostPERSISTEDOrders2Orders1sp_spaceused



sp_spaceused 'Orders1';
GO
sp_spaceused 'Orders2';
GO


図3は、この保存されたプロシヌゞャヌの出力を瀺しおいたす。衚のデヌタサむズは8,824KBOrders1で、衚のデヌタサむズはOrders212,936KBです。蚈算倀を保存するためにさらに4,112KB。





図3.Orders1テヌブルずOrders2テヌブルのサむズの比范



これらの䟋はかなり小さなデヌタセットに基づいおいたすが、保存されるデヌタの量がどのように急速に増加するかを確認できたす。ただし、パフォヌマンスが向䞊した堎合、これはトレヌドオフになる可胜性がありたす。



パフォヌマンスの違いを確認するには、次のSELECTを実行したす。



SELECT ItemID, Cost FROM Orders2 WHERE Cost >= 1000;


これは、Orders1テヌブルに䜿甚したものず同じSELECTです名前の倉曎を陀く。図4に実行蚈画を瀺したす。





図4.Orders2テヌブルぞのク゚リの実行蚈画。



これもクラスタヌ化むンデックススキャンから始たりたす。ただし、今回は、蚈算列を実行時に蚈算する必芁がなくなったため、ComputeScalarステヌトメントは1぀だけです。䞀般に、ステップは少ないほど良いです。これは垞にそうであるずは限りたせんが。



2番目のク゚リは1593の論理読み取りを生成したした。これは、最初のテヌブルの1108の読み取りよりも485倚い倀です。それにもかかわらず、それは最初のものより速く走りたす。わずか玄100msですが、それよりはるかに短い堎合もありたす。プロセッサ時間も枛少したしたが、それほどではありたせん。ほずんどの堎合、ボリュヌムが倧きく、蚈算が耇雑になるず、差ははるかに倧きくなりたす。



非氞続的な蚈算列のむンデックス



蚈算列のパフォヌマンスを向䞊させるために䞀般的に䜿甚されるもう1぀の手法は、玢匕付けです。むンデックスを䜜成できるようにするには、列が決定論的で正確である必芁がありたす。぀たり、匏は浮動小数点型ず実数型を䜿甚できたせん列が氞続的でない堎合。他のデヌタタむプやSETパラメヌタにも制限がありたす。制限の完党なリストに぀いおは、SQLServerのドキュメント「ComputedColumnsのむンデックス」を参照しおください。



非氞続的な蚈算列がそのプロパティを介しおむンデックス付けに適しおいるかどうかを確認できたす。この関数を䜿甚しおプロパティを衚瀺しおみたしょうCOLUMNPROPERTY。プロパティIsDeterministic、IsIndexable、およびIsPreciseは私たちにずっお重芁です。



DECLARE @id int = OBJECT_ID('dbo.Orders1')
SELECT
  COLUMNPROPERTY(@id,'Cost','IsDeterministic') AS 'Deterministic',
  COLUMNPROPERTY(@id,'Cost','IsIndexable') AS 'Indexable',
  COLUMNPROPERTY(@id,'Cost','IsPrecise') AS 'Precise';


蚈算された列にむンデックスを付けるこずができるように、SELECTステヌトメントはプロパティごずに1を返す必芁がありたす図5を参照。





図5.むンデックス



を䜜成できるこずの確認怜蚌埌、非クラスタヌ化むンデックスを䜜成できたす。テヌブルを倉曎する代わりにOrders1、3番目のテヌブルOrders3を䜜成し、テヌブル定矩にむンデックスを含めたした。



DROP TABLE IF EXISTS Orders3;
GO
CREATE TABLE Orders3(
  LineID int IDENTITY PRIMARY KEY,
  ItemID int NOT NULL,
  Quantity int NOT NULL,
  Price decimal(18, 2) NOT NULL,
  Profit decimal(18, 2) NOT NULL,
  Cost AS (Quantity * Price - Profit),
  INDEX ix_cost3 NONCLUSTERED (Cost, ItemID));

INSERT INTO Orders3 (ItemID, Quantity, Price, Profit)
SELECT StockItemID, Quantity, UnitPrice, LineProfit
FROM Sales.InvoiceLines
WHERE UnitPrice IS NOT NULL
ORDER BY InvoiceLineID;


SELECTク゚リの 列ItemIDずCostSELECTク゚リの䞡方の列を含む非クラスタヌ化カバヌむンデックスを䜜成したした。テヌブルずむンデックスを䜜成しおデヌタを入力した埌、前の䟋ず同様に次のSELECTステヌトメントを実行できたす。



SELECT ItemID, Cost FROM Orders3 WHERE Cost >= 1000;


図6は、このク゚リの実行蚈画を瀺しおいたす。このク゚リでは、クラスタヌ化むンデックススキャンを実行する代わりに、ix_cost3むンデックスシヌク非クラスタヌ化むンデックスを䜿甚しおいたす。





図6.Orders3テヌブルでのク゚リの実行蚈画IndexSeek



ステヌトメントのプロパティを芋るず、ク゚リが92の論理読み取りのみを実行し、SELECTステヌトメントのプロパティでCPUず合蚈時間が枛少しおいるこずがわかりたす。違いは重芁ではありたせんが、これも小さなデヌタセットです。



たた、実行蚈画には、最初のク゚リのように2぀ではなく、1぀のComputeScalarステヌトメントしかないこずにも泚意しおください。蚈算された列にはむンデックスが付けられおいるため、倀はすでに蚈算されおいたす。これにより、列が氞続的であるず定矩されおいない堎合でも、実行時に倀を蚈算する必芁がなくなりたす。



保存された列のむンデックス



保存する蚈算列にむンデックスを䜜成するこずもできたす。これにより、远加のデヌタずむンデックスデヌタが保存されたすが、堎合によっおは圹立぀こずがありたす。たずえば、floatたたは実際のデヌタタむプを䜿甚しおいる堎合でも、氞続的な蚈算列にむンデックスを䜜成できたす。このアプロヌチは、CLR関数を操䜜する堎合や、関数が決定論的であるかどうかを確認できない堎合にも圹立ちたす。



次のステヌトメントCREATE TABLEはテヌブルを䜜成したすOrders4。テヌブル定矩には、氞続列Costず非クラスタヌ化カバヌむンデックスix_cost4の䞡方が含たれたす。



DROP TABLE IF EXISTS Orders4;
GO
CREATE TABLE Orders4(
  LineID int IDENTITY PRIMARY KEY,
  ItemID int NOT NULL,
  Quantity int NOT NULL,
  Price decimal(18, 2) NOT NULL,
  Profit decimal(18, 2) NOT NULL,
  Cost AS (Quantity * Price - Profit) PERSISTED,
  INDEX ix_cost4 NONCLUSTERED (Cost, ItemID));

INSERT INTO Orders4 (ItemID, Quantity, Price, Profit)
SELECT StockItemID, Quantity, UnitPrice, LineProfit
FROM Sales.InvoiceLines
WHERE UnitPrice IS NOT NULL
ORDER BY InvoiceLineID;


テヌブルずむンデックスが䜜成され、デヌタが入力されたら、SELECTを実行したす。



SELECT ItemID, Cost FROM Orders4 WHERE Cost >= 1000;


図7に実行蚈画を瀺したす。前の䟋ず同様に、ク゚リは非クラスタヌ化むンデックス怜玢むンデックスシヌクから始たりたす。





図7.Orders4テヌブルでのク゚リの実行蚈画



このク゚リも、前のク゚リず92の論理読み取りのみを実行するため、ほが同じパフォヌマンスが埗られたす。 2぀の蚈算列の䞻な違い、およびむンデックス付き列ずむンデックスなし列の䞻な違いは、䜿甚されるスペヌスの量です。ストアドプロシヌゞャを実行しお、これを確認したしょうsp_spaceused。



sp_spaceused 'Orders1';
GO
sp_spaceused 'Orders2';
GO
sp_spaceused 'Orders3';
GO
sp_spaceused 'Orders4';
GO


結果を図8に瀺したす。予想どおり、栌玍された蚈算列にはより倚くのデヌタがあり、むンデックス付き列にはより倚くのむンデックスがありたす。





図8.4぀のテヌブルすべおのスペヌス䜿甚率の比范



ほずんどの堎合、正圓な理由がない限り、栌玍されおいる蚈算列にむンデックスを付ける必芁はありたせん。他のデヌタベヌス関連の質問ず同様に、遞択は特定の状況、぀たりク゚リずデヌタの性質に基づいお行う必芁がありたす。



SQLServerでの蚈算列の操䜜



蚈算列は通垞のテヌブル列ではないため、パフォヌマンスを䜎䞋させないように泚意しお凊理する必芁がありたす。ほずんどのパフォヌマンスの問題は、列を保存たたはむンデックス付けするこずで解決できたすが、どちらのアプロヌチでも、䜙分なディスク領域ずデヌタの倉曎方法を考慮する必芁がありたす。デヌタが倉曎された堎合、氞続化された蚈算列にむンデックスを付けた堎合は、蚈算列の倀をテヌブルたたはむンデックス、あるいはその䞡方で曎新する必芁がありたす。特定のケヌスに最適なオプションを決定するこずしかできたせん。そしお、ほずんどの堎合、すべおのオプションを䜿甚する必芁がありたす。





続きを読む






All Articles