【问题标题】:Best SQL query for list of records containing certain characters?包含某些字符的记录列表的最佳 SQL 查询?
【发布时间】:2011-06-29 10:44:19
【问题描述】:

我目前正在使用一个相对较大的 SQL Server 2000 数据库。它的大小为 80 GB,拥有数百万条记录。

我目前需要返回一个名称列表,其中至少包含一系列非法字符中的一个。非法字符只是指客户定义的任意字符列表。在下面的示例中,我使用问号、分号、句点和逗号作为非法字符列表。

我最初想做一个与正则表达式一起工作的 CLR 函数,但由于它是 SQL Server 2000,我想这是不可能的。

目前我已经这样做了:

select x from users
where 
columnToBeSearched like '%?%' OR
columnToBeSearched like '%;%' OR
columnToBeSearched like '%.%' OR
columnToBeSearched like '%,%' OR
otherColumnToBeSearched like '%?%' OR
otherColumnToBeSearched like '%;%' OR
otherColumnToBeSearched like '%.%' OR
otherColumnToBeSearched like '%,%'

现在,我无论如何都不是 SQL 专家,但我感觉上面的查询效率很低。在包含数百万条记录的表中执行 8 次多通配符搜索,似乎会严重降低系统速度。虽然它似乎在测试服务器上运行良好,但我得到了“这一定是完全错误的”的感觉。

由于我最终需要在现场生产服务器上执行此脚本,我希望能达到良好的性能,以免阻塞系统。稍后可能需要扩展该脚本以包含更多非法字符,但这不太可能。

总结一下:我的目标是获取一个记录列表,其中两列中的任何一列都包含客户定义的“非法字符”。该数据库是实时且庞大的,因此我想要一种更有效的方法,因为我相信上述查询会非常慢。

谁能告诉我实现结果的最佳方法?谢谢!

/莫腾

【问题讨论】:

  • 您只需要运行一次查询吗?或者这是您需要反复做的事情?
  • 实际上我并不完全确定。它很可能最多运行一次。一周,甚至可能是一次性手术。

标签: sql sql-server tsql sql-server-2000


【解决方案1】:

它用得不多,但LIKE 语句以与正则表达式类似(但大大简化)的方式接受模式。 This link 是它的 msdn 页面。

在您的情况下,您可以简化为(未经测试):

select x from users
where 
    columnToBeSearched like '%[?;.,]%' OR
    otherColumnToBeSearched like '%[?;.,]%'

另请注意,您可以将LIKE 模式创建为变量,从而允许客户定义您的部分需求。

另一个主要优化:如果您在用户行上有更新的日期(或时间戳)(对于任何审计历史类型的事物),那么您始终可以只查询自上次检查以来更新的行。

【讨论】:

  • 我刚试过 - 它返回的记录数量与我之前的查询相同,所以它工作正常。然而,我最初的查询需要 34 秒来执行,而这个需要 37 秒,所以或多或少是相同的。不过,我不确定,如果我什至可以衡量查询的效率,以执行所需的秒数为单位?
  • 一项主要优化:如果您在用户行上获得了更新日期(对于任何审计历史类型的事物),那么您始终可以只查询自上次检查以来更新的行。 (更新为答案文本)
  • 你可能还想看看你是否从... colname collate Latin1_General_Bin like '%[?;.,]%'获得了收益
  • Alex:刚试了一下,好像不影响性能。无论如何感谢您的提示。
  • Jon,关于您的优化提示:我有一个 CreatedDate 日期时间。我将此查询放入存储过程中。无论如何我可以自动存储存储过程的最后执行时间吗?这样我就可以将它用作参数,而数据库管理员不必记住他上次运行查询的时间?
【解决方案2】:

如果这是一个将重复运行的查询,您最好为它创建一个索引。语法目前让我无法理解,但您可能会创建一个计算列(编辑:可能是 PERSISTED 计算列),如果 columnToBeSearchedotherColumnToBeSearched 包含非法字符,则为 1,否则为 0。在该列上创建一个索引,然后简单地选择该列为 1 的所有行。这假设该数据库安装的非法字符集是固定的(我假设这就是您所说的“由客户指定”)。另一方面,如果每个查询可能指定一组不同的非法字符,这将不起作用。

顺便说一句,如果您不介意读取未提交行的风险,您可以在隔离级别为READ UNCOMMITTED 的事务中运行查询,这样您就不会阻塞其他事务。

【讨论】:

  • 计算列不会影响表上所有其他操作的性能吗?
  • @Jon Egerton:只有插入和更新操作(如果计算列是PERSISTED)——但是是的,这绝对应该考虑在内。不过,性能损失应该不会很高。
  • 感谢 Aasmund,我建议根据查询需要运行的频率创建索引。
【解决方案3】:

您可以尝试对数据进行水平分区并将查询“拆分”为多个较小的查询。例如你可以做

SELECT x FROM users 
WHERE users.ID BETWEEN 1 AND 5000 
AND -- your filters on columnToBeSearched

将您的结果重新放在一个列表中可能有点不方便,但如果它是一份报告,您只需提取一次(或偶尔提取一次),这可能是可行的。 我假设 ID 是用户的主键或定义了索引的列,这意味着 SQL 应该能够创建一个有效的执行计划,在其中评估 users.ID BETWEEN 1 AND 5000 (fast) 在尝试检查之前过滤器(可能很慢)。

【讨论】:

    【解决方案4】:

    查找 PATINDEX 它允许您放入一个字符数组 PATINDEX('[._]',ColumnName) 返回一个 0 或在某个值中发现非法字符的第一次出现的值。希望这会有所帮助。

    【讨论】:

    • 试过了,但实际上性能有点差。不过还是谢谢!
    猜你喜欢
    • 2014-06-27
    • 1970-01-01
    • 2014-12-20
    • 1970-01-01
    • 2014-08-16
    • 1970-01-01
    • 2023-04-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多