WHERE yan tümcesinde 100.000'den fazla giriş nedeniyle SQL Server Hatası 8632


16

Benim sorunum (ya da en azından hata mesajı) iç kaynak - son derece uzun sql sorgusu bitti sorgu işlemcisi çok benzer .

Müşterim, tam 100.000 giriş içeren bir nerede-cümlesi içeren bir SQL seçme sorgusu ile çalışıyor.

Sorgu 8632 hatası ve hata iletisiyle başarısız oluyor

Dahili hata: Bir ifade hizmetleri sınırına ulaşıldı. Lütfen sorgunuzda potansiyel olarak karmaşık ifadeler arayın ve bunları basitleştirmeye çalışın.)

Bu hata mesajının tam olarak 100.000 girişe atıldığını çok tuhaf buluyorum, bu yüzden bu yapılandırılabilir bir değer olup olmadığını merak ediyorum. Bu durumda mı ve evet ise, bu değeri nasıl daha yüksek bir değere yükseltebilirim?

On MSDN , orada sorguyu yeniden yeniden yazmak öneridir, ama bu önlemek istiyorum.

Bu arada bahsettiğim girişler listesinin doğal sayılar içerdiğini öğrendim, bunların bir kısmı sıralı görünüyor (1,2,3,6,7,8,9,10,12, 13,15,16,17,18,19,20).

Bu SQL nerede-cümlesi gibi bir şey yapar:

where entry in (1,2,3,6,7,8,9,10,12,13,15,16,17,18,19,20)

Bunu şu şekilde dönüştürebilirim:

where (entry between 1 and 3) OR
      (entry between 6 and 10) OR
      (entry between 12 and 13) OR
      (entry between 15 and 20)

Bu kısaltılabilir mi:

where entry in (1,...,3,6,...,10,12,13,15,...,20)

...Veya benzeri? (Uzun bir çekim olduğunu biliyorum, ancak yazılım güncellemelerini daha kolay ve daha okunabilir hale getirecektir)

Bilginiz için: nerede yan tümcesindeki veriler başka bir tabloda yapılan bir hesaplamanın sonucudur: ilk önce o tablonun girişleri başlangıçta okunur ve filtrelenir, daha sonra bazı ekstra işlemler yapılır (bunu yapmak imkansızdır) SQL), bu ekstra işlemenin sonucu daha fazla filtrelemedir ve bunun sonucu where-cümlesinde kullanılır. Tüm filtrelemeyi SQL'de yazmak imkansız olduğu için, söz konusu yöntem kullanılmıştır. Açıkçası nerede cümlesi içeriği her işlemde değişebilir, dolayısıyla dinamik bir çözüme ihtiyaç duyulabilir.


7
Düzenlemenize yanıt olarak: hayır, WHERE INbu tür aralık sözdizimini desteklemiyor. Ayrıca, WHERE () OR () OR ()VE olmamalıdır . Ama Brent'in önerisini kullanmak için, aslında tüm sorguyu değiştirmek zorunda değilsiniz, sadece yapabilirsiniz WHERE IN (SELECT myID FROM #biglist). Ve #biglistya gerçek (kalıcı) bir masa ya da anında yaptığınız geçici bir masa olabilir.
BradC

Lütfen tüm sorguyu ve harici olarak ne hesapladığınızı lütfen gönderin, bu muhtemelen SQL'de tamamen yapabileceğiniz bir şeydir. Gizlilik veya herhangi bir şeyle ilgili endişeleriniz varsa alan adlarını yeniden adlandırın.
Mike

Yanıtlar:


67

100.000'den fazla değer aramak için, bunları, aradığınız değer başına bir satır olan bir geçici tabloya yerleştirin. Ardından, sorgunuzu filtreleme için bu geçici tabloya ekleyin.

100.000'den fazla değere sahip bir şey parametre değildir - tablodur. Sınırı yükseltmeyi düşünmektense, Swart'ın Yüzde On Kuralını düşünün : SQL Server sınırının% 10'una yaklaşıyorsanız, muhtemelen kötü bir zaman geçireceksiniz.


1
Yorumlar uzun tartışmalar için değildir; bu görüşme sohbete taşındı .
Paul White Reinstate Monica

10

Uygulamayı yine de değiştirecekseniz,

(a) tüm değerler kümesi için bir TVP kullanma - burada gösterdiğim gibi, DataTablebir C # içinde bir ve bir saklı yordam geçirin . (Umarım 100.000 giriş normal değildir, çünkü hangi yaklaşımı kullanırsanız kullanın ölçeklenebilirlik bir sorun olabilir.)StructuredType

CREATE TYPE dbo.optionA AS TABLE(value int PRIMARY KEY);
GO

CREATE PROCEDURE dbo.procedure_optionA
  @t dbo.optionA READONLY
AS
BEGIN
  SET NOCOUNT ON;
  SELECT <cols> FROM dbo.<table> AS t
    INNER JOIN @t AS tvp
    ON t.entry = tvp.value;
END
GO

veya

(b) aralıkların üst ve alt sınırlarını geçmek için bir TVP kullanmak ve biraz farklı bir birleşim yazmak (teşekkürler @ypercube).

  CREATE TYPE dbo.optionB AS TABLE
  (
    LowerBound int,
    UpperBound int,
    PRIMARY KEY (LowerBound, UpperBound)
  );
  GO

  CREATE PROCEDURE dbo.procedure_optionB
    @t dbo.OptionB READONLY
  AS
  BEGIN
    SET NOCOUNT ON;
    SELECT <cols> FROM dbo.<table> AS t
      INNER JOIN @t AS tvp
      ON  t.entry >= tvp.LowerBound 
      AND t.entry <= tvp.UpperBound;
  END
  GO


4

Sorgu koşulu daha kısa yapma konusunda sadece benim 2 ¢: -

Eğer olası tüm değerleri entryönceden belirleyebiliyorsanız, sorgunuzun tamamlayıcısını almanız uygun olur mu?

Önce

where entry in (1,2,3,6,7,8,9,10,12,13,15,16,17,18,19,20)

Sonra

where entry not in (4,5,11,14)

0

Sorguyu gerçekten görmeden ne yapmaya çalıştığınızı anlamak zordur, ancak işte, sorgunuzun yalnızca biri verileri olmak üzere, kaçınmak istediğiniz değerlere sahip iki tabloya ihtiyacı olduğunu varsayarız:

  • Kullanma where entry in (<list of 100.000 values>)bakış verimliliği ve bakım açısından her iki outrageously korkunç.
  • Kullanmak where entry in (select whatever from table)eskisi kadar zorlukla korkunçtur. Bununla birlikte, her iki tabloya da idiyosenkrazi ve SQL motoruna bağlı olarak, kornea kanserine rağmen Tamam olabilir. (Oracle'da Tamam olabilir, asla MySQL'de olmayacak, MSSQL hakkında hatırlayamıyorum).
  • Bunu başarmak için PLSQL kullanmak çok korkunç.
  • Bence (sorguyu bilmeden), sorguyu aşağıdaki gibi yeniden yazmalısınız:

    • Bu 100.000 değer her zaman aynıysa, sorgunun geri kalanına dayanmıyorsa, bu değerleri bir tabloya yüklemelisiniz (table_fixed_values) ve

      SELECT * -- whatever you might need
      FROM
         table_a A
         LEFT JOIN table_fixed_values ON A.entry=B.entry
      WHERE B.entry IS NOT NULL
    • Bu 100.000 değer aynı değilse, bu 100.000 değeri toplamak için bir tür mantık olmalıdır ON, önceki sorgunun içine yerleştirmeniz gereken mantık .


3
Neden LEFT JOIN table_fixed_values ON A.entry=B.entry WHERE B.entry IS NOT NULLeşdeğer, daha basit ve okunması kolay değil INNER JOIN table_fixed_values ON A.entry=B.entry?
ypercubeᵀᴹ

@ ypercubeᵀᴹ tamamen doğru, INNER JOIN daha anlamlı.
glezo
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.