如何在 SQL SERVER 中将内联 SQL 查询转换为 JOINS 以减少加载时间
Posted
技术标签:
【中文标题】如何在 SQL SERVER 中将内联 SQL 查询转换为 JOINS 以减少加载时间【英文标题】:How to convert inline SQL queries to JOINS in SQL SERVER to reduce load time 【发布时间】:2021-12-26 04:21:56 【问题描述】:我需要帮助来优化这个 SQL 查询。
在SELECT
主语句中,有三列取决于外部查询结果。这就是为什么我的查询需要很长时间才能返回数据。我曾尝试进行左连接,但这不能正常工作。
谁能帮我解决这个问题?
SELECT
DISTINCT ou.OrganizationUserID AS StudentID,
ou.FirstName,
ou.LastName,
(
SELECT
STRING_AGG(
(ug.UG_Name),
','
)
FROM
Groups ug
INNER JOIN ApplicantUserGroup augm ON augm.AUGM_UserGroupID = ug.UG_ID
WHERE
augm.AUGM_OrganizationUserID = ou.OrganizationUserID
AND ug.UG_IsDeleted = 0
AND augm.AUGM_IsDeleted = 0
) AS UserGroups,
order1.OrderNumber AS OrderId -- UAT-2455
,
(
SELECT
STRING_AGG(
(CActe.CustomAttribute),
','
)
FROM
CustomAttributeCte CActe
WHERE
CActe.HierarchyNodeID = dpm.DPM_ID
AND CActe.OrganizationUserID = ps.OrganizationUserID
) AS CustomAttributes -- UAT-2455
,
(
SELECT
STRING_AGG(
(CActe.CustomAttributeID),
','
)
FROM
CustomAttributeCte CActe
WHERE
CActe.HierarchyNodeID = dpm.DPM_ID
AND CActe.OrganizationUserID = ps.OrganizationUserID
) AS CustomAttributeID
FROM
ApplicantData acd WITH (NOLOCK)
INNER JOIN ClientPackage ps WITH (NOLOCK) ON acd.ClientSubscriptionID = ps.ClientSubscriptionID
INNER JOIN [ClientOrder] order1 WITH (NOLOCK) ON order1.OrderID = ps.OrderID
AND order1.IsDeleted = 0
INNER JOIN OUser ou WITH (NOLOCK) ON ou.OrganizationUserID = ps.OrganizationUserID
【问题讨论】:
“mysql”或“SQL Server”?这些是完全不同的数据库。更新不正确的标签/文本。 @user2864740,问题基本和SQL SERVER有关 然后从标签中删除“mysql”。 为了获得性能帮助,您必须包括表和索引定义,以及共享查询计划(您可以通过brentozar.com/pastetheplan )。没有这些信息,这是无法回答的。为什么你有NOLOCK
,你知道它是做什么的吗,你有你想要解决的阻塞问题吗?它不是一个更快的开关,它是一个给出不正确结果的开关,并且可能导致比它解决的问题更多
您可以在单个APPLY
中执行第二个和第三个STRING_AGG
,以避免对该表进行两次查询。为什么DISTINCT
在那里,我怀疑这可能是您的主要问题,您是否有要删除的重复项?如果是这样,为什么会有重复,哪个连接导致它们?
【参考方案1】:
看起来这个查询可以简化,并且删除 SELECT 子句中的依赖子查询,考虑你的第二个和第三个依赖子查询。您可以使用 LEFT JOIN 将它们重构为一个不相关的子查询。使用非依赖子查询效率更高,因为查询计划器可以只运行一次,而不是每行运行一次。
您需要来自同一个表的两个 STRING_AGG()
结果。此子查询为HierarchyNodeID
和OrganizationUserID
值的每个可能组合提供这两个输出。 STRING_AGG()
是一个类似于 SUM()
的聚合函数,因此可以很好地与 GROUP BY
配合使用。
SELECT HierarchyNodeID, OrganizationUserID,
STRING_AGG((CActe.CustomAttribute), ',') CustomAttributes -- UAT-2455,
STRING_AGG((CActe.CustomAttributeID), ',') CustomAttributeIDs -- UAT-2455
FROM CustomAttributeCte CActe
GROUP BY HierarchyNodeID, OrganizationUserID
您可以自己运行这个子查询来说服自己它有效。
现在,我们可以将其加入到您的查询中。像这样。 (为了可读性,我去掉了 NOLOCK 并使用了 JOIN:它与 INNER JOIN 的含义相同。)
SELECT DISTINCT
ou.OrganizationUserID AS StudentID,
ou.FirstName,
ou.LastName,
'tempvalue' AS UserGroups, -- shortened for testing
order1.OrderNumber AS OrderId, -- UAT-2455
uat2455.CustomAttributes, -- UAT-2455
uat2455.CustomAttributeIDs -- UAT-2455
FROM ApplicantData acd
JOIN ClientPackage ps
ON acd.ClientSubscriptionID = ps.ClientSubscriptionID
JOIN ClientOrder order1
ON order1.OrderID = ps.OrderID
AND order1.IsDeleted = 0
JOIN OUser ou
ON ou.OrganizationUserID = ps.OrganizationUserID
LEFT JOIN (
SELECT HierarchyNodeID, OrganizationUserID,
STRING_AGG((CActe.CustomAttribute), ',') CustomAttributes -- UAT-2455,
STRING_AGG((CActe.CustomAttributeID), ',') CustomAttributeIDs -- UAT-2455
FROM CustomAttributeCte CActe
GROUP BY HierarchyNodeID, OrganizationUserID
) uat2455
ON uat2455.HierarchyNodeID = dpm.DPM_ID
AND uat2455.OrganizationUserId = ps.OrganizationUserID
看看我们如何将您的第二个和第三个依赖子查询折叠成一个,然后将其用作具有 LEFT JOIN 的虚拟表?我们将 WHERE 子句从依赖子查询转换为 ON 子句。
您可以对此进行测试:使用 TOP(50) 运行它并观察结果。
当您满意时,下一步就是以同样的方式转换您的第一个依赖子查询。
专业提示永远不要使用WITH (NOLOCK)
,除非数据库管理专家在查看您的特定查询后告诉您。如果您的查询的目的是历史报告,并且您不关心数据库中的最新交易是否完全正确,您可以在查询之前使用此语句。它还允许查询在避免锁定的同时运行。
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
专业提示 着迷于格式化查询以提高可读性。一年后,您、您的同事和您自己必须能够阅读和推理此类查询。
【讨论】:
以上是关于如何在 SQL SERVER 中将内联 SQL 查询转换为 JOINS 以减少加载时间的主要内容,如果未能解决你的问题,请参考以下文章
如何检索 sql server 内联表值函数的返回值的元数据?