SQL 'gibi' vs '=' performansı


82

Bu soru merak ettiklerimin etrafında dönüyor, ancak cevaplar tam olarak onu ele almıyor.

Gibi görünüyor genel olarak '=' daha hızlı joker kullanırken 'gibi' olduğunu. Bu geleneksel bilgelik gibi görünüyor. Ancak, sınırlı sayıda farklı sabit, kodlanmış, varchar tanımlayıcı içeren bir sütunum olduğunu ve bunlardan biriyle eşleşen tüm satırları seçmek istediğimi varsayalım:

select * from table where value like 'abc%'

ve

select * from table where value = 'abcdefghijklmn'

'Beğen' bir eşleşme bulmak için yalnızca ilk üç karakteri test etmelidir, oysa '=' tüm dizeyi karşılaştırmalıdır. Bu durumda, bana 'beğenmek' bir avantaja sahip olacak gibi görünüyor, diğer tüm şeyler eşittir.

Bu genel, akademik bir soru olarak düşünülmüştür ve bu nedenle hangi DB'nin önemi olmamalıdır, ancak SQL Server 2005 kullanılarak ortaya çıkmıştır.


23
Dışarıda bıraktığınız önemli bir şey value, dizine eklenip eklenmediğidir. Eğer öyleyse, o zaman =masa taraması gerektirmeyen basit bir aramadır LIKEve ona attığınız herhangi bir ifadenin altını üstüne getirecektir.
Daniel DiPaolo

7
@Daniel Bunun yanlış olduğunu düşünüyorum. LIKESonunda joker karakter bulunan A , SARGable'dır ve bu nedenle, bir indekste aralık araması yapar, görünürde tablo taraması yoktur. Bu aralık arama, bir =ifadeyle oldukça kolay bir şekilde rekabet edebilir ve birçok durumda (örneğin, tüm tatmin edici satırlar bir sayfadaysa, pek olası olmayan bir koşul), aynı sayıda okumayı gerektiren tam olarak aynı performans olabilir.
ErikE

Benim "diğer her şeyin eşit olması", "endeksli olup olmadığı" konusunu kapsamayı amaçlıyordu, ancak diğer yanıtlar hakkındaki yorumlarıma göre, bunun ne kadar fark yaratacağı konusunda en azından bazı tartışmalar var gibi görünüyor.
MickeyfAgain_BeforeExitOfSO

Cevabımı gör. Başlangıçta dizinsiz olarak test ettim ve performans aynı (her iki tablo taraması da tamamen aynıydı). Test senaryom için dizine ekleneceğini varsaydım, yoksa performansı neden önemseyesin ki?
JNK

5
Bu sorudaki tüm 'beğenme' konuşmaları ve cevaplar bize bir grup liseli kız gibi ses çıkarıyor. Tamamen.
JulianR

Yanıtlar:


64

Bkz. Https://web.archive.org/web/20150209022016/http://myitforum.com/cs2/blogs/jnelson/archive/2007/11/16/108354.aspx

Oradan alıntı yapın:

LIKE ile dizin kullanımına ilişkin kurallar genel olarak şu şekildedir:

  • Filtre ölçütleriniz eşittir = kullanıyorsa ve alan dizine alınmışsa, büyük olasılıkla bir INDEX / CLUSTERED INDEX SEEK kullanacaktır.

  • Filtre ölçütleriniz, joker karakter içermeyen LIKE kullanıyorsa (örneğin, bir web raporunda bir parametreniz OLABİLİR, ancak bunun yerine tam dizeyi kullanırsanız), dizini kullanma olasılığı yaklaşık 1 numaradır. Artan maliyet neredeyse hiçbir şeydir.

  • Filtre kriterleriniz LIKE kullanıyorsa, ancak başında bir joker karakter varsa (Name0 LIKE '% UTER' gibi) dizini kullanma olasılığı çok düşüktür, ancak yine de tam veya kısmi bir aralıkta en azından bir INDEX SCAN gerçekleştirebilir. İçerik.

  • ANCAK, filtre kriterleriniz LIKE kullanıyorsa, ancak İLK STRING ile başlıyorsa ve bundan SONRA bir yerde joker karakterler varsa (Name0 GİBİ 'COMP% ER' gibi), o zaman SQL aynı ilk satırları hızlıca bulmak için bir INDEX SEEK kullanabilir. başlangıç ​​karakterleri ve ardından tam eşleşme için bu satırlara bakın.

(Ayrıca, sorgunuzda başka neler olup bittiğine ve hangi tablolara katıldığınıza bağlı olarak SQL motorunun hala beklediğiniz şekilde bir dizin kullanmayabileceğini unutmayın. SQL motoru, dizini yeniden yazma hakkını saklı tutar. Verileri en verimli olduğunu düşündüğü ve bir INDEX SEEK yerine bir INDEX SCAN içerebilecek şekilde almak için biraz sorgulayın)


1
bu bağlantı öldü
baxx

2
@baxx bağlantının bir kopyası wayback makinesinde mevcuttur. web.archive.org/web/20150209022016/http://myitforum.com/cs2/…
alphabet5

45

Ölçülebilir bir fark.

Aşağıdakileri çalıştırın:

Create Table #TempTester (id int, col1 varchar(20), value varchar(20))
go

INSERT INTO #TempTester (id, col1, value)
VALUES
(1, 'this is #1', 'abcdefghij')
GO

INSERT INTO #TempTester (id, col1, value)
VALUES
(2, 'this is #2', 'foob'),
(3, 'this is #3', 'abdefghic'),
(4, 'this is #4', 'other'),
(5, 'this is #5', 'zyx'),
(6, 'this is #6', 'zyx'),
(7, 'this is #7', 'zyx'),
(8, 'this is #8', 'klm'),
(9, 'this is #9', 'klm'),
(10, 'this is #10', 'zyx')
GO 10000

CREATE CLUSTERED INDEX ixId ON #TempTester(id)CREATE CLUSTERED INDEX ixId ON #TempTester(id)

CREATE NONCLUSTERED INDEX ixTesting ON #TempTester(value)

Sonra:

SET SHOWPLAN_XML ON

Sonra:

SELECT * FROM #TempTester WHERE value LIKE 'abc%'

SELECT * FROM #TempTester WHERE value = 'abcdefghij'

Ortaya çıkan uygulama planı, size ilk operasyonun maliyetinin, yani LIKEkarşılaştırmanın, karşılaştırmadan yaklaşık 10 kat daha pahalı olduğunu gösterir =.

Bir =karşılaştırma kullanabiliyorsanız , lütfen kullanın .


2
Gerçekten test etmek için +1. Yine de şov planına bakmak tüm hikayeyi anlatmayabilir. Kendi testlerimden bazılarını yapacağım ve beklenmedik bir şey bulursam herkese haber vereceğim.
Tom H

1
Tom - doğru, ama bana ikisinin perde arkasında aynı şekilde İŞLENMEDİĞİ konusunda yeterince gösterge verdi.
JNK

1
Uygulama planında gösterilen maliyetler yanlış. Gerçek performansı yansıtmazlar. İlk planda, bunlar 19.95gerçeklikte asla gerçekleşmeyen ek 19 anahtar aramada SQL Server maliyetlerinin tahmini satır sayısına dayalıdır ( Gerçek yürütme planında bile gösterilen maliyetler Tahmini alt ağaç maliyetine dayanır )
Martin Smith

Testinizi yaklaşık 1 milyon satırlık bir testin yanı sıra yaptım ve her iki durumda da performans ve sorgu planları aynıydı. Bu makinede 2005 olmadığı için bu SQL 2008'de.
Tom H

1
@JNK - az önce denedim - göz ardı edilebilir bir fark var, ancak eşitsizlik aynı. 327ms için LIKE, 203ms için =. Daha fazla test yapıp doğru ortalamalar alırsam, #temp ile gerçek tablo arasında gerçek bir fark olmayacağını umuyorum.
Will A

13

Ayrıca likebazı sql çeşitlerinin indeksleri görmezden geleceğini ve bunun performansı düşüreceğini unutmamalısınız . Bu, özellikle örneğiniz gibi "şununla başlar" kalıbını kullanmazsanız geçerlidir.

Sorgu için yürütme planına gerçekten bakmalı ve ne yaptığını görmelisiniz, mümkün olduğunca az tahmin etmelisiniz.

Bununla birlikte, "ile başlar" kalıbı sql sunucusunda olabilir ve optimize edilmiştir. Bu olacak tablo dizinini kullanırız. EF 4.0'a geçiş likeiçin StartsWithbu çok nedenle.


2
Benzer model sorgunun bir parçası olduğunda ve joker karakter sondayken, tuzuna değecek hiçbir ilişkisel veritabanı bir dizini yok saymaz. Değeri bağlıyorsanız ve veritabanı sorgu hazırlığından ayrı olarak bağlamayı destekliyorsa bu farklı bir hikaye olabilir.
Dave W. Smith

Benim de içgüdülerimin söylediği şey bu, ancak bu konuda sadece sql server ile deneyimim var, bu yüzden özellikle ona odaklandım.
Blindy

7

Eğer valueAktar, yani bir tablo-tarama hem sonucu. Bu senaryodaki performans farkı ihmal edilebilir düzeyde olacaktır.

Eğer valuedizine Daniel onun yorumunda işaret ettiği gibi, =O (log N) performanstır bir dizin arama sonuçlanacaktır. LIKE (büyük olasılıkla - ne kadar seçici olduğuna bağlı olarak) dizinin kısmi bir taramasıyla sonuçlanacak >= 'abc've < 'abd'bu da =.

Burada SQL Server'dan bahsettiğime dikkat edin - tüm DBMS'ler LIKE ile hoş olmayacak.


İkili aramanın nasıl çalıştığını bildiğinizi sanmıyorum. Hem =vaka ve like '...%'her iki durumda da alt ağaçlar karşılaştırma ilişkilerine göre değerlendirilecek çünkü sql, (ve öyle) desen tanırsa aynı davranırlar durum.
Blindy

Oh, yaparım. GİBİ, büyük olasılıkla daha kötü davranacaktır, ancak seçicilik yeterince yüksekse yine de O (log N) olacaktır - kısmi taramanın nereden başlayacağını bulmak için O (günlük N), daha sonra dizinde bir dizi ileri okuma yapılır. bitiş noktasına 'abd'ulaşıldı.
Will A

Evet, ancak OP'nin örneği bu aralıkta yalnızca bir değer olduğunu varsayar, bu nedenle bu akılda tutularak karşılaştırmalar aynı olacaktır.
Blindy

Geçerli nokta - OP'nin söylediği şeyin bu olduğu tam olarak açık değil , ancak bence durum böyle olmamasından daha muhtemel. Bu durumda performans hemen hemen aynı olacaktır.
A

Bir LIKE'nin menzil araması muhtemelen bir = ifadesiyle oldukça kolay bir şekilde rekabet edecektir ve çoğu durumda (örneğin, tüm tatmin edici satırlar tek bir sayfadaysa, olası olmayan bir koşul), aynı sayıda okumayı gerektiren tam olarak aynı performans olabilir. . Bence "daha fazla çaba gerektirecek" demek yanlış bir kapsamlı ifadedir.
ErikE

6

Yanlış soruyu soruyorsunuz. Veritabanlarında hususlar, her zaman olduğu operatör performansı değil SARGability ekspresyonunun ve coverability genel sorgunun. Operatörün performansı büyük ölçüde önemsizdir.

Öyleyse, SARGability açısından nasıl yapılır LIKEve =karşılaştırılır? LIKE, bir sabitle başlamayan bir ifadeyle kullanıldığında (örneğin kullanıldığında LIKE '%something'), SARGabale dışıdır. Ama bu yapar mı =yoksa LIKE 'something%'SARGable mı? Hayır. SQL performansı ile ilgili herhangi bir soruda olduğu gibi, cevap metnin sorgusunda değil, dağıtılan şemada yatmaktadır. Bunlar ifade edebilir SARGable olmak eğer bir dizin onları tatmin etmek vardır.

Yani, doğruyu söylemek gerekirse, =ve arasında küçük farklar var LIKE. Ancak bir operatörün veya başka bir operatörün SQL'de 'daha hızlı' olup olmadığını sormak, 'Hangisi daha hızlı, kırmızı araba mı mavi araba mı?' Diye sormak gibidir. Renk hakkında değil, motor boyutu ve araç ağırlığı hakkında sorular sormalısınız ... İlişkisel tabloları optimize etme ile ilgili sorulara yaklaşmak için bakmanız gereken yer , WHERE cümlesindeki (ve diğer tümcelerdeki ifadeleriniz ve dizinlerinizdir , ancak bunlar genellikle WHERE ile başlar).


5

Mysql 5.5 kullanan kişisel bir örnek: 2 tablo arasında, 3 milyon satırdan biri ve 10 bin satırdan biri arasında bir iç birleşim vardı.

Aşağıdaki gibi bir dizinde beğeni kullanırken (joker karakter yok), yaklaşık 30 saniye sürdü:

where login like '12345678'

'açıkla' kullanarak şunu elde ederim:

görüntü açıklamasını buraya girin

Aynı sorguda bir '=' kullanıldığında, yaklaşık 0,1 saniye sürdü:

where login ='600009'

"Açıkla" yı kullanarak şunu elde ederim:

görüntü açıklamasını buraya girin

Gördüğünüz gibi like, dizin aramasını tamamen iptal etti, bu nedenle sorgu 300 kat daha fazla zaman aldı.


Bunu doğrulamak için yürütme planına da bakabilirsiniz
LittleBobbyTables - Au Revoir

teşekkürler @LittleBobbyTables. Şuna bir bakacağım.
Aris

Bunun son sürümümden mi (5.7) kaynaklandığını bilmiyorum, ancak LIKE buradaki benzersiz dizinimi kırmıyor.
Sebas

0

Belki Tam Metin Arama ile ilgileniyorsunuz .

Tam metin aramanın aksine, LIKE Transact-SQL koşulu yalnızca karakter kalıpları üzerinde çalışır. Ayrıca, biçimlendirilmiş ikili verileri sorgulamak için LIKE koşulunu kullanamazsınız. Ayrıca, büyük miktarda yapılandırılmamış metin verisine yönelik bir LIKE sorgusu, aynı verilere yönelik eşdeğer bir tam metin sorgusundan çok daha yavaştır . Milyonlarca metin verisi satırına yönelik LIKE sorgusunun döndürülmesi dakikalar alabilir; tam metin sorgusu ise, döndürülen satır sayısına bağlı olarak aynı verilere karşı yalnızca birkaç saniye veya daha kısa sürebilir.


-1

Her şey sırayla ,

her zaman eşit değiller

    select 'Hello' from dual where 'Hello  ' like 'Hello';

    select 'Hello' from dual where 'Hello  ' =  'Hello';

işler her zaman eşit olmadığında, performansları hakkında konuşmak o kadar da önemli değildir.

Dizeler üzerinde çalışıyorsanız ve yalnızca karakter değişkenleri varsa, performans hakkında konuşabilirsiniz. Ancak genel olarak birbirinin yerine "=" ve "beğen" kullanmayın.

Birçok yayında (yukarıdaki ve diğer sorularda) görmüş olacağınız gibi, eşit oldukları durumlarda, benzerlerin performansı, desen eşleştirme (harmanlama) nedeniyle daha yavaş.


Eğer 'Hello 'bir VARCHAR(varsayılan) ise haklısınız, ancak bir CHARise değilsiniz. A'ya çevirin CHAR(7)ve her ikisi de doğru olur. Ayrıca, TRIMvarcar'larını almadığın yerde ne halt ediyorsun? (not: bu en azından durum böyledir SQL Server 2008r2)
abluejelly
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.