Actualización de una cláusula WHERE para verificar si un valor NO está en una tabla separada


8

Tengo una consulta que usa una WHEREcláusula, y resulta que uso exactamente la misma WHEREcláusula en muchas consultas en esta tabla (et al).

La consulta es:

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

La parte que deseo cambiar es la WHEREcláusula, para permitirme usar una tabla de manera que si tengo que agregar un número de cliente para ser ignorado, no tenga que actualizar todas mis consultas. (Y hay bastantes consultas que tienen esta misma WHEREcláusula).


Si las exclusiones del Cliente son actualmente específicas para la ejecución de la consulta, ¿por qué moverlas a una tabla / tabla de trabajo compartida no introduciría un intercambio falso? En una aplicación normal, los clientes suelen ser arbitrarios y, por lo tanto, específicos para una sola ejecución de consulta. Sugeriría que esta pregunta omita hechos importantes en cuanto a la generalidad necesaria para que la solución funcione correctamente, o pasa por alto el tema del intercambio.
Thomas W

@ThomasW: ¿de qué habla "falso intercambio"? Tienes una referencia para eso? Nunca he oído hablar de eso antes.
Max Vernon el

1
@ThomasW Los requisitos para esto son que ciertos clientes que tenemos (que utilizamos mucho para las pruebas) deben ser excluidos de ciertos informes, ya que sesgan los resultados.
Der Kommissar

1
@MaxVernon: quizás un término mejor reconocido sería "alcance incorrecto". Lo que se describió implicaba cambiar una entrada de un parámetro completamente independiente, a ser una tabla de base de datos compartida de usuario cruzado e invocación cruzada. Este cambio cruza los 2 límites del alcance. Dado un contexto adicional, el alcance descrito parece correcto, pero si no fuera así, esto se manifestaría como "intercambio erróneo".
Thomas W

1
El enfoque descrito también recordaba una gran cantidad de implementación de tablas de trabajo heredadas (~ 1000 tablas) en una aplicación importante de la que tengo responsabilidad. En este sentido, planteé la posible naturaleza de "mesa de trabajo" como una pregunta :) Gracias.
Thomas W

Respuestas:


5

Cree una tabla para contener los números de clientes que se excluirán, luego excluya esas filas usando a NOT EXISTSen la WHEREcláusula.

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');

Ahora su WHEREcláusula en todas las consultas se convierte en:

WHERE NOT EXISTS 
(
  SELECT 1 FROM dbo.CustomerExclusions AS c
  WHERE c.CustomerNumber = SearchHistory.CustomerNumber
)

Sí, lamentablemente Los números de cliente deben ser una cadena para la compatibilidad cruzada con el AS / 400. (Al menos por ahora, estamos trabajando en una solución).
Der Kommissar

3
@EBrown Uh, ugh.
Aaron Bertrand

-3

Hay preguntas importantes / problemas potenciales con su enfoque propuesto. Por supuesto, puede excluir fácilmente a través de una tabla de trabajo 'Exclusión de número de cliente':

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

Pero ahora, lo que eran "parámetros de consulta" - completamente dinámicos e independientes, por consulta y por usuario - se están convirtiendo en "estado persistente compartido en la base de datos".

Algunas preguntas y puntos relevantes:

  1. ¿La información de exclusión del cliente debe ser separada, por usuario o por sesión? puede agregar un parámetro 'SessionID' para distinguirlos, pero esencialmente está recreando un viejo patrón de "Tabla de trabajo".

  2. quizás una cláusula NOT IN (...) podría ser preferible? que se puede parametrizar dinámicamente, hasta el límite de 2100 parámetros.

  3. quizás vuelva a visitar su código / infraestructura para crear consultas y enlazar parámetros, si actualmente confía en números de parámetros fijos; mejorar esto permitirá la modularidad y el uso de cláusulas IN o NOT IN (?,?,? ..) con un número variable de parámetros.

Enfoque sugerido:

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

Con los enlaces '1234', '5678', '6789', etc. a los parámetros NOT IN () y parámetros de consulta lógica posteriores vinculados dinámicamente a la numeración adecuada.


1
El uso de NOT IN (...) y / o la creación dinámica de texto de consulta es un antipatrón y dará como resultado un rendimiento menor que los enfoques basados ​​en conjuntos recomendados por Aaron y por mí.
Max Vernon

Para una excelente lectura sobre las diferencias, consulte esta publicación.
Max Vernon

@MaxVernon: reemplazar los parámetros dinámicos con datos "compartidos" o tablas de trabajo puede introducir el intercambio falso, que es mucho más que un antipatrón. Como nadie más ha considerado o establecido específicamente que esto no es un problema, es absolutamente válido plantear esta preocupación; ni debería ser trivialmente rechazado.
Thomas W
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.