SQL Server Error 8632 karena lebih dari 100.000 entri dalam klausa WHERE

16

Masalah saya (atau setidaknya pesan kesalahan) sangat mirip dengan prosesor kueri kehabisan sumber daya internal - kueri sql yang sangat panjang .

Pelanggan saya bekerja dengan kueri pemilihan-SQL, yang berisi klausa di mana dengan tepat 100.000 entri.

Permintaan gagal dengan kesalahan 8632 dan pesan kesalahan

Kesalahan internal: Batas layanan ekspresi telah tercapai. Harap cari kemungkinan ekspresi kompleks dalam kueri Anda, dan cobalah untuk menyederhanakannya.)

Saya merasa sangat aneh bahwa pesan kesalahan ini dilemparkan, tepat pada 100.000 entri, jadi saya ingin tahu apakah ini adalah nilai yang dapat dikonfigurasi. Apakah ini masalahnya dan dalam kasus ya, bagaimana saya bisa meningkatkan nilai ini ke yang lebih tinggi?

Di MSDN , ada proposal untuk menulis ulang kueri, tapi saya ingin menghindari ini.

Sementara itu saya telah menemukan bahwa daftar entri yang saya bicarakan berisi bilangan asli, beberapa dari mereka tampaknya berurutan (seperti (1,2,3,6,7,8,9,10,12, 13,15,16,17,18,19,20).

Ini membuat SQL di mana-klausa seperti:

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

Saya bisa mengubah ini menjadi:

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

Bisakah ini disingkat dengan:

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

... atau yang serupa? (Saya tahu ini sangat sulit, tetapi itu akan membuat pembaruan perangkat lunak lebih mudah dan lebih mudah dibaca)

Untuk informasi Anda: data di mana-klausa adalah hasil perhitungan, dilakukan pada tabel lain: pertama entri tabel itu dibaca dan difilter di awal, kemudian beberapa pemrosesan tambahan dilakukan (yang tidak mungkin dilakukan dengan menggunakan SQL), hasil dari pemrosesan ekstra lebih banyak penyaringan dan hasil yang digunakan di mana-klausa. Karena tidak mungkin untuk menulis penyaringan lengkap dalam SQL, metode yang disebutkan telah digunakan. Tentunya isi dari mana-klausa mungkin berubah di setiap pemrosesan, karenanya kebutuhan solusi dinamis.

Dominique
sumber
7
Menanggapi hasil edit Anda: tidak, WHERE INtidak mendukung sintaks rentang semacam itu. Juga, seharusnya WHERE () OR () OR ()bukan DAN. Tetapi untuk menggunakan saran Brent, Anda sebenarnya tidak harus mengubah seluruh permintaan, Anda bisa melakukannya WHERE IN (SELECT myID FROM #biglist). Dan #biglistbisa berupa meja nyata (permanen), atau meja temp yang Anda buat dengan cepat.
BradC
Silakan kirim seluruh permintaan dan apa yang Anda hitung secara eksternal, ini mungkin benar-benar sesuatu yang bisa Anda lakukan dalam SQL sepenuhnya. Ganti nama bidang jika Anda khawatir tentang privasi atau apa pun.
Mike

Jawaban:

67

Untuk mencari lebih dari 100.000 nilai, alih-alih masukkan dalam tabel temp, satu baris per nilai yang Anda cari. Kemudian, gabungkan kueri Anda ke tabel temp untuk filter

Sesuatu dengan lebih dari 100.000 nilai bukan parameter - ini adalah tabel. Daripada berpikir untuk menaikkan batas, pertimbangkan Aturan Sepuluh Persen dari Swart : jika Anda mendekati 10% dari batas SQL Server, Anda mungkin akan memiliki waktu yang buruk.

Brent Ozar
sumber
1
Komentar bukan untuk diskusi panjang; percakapan ini telah dipindahkan ke obrolan .
Paul White Reinstate Monica
10

Jika Anda akan mengubah aplikasi, pertimbangkan juga

(a) menggunakan TVP untuk seluruh rangkaian nilai - Anda akan membuat DataTabledalam C # dan meneruskannya ke prosedur tersimpan menggunakan StructuredType, seperti yang saya tunjukkan di sini . (Semoga 100.000 entri tidak normal, karena skalabilitas mungkin menjadi masalah apa pun pendekatan yang Anda gunakan.)

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

atau

(B) menggunakan TVP untuk melewati batas atas dan bawah rentang, dan menulis gabungan yang sedikit berbeda (terima kasih @ 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
Aaron Bertrand
sumber
4

Hanya 2 ¢ saya tentang mempersingkat kondisi permintaan: -

Jika Anda dapat menentukan semua nilai yang mungkin ada entrydi muka, apakah itu layak jika Anda mengambil pelengkap dari kueri Anda?

Sebelum

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

Setelah

where entry not in (4,5,11,14)
Angin barat
sumber
0

Sulit untuk mengetahui apa yang ingin Anda capai tanpa benar-benar dapat melihat kueri, tetapi di sini kita mulai, dengan asumsi bahwa kueri Anda hanya membutuhkan dua tabel, satu dengan data, satu dengan nilai-nilai yang ingin Anda hindari:

  • Penggunaannya where entry in (<list of 100.000 values>)sangat mengerikan, baik dari sudut pandang efisiensi maupun pemeliharaan.
  • Menggunakannya where entry in (select whatever from table)sama mengerikannya dengan sebelumnya. Namun, tergantung pada kedua tabel keanehan dan mesin SQL, itu mungkin melakukan OK meskipun kanker kornea. (Mungkin OK di Oracle, tidak akan pernah di MySQL, tidak dapat mengingat tentang MSSQL).
  • Menggunakan PLSQL untuk mencapai hal ini sungguh menghebohkan.
  • Menurut pendapat saya (tanpa mengetahui kueri sama sekali), Anda harus menulis ulang kueri sebagai berikut:

    • Jika 100.000 nilai itu selalu sama, tidak bergantung pada sisa kueri, Anda harus mengunggah nilai-nilai itu ke tabel (table_fixed_values), dan gunakan

      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
    • Jika 100.000 nilai itu tidak sama, harus ada semacam logika untuk mengambil 100.000 nilai itu, logika yang harus Anda tanamkan dalam ONkueri sebelumnya.

Glezo
sumber
3
Mengapa LEFT JOIN table_fixed_values ON A.entry=B.entry WHERE B.entry IS NOT NULLdan tidak setara, lebih sederhana dan lebih mudah dibaca INNER JOIN table_fixed_values ON A.entry=B.entry?
ypercubeᵀᴹ
@ ypercubeᵀᴹ sepenuhnya benar, INNER JOIN lebih masuk akal.
glezo