Обновление предложения WHERE, чтобы проверить, не находится ли значение в отдельной таблице

8

У меня есть запрос, который использует WHEREпредложение, и я случайно использую одно и то же WHEREпредложение во многих запросах к этой таблице (и др.).

Запрос:

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])

Часть, которую я хочу изменить, - это WHEREпредложение, позволяющее мне вместо этого использовать таблицу, так что, если мне нужно добавить номер клиента, который будет игнорироваться, мне не придется обновлять все мои запросы. (И есть довольно много запросов, которые имеют такое же WHEREпредложение.)

Der Kommissar
источник
Если исключения клиента в настоящее время относятся только к выполнению запроса, почему перемещение их в общую таблицу / рабочую таблицу не приводит к ложному совместному использованию? В обычном приложении клиенты обычно бывают произвольными и, следовательно, специфичными для выполнения одного запроса. Я хотел бы предложить, чтобы этот вопрос либо не содержал важных фактов в отношении общности, необходимой для правильной работы решения, либо не учитывал проблему совместного использования.
Томас В.
@ThomasW - что это за «ложный обмен», о котором ты говоришь? У вас есть ссылка на это? Я никогда не слышал об этом раньше.
Макс Вернон
1
@ThomasW Требования к этому состоят в том, что некоторые наши клиенты (которых мы часто используем для тестирования) должны быть исключены из определенных отчетов, поскольку они искажают результаты.
Der Kommissar
1
@MaxVernon - возможно, лучше узнаваемым термином будет «неправильный охват». То, что было описано, включало в себя изменение входных данных с полностью независимого параметра на общую таблицу БД с несколькими пользователями. Это изменение пересекает 2 границы области видимости. Учитывая дополнительный контекст, описанная область видимости в порядке, но в противном случае это проявилось бы как «ошибочный обмен».
Томас В.
1
Описанный подход также напоминает множество традиционных реализаций рабочих таблиц (~ 1000 таблиц) в основном приложении, за которое я отвечаю. В связи с этим я поднял вопрос о возможном характере «рабочего стола» :) Спасибо.
Томас В.

Ответы:

5

Создайте таблицу для хранения номеров клиентов, подлежащих исключению, а затем исключите эти строки, используя NOT EXISTSв WHEREпредложении.

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
    )
    <...>;
Макс Вернон
источник
7
CREATE TABLE dbo.CustomerExclusions
(
  CustomerNumber VARCHAR(32) PRIMARY KEY -- Is CustomerNumber *really* a string?
);

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

Теперь ваше WHEREпредложение по всем запросам становится:

WHERE NOT EXISTS 
(
  SELECT 1 FROM dbo.CustomerExclusions AS c
  WHERE c.CustomerNumber = SearchHistory.CustomerNumber
)
Аарон Бертран
источник
Да, к сожалению. Номера клиентов должны быть строкой для совместимости с AS / 400. (По крайней мере, сейчас мы работаем над исправлением.)
Der Kommissar
3
@EBrown Э-э, тьфу.
Аарон Бертран
-3

Есть важные вопросы / потенциальные проблемы с вашим предложенным подходом. Конечно, вы можете легко исключить с помощью рабочей таблицы «Исключение номера клиента»:

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

Но теперь то, что было «параметрами запроса» - полностью динамическими и независимыми, для каждого запроса и для каждого пользователя - превращается в «общее постоянное состояние в базе данных».

Некоторые вопросы и актуальные вопросы:

  1. должна ли информация об исключении клиента быть отдельной, для каждого пользователя или для каждой сессии? Вы можете добавить параметр 'SessionID', чтобы различать их, но по сути вы воссоздаете старый седой шаблон «Рабочий стол».

  2. возможно, предложение NOT IN (...) может быть предпочтительнее? который может быть параметризован динамически, вплоть до 2100 параметров.

  3. возможно, пересмотрите ваш код / ​​инфраструктуру для создания запросов и привязки параметров, если вы в настоящее время полагаетесь на фиксированные номера параметров; его улучшение позволит использовать модульность и использовать предложения IN или NOT IN (?,?,? ..) с переменным числом параметров.

Предлагаемый подход:

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

С привязками «1234», «5678», «6789» и т. Д. К параметрам NOT IN () и последующим параметрам логического запроса, динамически привязанным к соответствующей нумерации.

Томас В.
источник
1
Использование NOT IN (...) и / или динамическое построение текста запроса является анти-шаблоном и приведет к более низкой производительности, чем подходы на основе множеств, рекомендованные Аароном и мной.
Макс Вернон
Для отличного прочтения о различиях, проверьте этот пост.
Макс Вернон,
@MaxVernon - замена динамических параметров «общими» данными или рабочими таблицами может привести к ложному совместному использованию, что является гораздо большим препятствием. Поскольку никто другой специально не учел или не установил, что это не проблема, абсолютно правильно поднять эту проблему; и при этом это не должно быть тривиально опущено.
Томас В.