Memperbarui klausa WHERE untuk memeriksa apakah suatu nilai TIDAK dalam tabel terpisah

8

Saya memiliki kueri yang menggunakan WHEREklausa, dan saya kebetulan menggunakan WHEREklausa yang sama persis di banyak pertanyaan pada tabel ini (et al).

Pertanyaannya adalah:

SELECT
    DATENAME(DW, [AtDateTime]) AS [Day of Week]
    ,COUNT(*) AS [Number of Searches]
    ,CAST(CAST(COUNT(*) AS DECIMAL(10, 2)) 
         / COUNT(DISTINCT CONVERT(DATE, [AtDateTime])) AS DECIMAL(10, 2)) 
       AS [Average Searches per Day]
    ,SUM(CASE WHEN [NumFound] = 0 THEN 1 ELSE 0 END) 
       AS [Number of Searches with no Results]
    ,CAST(CAST(SUM(CASE WHEN [NumFound] = 0 THEN 1 ELSE 0 END) 
         AS DECIMAL(10, 2)) / COUNT(*) AS DECIMAL(10, 4)) 
       AS [Percent of Searches with no Results]
FROM [DB].[dbo].[SearchHistory] 
WHERE 
    [CustomerNumber] <> '1234' AND [CustomerNumber] <> '5678'
GROUP BY DATENAME(DW, [AtDateTime]), DATEPART(DW, [AtDateTime])
ORDER BY DATEPART(DW, [AtDateTime])

Bagian yang ingin saya ubah adalah WHEREklausa, sebagai gantinya memungkinkan saya untuk menggunakan tabel sehingga jika saya harus menambahkan nomor pelanggan untuk diabaikan, saya tidak perlu memperbarui semua pertanyaan saya. (Dan ada beberapa pertanyaan yang memiliki WHEREklausa yang sama ini .)

Der Kommissar
sumber
Jika saat ini pengecualian Pelanggan khusus untuk eksekusi kueri, mengapa memindahkannya ke tabel / tabel kerja bersama tidak memperkenalkan pembagian yang salah? Dalam aplikasi normal, pelanggan biasanya akan sewenang-wenang & dengan demikian khusus untuk eksekusi permintaan tunggal. Saya akan menyarankan pertanyaan ini baik menghilangkan fakta-fakta penting seperti generalitas yang diperlukan untuk solusi untuk bekerja dengan benar, atau mengabaikan masalah berbagi.
Thomas W
@ThomasW - "berbagi salah" apa yang Anda bicarakan ini Apakah Anda punya referensi untuk itu? Saya belum pernah mendengarnya sebelumnya.
Max Vernon
1
@ThomasW Persyaratan untuk ini adalah bahwa pelanggan tertentu yang kami miliki (yang kami gunakan banyak untuk pengujian) harus dikeluarkan dari laporan tertentu, karena mereka condongkan hasilnya.
Der Kommissar
1
@ MaxVernon - mungkin istilah yang lebih dikenal adalah "ruang lingkup yang salah". Apa yang dideskripsikan memang melibatkan perubahan input dari parameter yang sepenuhnya independen, menjadi tabel DB bersama cross-user dan invokasi. Perubahan ini melintasi 2 batas ruang lingkup. Dengan konteks tambahan, ruang lingkup yang dijelaskan tampaknya OK, tetapi jika tidak, ini akan bermanifestasi sebagai "berbagi yang salah".
Thomas W
1
Pendekatan yang dideskripsikan juga mengingatkan banyak implementasi legacy work-table (~ 1000 tabel) dalam aplikasi utama yang saya punya tanggung jawab. Dalam hal ini saya mengangkat kemungkinan sifat "meja kerja" sebagai pertanyaan :) Terima kasih.
Thomas W

Jawaban:

5

Buat tabel untuk menahan nomor pelanggan yang akan dikecualikan, lalu kecualikan baris tersebut menggunakan a NOT EXISTSdalam WHEREklausa.

CREATE TABLE dbo.ExcludedCustomers
(
    CustomerNumber VARCHAR(255) NOT NULL
        CONSTRAINT PK_ExcludedCustomers
        PRIMARY KEY CLUSTERED
);

INSERT INTO dbo.ExcludedCustomers (CustomerNumber)
VALUES ('1234')
    , ('5678');


SELECT
    <....>
FROM [DB].[dbo].[SearchHistory] 
WHERE 
    NOT EXISTS (
        SELECT 1
        FROM dbo.ExcludedCustomers ec
        WHERE ec.CustomerNumber = SearchHistory.CustomerNumber
    )
    <...>;
Max Vernon
sumber
7
CREATE TABLE dbo.CustomerExclusions
(
  CustomerNumber VARCHAR(32) PRIMARY KEY -- Is CustomerNumber *really* a string?
);

INSERT dbo.CustomerExclusions(CustomerNumber) VALUES('1234'),('5678');

Sekarang WHEREklausa Anda di semua pertanyaan menjadi:

WHERE NOT EXISTS 
(
  SELECT 1 FROM dbo.CustomerExclusions AS c
  WHERE c.CustomerNumber = SearchHistory.CustomerNumber
)
Aaron Bertrand
sumber
Ya sayangnya Nomor pelanggan harus berupa string untuk kompatibilitas silang dengan AS / 400. (Setidaknya untuk saat ini, kami sedang mengerjakan perbaikan untuk itu.)
Der Kommissar
3
@EBrown Uh, ugh.
Aaron Bertrand
-3

Ada pertanyaan / potensi masalah penting dengan pendekatan yang Anda usulkan. Yang pasti, Anda dapat mengecualikan cukup mudah melalui tabel kerja 'Pengecualian Nomor Pelanggan':

WHERE NOT EXISTS (
  SELECT 1 FROM [dbo].Work_ExcludeCustomer
  WHERE CustomerNumber = SearchHistory.CustomerNumber
)

Tapi sekarang, apa yang disebut "parameter kueri" - sepenuhnya dinamis & independen, per-kueri, dan per-pengguna - berubah menjadi "status persisten yang dibagi bersama dalam basis data".

Beberapa pertanyaan & poin yang relevan:

  1. haruskah informasi Pengecualian Pelanggan terpisah, per-Pengguna atau per-Sesi? Anda dapat menambahkan parameter 'SessionID' untuk membedakannya, tetapi pada dasarnya Anda membuat ulang pola "Work Table" lama.

  2. mungkin klausa NOT IN (...) bisa lebih disukai? yang dapat diparameterisasi secara dinamis, hingga batas 2.100 parameter.

  3. mungkin mengunjungi kembali kode / infrastruktur Anda untuk membangun kueri & mengikat parameter, jika saat ini Anda mengandalkan nomor parameter tetap; meningkatkan ini akan memungkinkan modularitas & penggunaan klausa IN or NOT IN (?,?,? ..) dengan jumlah parameter variabel.

Pendekatan yang disarankan:

WHERE [CustomerNumber] NOT IN (?, ?, ?)

Dengan binding '1234', '5678', '6789' dll ke parameter NOT IN () & parameter kueri logis berikutnya terikat ke penomoran yang sesuai secara dinamis.

Thomas W
sumber
1
Penggunaan NOT IN (...) dan / atau secara dinamis membangun teks kueri adalah anti-pola, dan akan menghasilkan kinerja yang lebih rendah daripada pendekatan berbasis set yang direkomendasikan oleh Aaron dan saya sendiri.
Max Vernon
Untuk membaca perbedaan yang luar biasa, periksa pos ini .
Max Vernon
@ MaxVernon - mengganti parameter dinamis dengan data "dibagikan" atau tabel kerja dapat memperkenalkan pembagian yang salah, yang jauh lebih anti-pola. Karena tidak ada orang lain yang secara khusus mempertimbangkan atau menetapkan bahwa ini bukan masalah, sangat sah untuk mengangkat masalah ini; atau sepele tidak akan dipilih.
Thomas W