4

大家好,我对一些报告有几个查询,其中每个查询都从 35 个以上的表中提取数据。每个表都有近 10 万条记录。例如,所有查询都是 Union ALL

;With CTE
AS
(
Select col1, col2, col3 FROM Table1 WHERE Some_Condition
UNION ALL 
Select col1, col2, col3 FROM Table2 WHERE Some_Condition
UNION ALL 
Select col1, col2, col3 FROM Table3 WHERE Some_Condition
UNION ALL 
Select col1, col2, col3 FROM Table4 WHERE Some_Condition
.
.
. And so on 
)
SELECT col1, col2, col3 FROM CTE
ORDER BY col3 DESC

到目前为止,我只在 Dev Server 上测试了这个查询,我可以看到它需要一些时间才能得到结果。所有这 35 个以上的表都彼此不相关,这是我能想到的在结果集中获取所有所需数据的唯一方法。

  1. 有没有更好的方法来做这种查询?

  2. 如果这是进行此类查询的唯一方法,我如何通过在可能的情况下进行任何更改来提高此查询的性能?

我的意见
我不介意在这份报告中有一些脏读。我正在考虑使用查询提示 with nolockTransaction Isolation Level设置为READ UNCOMMITED.

这有什么帮助吗???

编辑
每个表都有 5-10 位列和每个位列的对应日期列,我对每个 SELECT 语句的条件类似于

WHERE BitColumn = 1 AND DateColumn IS NULL 

同行建议

过滤索引

CREATE NONCLUSTERED INDEX IX_Table_Column
ON TableName(BitColumn)
WHERE BitColum = 1

包含列的过滤索引

CREATE NONCLUSTERED INDEX fIX_IX_Table_Column
ON TableName(BitColumn)
INCLUDE (DateColumn)
WHERE DateColumn IS NULL

这是最好的方法吗?或者有什么建议吗???

4

4 回答 4

2

可以做很多事情来让它更快。如果我假设您需要执行这些 UNION,那么您可以通过以下方式加快查询速度:

  1. 缓存结果,例如,
    • 你能从整个语句中创建一个索引视图吗?还是有很多不同的 WHERE 条件,所以会有很多索引视图?但要知道这会减慢这些表的修改(INSERT 等)
    • 你能以不同的方式缓存它吗?也许在中间层?
    • 可以提前重新计算吗?
  2. 做一个覆盖索引。前导列是 WHERE 形式的列,然后是查询中的所有其他列作为包含列
    • 请注意,也可以过滤覆盖索引,但如果查询中的 WHERE 将包含变量/参数并且它们可能具有过滤索引未覆盖的值(即,该行不是覆盖)
  3. ORDER BY 将导致排序
    • 如果你可以缓存它,那很好 - 不需要排序(它是缓存排序的)
    • 否则,排序受 CPU 限制(如果不在内存中,则受 I/O 限制)。为了加快速度,您是否使用快速整理?最慢排序和最快排序之间的性能差异甚至可以达到 3 倍。例如,SQL_EBCDIC280_CP1_CS_AS、SQL_Latin1_General_CP1251_CS_AS、SQL_Latin1_General_CP1_CI_AS 是最快的排序规则之一。但是,如果我不知道您需要的排序规则特征,就很难提出建议
  4. 网络
    • 执行 SELECT 的连接的“网络数据包大小”应该是可能的最大值 - 如果结果集(行数)很大,则为 32,767 字节。这可以在客户端设置,例如,如果您在连接字符串中使用 .NET 和 SqlConnection。这将最大限度地减少从 SQL Server 发送数据时的 CPU 开销,并提高客户端和服务器端的性能。如果网络是瓶颈,这甚至可以将性能提高几十个百分点
    • 如果客户端在 SQL Server 上,则使用共享内存端点;否则 TCP/IP 以获得最佳性能
  5. 一般的东西
    • 如您所说,使用未提交的隔离级别将提高性能

...

除了重写查询等之外,您可能无法进行更改,但以防万一,添加更多内存以防现在不够用,或者在内存功能中使用 SQL Server 2014 :-),...肯定会有所帮助。

可以调整的东西太多了,但是如果问题不是很具体,就很难指出关键的东西。

希望这个对你有帮助

于 2013-10-27T20:34:50.440 回答
1

查询提示或隔离级别只会在发生任何阻塞时为您提供帮助。如果您不介意脏读并且在执行期间有锁,那可能是个好主意。

关键问题是有多少数据适合您需要使用的 Where 子句(WHERE BitColumn = 1 AND DateColumn IS NULL)和 DateColumn,包括选择子句中的列,以避免查询计划中的“页面查找”操作。

CREATE NONCLUSTERED INDEX IX_[Choose an IndexName]
ON TableName(BitColumn, DateColumn)
INCLUDE (col1, col2, col3)

当然,该覆盖过滤索引所需的空间取决于所涉及字段的数据类型以及满足 WHERE BitColumn = 1 AND DateColumn IS NULL 的行数。

之后,我建议使用 View 而不是 CTE:

CREATE VIEW [Choose a ViewName]
AS
(
Select col1, col2, col3 FROM Table1 WHERE Some_Condition
UNION ALL 
Select col1, col2, col3 FROM Table2 WHERE Some_Condition
.
.
.
)

通过这样做,您的查询计划应该看起来像 35 个小型索引扫描,但是如果大多数数据满足索引的 where 子句,则性能将类似于扫描 35 个源表,并且该解决方案不值得.

但是您说“每个表都有 5-10 位列和一个对应的日期列..”然后我认为为每个位列创建索引不是一个好主意。如果您需要使用不同的 BitColums 和不同的 DateColums 进行过滤,请在表中使用计算列:

ALTER TABLE Table1 ADD ComputedFilterFlag AS
CAST(
    CASE WHEN BitColum1 = 1 AND DateColumn1 IS NULL THEN 1 ELSE 0 END +
    CASE WHEN BitColum2 = 1 AND DateColumn2 IS NULL THEN 2 ELSE 0 END +
    CASE WHEN BitColum3 = 1 AND DateColumn3 IS NULL THEN 4 ELSE 0 END
AS tinyint)

我建议您将值 2^(X-1) 用于 conditionX(BitColumnX=1 和 DateColumnX IS NOT NULL)。它将允许您使用该标准的任意组合进行过滤。通过使用值 3,您可以找到完成以下条件的所有行:Bit1、Date1 和 Bit2、Date2 条件。任何条件组合都有其对应的 ComputedFilterFlag 值,因为 ComputedFilterFlag 充当条件的位图。如果你有少于 8 个不同的过滤器,你应该使用 tinyint 来节省索引空间并减少所需的 IO 操作。

然后在 ComputedFilterFlag 列上使用索引:

CREATE NONCLUSTERED INDEX IX_[Choose an IndexName]
ON TableName(ComputedFilterFlag)
INCLUDE (col1, col2, col3)

并创建视图:

CREATE VIEW [Choose a ViewName]
AS
(
Select col1, col2, col3 FROM Table1 WHERE ComputedFilterFlag IN [Choose the Target Filter Value set]--(1, 3, 5, 7)
UNION ALL 
Select col1, col2, col3 FROM Table2 WHERE ComputedFilterFlag IN [Choose the Target Filter Value set]--(1, 3, 5, 7)
.
.
.
)

通过这样做,您的索引涵盖了所有条件,并且您的查询计划应该看起来像 35 个小型索引搜索。

但这是一个棘手的解决方案,可能是在您的表模式中重构可以产生更简单和更快的结果。

于 2013-10-27T20:16:07.913 回答
1

好吧,您没有提供任何执行的任何统计数据或示例运行时间,因此无法猜测什么是慢的,它真的很慢。结果集中有多少数据?它可能只是检索 100K 行,因为结果只是花时间。如果 10000 行的结果集需要 5 分钟,那么肯定可以查看一些内容。因此,如果您有示例查询、结果中的行数以及在不同 where 条件下执行几次所需的时间,请发布。它将帮助我们比较结果。

顺便说一句,不要使用 CTE,只需使用常规的内部和外部查询选择。确保正确配置了 Temp DB。LDF 和 MDF 未默认配置为增加 10%。通过某些尝试和错误,您将了解为范围查询的真实性增加了多少日志和临时数据库,并基于此设置临时数据库的 MDF 和 LDF 的初始和增量大小。对于 Covered 过滤器索引,包含列应该是 col1、col2 和 co3,而不是列 Date,除非 Date 也在选择列表中。

原始 35 个表中的数据多久更新一次?如果每天最多一次,或者如果他们几乎同时获得更新,那么索引视图可能是一个可能的解决方案。但是,如果原始表每天更新一次以上,或者他们随时更新,并且不在同一行中,那么不要考虑索引视图。

如果磁盘空间不是最后的手段,请尝试在每个 35 个表上使用触发器测试性能。按照您对此选择查询的期望,创建新表来保存最终结果。在每个 35 个表上创建插入/更新/删除触发器,在其中检查触发器内的条件,如果是,则仅将相同的插入/更新/删除复制到新表。是的,您将需要新表中的一列来标识哪些数据来自哪个表。因为 Date 是 Null-Able 列,所以您无法充分利用该列上的 Index,因为“大多数情况下您正在寻找 WHERE Date is NULL”。在新表中,您总是做的唯一查询是 Date 为 NULL,然后甚至不必费心创建该列只需创建 BIT 列和其他 col1、col2、col3 等...如果您给出查询的真实示例并解释实际表,

于 2013-10-25T03:22:37.080 回答
1

你永远不会从对许多表的联合查询中获得实时结果,但我可以告诉你我是如何从类似的情况中获得一点速度的。希望这会帮助你。

您实际上可以通过一点编码和独创性一次运行所有这些。

您创建一个全局临时表而不是公用表表达式,并且不要在全局临时表上放置任何键,它只会减慢速度。然后,您启动所有插入到全局临时表中的单个查询。我已经手动完成了一百次左右,它比联合查询更快,因为您可以在每个 cpu 核心上运行一个查询。棘手的部分是确定各个查询何时自行完成该部分的机制,因此我手动执行这些操作。

于 2013-11-02T21:45:46.493 回答