以下のXMLの例で更新
現在のデザインは第一正規形に違反しています。
それ自体は大丈夫です。何年にもわたって、私は継承し、そうするいくつかのシステムを維持しなければなりませんでした。なぜそのように建てられたのかわかりません。それは本当に重要ではありません。それらを維持する必要があり、それらに基づいて構築されたアプリのスタックに対してそうすることは言うまでもなく、リファクタリング、テスト、および検証の時間があるようなスケジュールではありませんでした。
しかし、今振り返ると、彼ら全員が共有している1つの属性を簡単に見つけることができます。これは、これらのシステムを最適化および拡張する上での絶対的な最大の障壁でした。基盤となる「リレーショナル」データベースは、第一正規形に違反していました。事実上すべての技術的な「落とし穴」が発生し、事実上すべてのパフォーマンスの問題が根本的な原因でした。文字列を分割します。それらを検証するための偽のデータ型システムを作成します。それらを説明するためにさらに区切られた属性を作成します。区切られた「場所」ごとに特別なルールを作成し、それらを適用するために多くのシステムでEVAL関数を実装する必要があります。動的SQL以下を使用してすべてを検索します。概念的に単純な機能のように見えるものを実装するには、私が思い出すよりも多くの「巧妙な」プログラミングが必要でした。
たぶんあなたのシステムは違うでしょう。たぶん、40年以上のリレーショナルデータベースの研究はあなたの状況には当てはまりません。あなたのために、私は本当にそう願っています。唯一の問題は、リレーショナルデータベースを非リレーショナルな方法で使用していることです。ハンマーでネジを叩くことができ、オートバイでボートを引くことができるのと同じように(実際にそれを実行する場合はブレーキを踏まないでください)、テキストにインデックス(フルテキストまたはBツリー)を作成できますそれは整数を表します。
しかし、なぜあなたはこれらのことのいずれかをするのですか?実際に整数を整数として格納し、型安全性を楽しんでみませんか?これを2つの関連するテーブルに正規化して、より小さなトランザクションとより多くのインデックス作成オプションを利用しないのはなぜですか?変更できないシステムを継承している場合は、そのように言ってください。そうすれば、人々は代替案を手伝うことができるかもしれません(TVPとXMLは正しく言及されています)。でも、ハンマーやバイクはネジをうまく動かせず、ボートを引っ張らないので壊れているとは言えません。
とはいえ(おそらく誰か、どこかで不適切なデザインを再考している)、LIKE
区切られた文字列を検索するときに私はうまく利用しました:
-- Setup demo data
declare @delimitedInts table (
data varchar(max) not null
)
insert into @delimitedInts select '0,1,2'
insert into @delimitedInts select '1,2,3,4'
insert into @delimitedInts select '5,10'
-- Create a search term
declare @searchTerm int = 2
-- Get all rows that contain the searchTerm
select data
from @delimitedInts
where ',' + data + ',' like '%,' + cast(@searchTerm as varchar(11)) + ',%'
-- Create many search terms
declare @searchTerms table (
searchTerm int not null primary key
)
insert into @searchTerms select 2
insert into @searchTerms select 3
insert into @searchTerms select 4
-- Get all rows that contain ANY of the searchTerms
select distinct a.data
from @delimitedInts a
join @searchTerms b on ',' + a.data + ',' like '%,' + cast(b.searchTerm as varchar(11)) + ',%'
-- Get all rows that contain ALL of the searchTerms
select a.data
from @delimitedInts a
join @searchTerms b on ',' + a.data + ',' like '%,' + cast(b.searchTerm as varchar(11)) + ',%'
group by a.data
having count(*) = (select count(*) from @searchTerms)
これはあなたにとって遅すぎますか?多分。実際に測定しましたか?少なくとも、実装を導入して、最適化する前にそれが機能することを証明することができます。
更新:XML
スペースで区切られた列をXML列に変換し、XMLインデックスを使用してクエリを実行することについて、少しテストを行いました。残念ながら、計算列にXMLインデックスを配置することはできないため、トリガーを使用してXML列を自動的に更新します。ここにいくつかの興味深い結果があります(SQLコメントに注意してください):
-- Create a demo table
create table MyTable (
ID int not null primary key identity
, SpaceSeparatedInts varchar(max) not null
--, ComputedIntsXml as cast('<ints><i>' + replace(SpaceSeparatedInts, ' ', '</i><i>') + '</i></ints>' as xml) persisted -- Can't use XML index
, IntsXml xml null
)
go
-- Create trigger to update IntsXml
create trigger MyTable_Trigger on MyTable after insert, update as begin
update m
set m.IntsXml = cast('<ints><i>' + replace(m.SpaceSeparatedInts, ' ', '</i><i>') + '</i></ints>' as xml)
from MyTable m
join inserted i on m.ID = i.ID
end
go
-- Add some demo data
insert into MyTable (SpaceSeparatedInts) select '1'
insert into MyTable (SpaceSeparatedInts) select '1 2'
insert into MyTable (SpaceSeparatedInts) select '2 3 4'
insert into MyTable (SpaceSeparatedInts) select '5 6 7 10'
insert into MyTable (SpaceSeparatedInts) select '100 10 1000'
go
-- Search for the number 10 (and use this same query in subsequent testing, below)
select *
from MyTable
where IntsXml.exist('/ints/i[. = "10"]') = 1
-- This query spends virtually all of its time running an XML Reader and an XPath filter
-- Add a primary xml index
create primary xml index IX_MyTable_IntsXml on MyTable (IntsXml)
-- The query now uses a clustered index scan and clustered index seek on PrimaryXML
-- Add secondary xml index for value
create xml index IX_MyTable_IntsXml_Value on MyTable (IntsXml) using xml index IX_MyTable_IntsXml for value
-- No change
-- Add secondary xml index for path
create xml index IX_MyTable_IntsXml_Path on MyTable (IntsXml) using xml index IX_MyTable_IntsXml for path
-- No change
-- Add secondary xml index for property
create xml index IX_MyTable_IntsXml_Property on MyTable (IntsXml) using xml index IX_MyTable_IntsXml for property
-- The query now replaces the clustered index scan on PrimaryXML with an index seek on SecondaryXML
明らかに別の方法ですが、これはLIKEよりも高速ですか?環境でテストする必要があります。うまくいけば、これはあなたにそうする方法のいくつかのアイデアを与えるでしょう。それがあなたの店で実行可能であるならば、これがあなたのためにどのようにうまくいくかを私に知らせてください。