更新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子句。)


如果客户排除项当前特定于查询执行,为什么将它们排除到共享表/工作表中不会引入错误共享?在普通应用程序中,客户通常是任意的,因此特定于单个查询执行。我建议这个问题或者忽略解决方案正常工作所必需的重要事实,或者忽略共享问题。
Thomas W

@ThomasW-您所说的“虚假共享”是什么?你有参考吗?我以前从未听说过。
Max Vernon

1
@ThomasW对此的要求是,我们拥有的某些客户(我们经常进行测试)必须从某些报告中排除,因为他们会歪曲结果。
Der Kommissar

1
@MaxVernon -也许更好理解的术语是“范围不正确”。所描述的确实涉及将输入从完全独立的参数更改为跨用户,跨调用的共享DB表。此更改跨越2个范围边界。给定额外的上下文,所描述的范围似乎还可以,但是如果不是这样,它将表现为“错误共享”。
Thomas W

1
所描述的方法还让人想起我负责的主要应用程序中的许多遗留工作表实现(〜1000个表)。在这方面,我提出了可能的“工作台”性质作为问题:)谢谢。
Thomas W

Answers:


5

创建一个表来保存要排除的客户号码,然后排除使用这些行NOT EXISTSWHERE的条款。

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 2015年

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(...)和/或动态构建查询文本是一种反模式,与Aaron和本人建议的基于集合的方法相比,其性能会降低。
Max Vernon 2015年

有关差异的出色阅读,请查看文章。
Max Vernon 2015年

@MaxVernon -用“共享的”数据或工作表替换动态参数可能会导致错误的共享,这更像是一种反模式。由于没有其他人专门考虑或确定这不是问题,因此提出这一担忧是绝对有效的;也不应轻描淡写。
Thomas W
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.