Inner join in Outer Join 导致性能不足,有什么不同的方法?
An Inner join in an Outer Join leading to peformance shortfalls, what is a different approach?
我在我的一个数据库中有一个视图,它从一些表和视图中检索以前和当前的办案人员(思考者)。问题是这些记录仅由结束日期 (saoh.Date_TO) 链接,与另一个案件官员的开始日期 (saoh.Date_FROM) 相同。
为了在这些记录之间创建连接,我目前正在执行外部连接和内部连接。 (这可以在下面的脚本中看到)。问题是视图有大约 300 万条记录。这意味着查询此视图需要很长时间(2-3 小时)。
有没有人对如何改进下面SQL的基本设计有什么建议。
更多信息
环境: MSSQL 服务器 2008
其他信息: Snapshot_Period 是一种报告工具,未链接到案件官员日期。
ALTER VIEW [dbo].[vw_Stage_Estate_Case_Officer_Source] AS
SELECT sp.SNAPSHOT_PERIOD_START_DATETIME,
sp.SNAPSHOT_PERIOD_END_DATETIME,
aes.APPLICATION_RID,
aes.ESTATE_RID,
aes.TRUSTEE_NUMBER,
aes.TRUSTEE_TYPE,
aes.TEAM_CODE,
saoh.POSITION,
saoh.DATE_FROM,
saoh.DATE_TO,
saoh.ERROR_CONDITION,
saoch.USER_ID as PRIOR_CASE_OFFICER
FROM Stage_App_Estate_Statuses aes
/*Standard snapshot period new join for MonthlyITS and yearlyTIS*/
INNER JOIN Stage_Snapshot_Period_New sp ON
change_date < sp.SNAPSHOT_PERIOD_END_DATETIME and
sp.SNAPSHOT_PERIOD_IS_FINALISED_INDICATOR = 'No'and
(sp.SNAPSHOT_PERIOD_TYPE_NAME = 'MonthlyITS' or sp.SNAPSHOT_PERIOD_TYPE_NAME = 'YearlyITS')
/*This should be inner joining to the staging table that links Case officers to team codes by region*/
INNER JOIN [DEV_STAGING].[dbo].[STAGE_STAF_ACTION_OFFICERS] saoh ON
aes.TEAM_CODE = saoh.POSITION AND LEFT(aes.ESTATE_RID,3) = LEFT(saoh.ACTION_OFFICER_RID,3)
/*This should be inner joining to App_Estate_Statues again to get the previous UserID*/
LEFT OUTER JOIN (SELECT staf.USER_ID,
staf.DATE_FROM,
staf.DATE_TO,
a.TEAM_CODE,
a.ESTATE_RID
FROM [DEV_STAGING].[dbo].[STAGE_STAF_ACTION_OFFICERS] staf
INNER JOIN Stage_App_Estate_Statuses a ON
a.TEAM_CODE = staf.POSITION) saoch ON
saoh.DATE_TO = saoch.DATE_FROM AND saoch.ESTATE_RID = aes.ESTATE_RID
GO
Table 定义
快照周期:无关/必须保持原样。
Stage App Estate 状态:
CREATE TABLE [dbo].[Stage_App_Estate_Statuses](
[ESTATE_STATUS_RID] [nvarchar](20) NOT NULL,
[ESTATE_RID] [nvarchar](30) NULL,
[CATEGORY] [decimal](1, 0) NULL,
[CHANGE_DATE] [datetime2](0) NULL,
[CHANGE_TYPE] [nvarchar](3) NULL,
[STATUS] [nvarchar](1) NULL,
[TEAM_CODE] [nvarchar](4) NULL,
[TRUSTEE_NUMBER] [decimal](22, 0) NULL,
[TRUSTEE_TYPE] [nvarchar](10) NULL,
[APPLICATION_RID] [nvarchar](30) NULL,
[DML_TYPE] [nvarchar](1) NOT NULL,
[AUDIT_KEY] [int] NOT NULL
) ON [PRIMARY]
Stage_STAF_ACTION_OFFICERS
CREATE TABLE [dbo].[Stage_STAF_ACTION_OFFICERS](
[ACTION_OFFICER_RID] [nvarchar](15) NOT NULL,
[POSITION] [nvarchar](13) NULL,
[DATE_FROM] [date] NULL,
[DATE_TO] [date] NULL,
[USER_ID] [nvarchar](32) NULL,
[ERROR_CONDITION] [nvarchar](100) NULL,
[EXTRACTED_DATE] [date] NULL,
[DML_TYPE] [nvarchar](1) NULL,
[AUDIT_KEY] [int] NOT NULL
) ON [PRIMARY]
很难没有任何东西可以测试,所以就把它作为一个起点
SELECT
sp.SNAPSHOT_PERIOD_START_DATETIME,
sp.SNAPSHOT_PERIOD_END_DATETIME,
aes.APPLICATION_RID,
aes.ESTATE_RID,
aes.TRUSTEE_NUMBER,
aes.TRUSTEE_TYPE,
aes.TEAM_CODE,
saoh.POSITION,
saoh.DATE_FROM,
saoh.DATE_TO,
saoh.ERROR_CONDITION,
(select USER_ID
from [DEV_STAGING].[dbo].[STAGE_STAF_ACTION_OFFICERS] sao
where sao.DATE_FROM = saoh.DATE_TO
and (select ESTATE_RID from Stage_App_Estate_Statuses where TEAM_CODE = sao.POSITION) = aes.ESTATE_RID
)as PRIOR_CASE_OFFICER --Even if this approach doesnt help, keep the original join as outer because there might not be a prior_case_officer
FROM
Stage_App_Estate_Statuses aes
/<em>This should be inner joining to the staging table that links Case officers to team codes by region</em>/
INNER JOIN [DEV_STAGING].[dbo].[STAGE_STAF_ACTION_OFFICERS] saoh
ON aes.TEAM_CODE = saoh.POSITION
AND LEFT(aes.ESTATE_RID,3) = LEFT(saoh.ACTION_OFFICER_RID,3)
/<em>Standard snapshot period new join for MonthlyITS and yearlyTIS</em>/
INNER JOIN (select<br>
SNAPSHOT_PERIOD_START_DATETIME,
SNAPSHOT_PERIOD_END_DATETIME,<br>
from Stage_Snapshot_Period_New
where
SNAPSHOT_PERIOD_IS_FINALISED_INDICATOR = 'No'
and SNAPSHOT_PERIOD_TYPE_NAME in ('MonthlyITS','YearlyITS')
) sp
ON change_date < sp.SNAPSHOT_PERIOD_END_DATETIME </pre>
在这种情况下,您可以将 "inner join in the outer join" 变成多个左连接,尽管您的 where 子句随后需要处理子查询中的内部连接所处理的情况,例如:
... query same up to the left join ...
/*This should be inner joining to App_Estate_Statues again to get the previous UserID*/
LEFT OUTER JOIN [dbo].[STAGE_STAF_ACTION_OFFICERS] saoch
ON saoh.DATE_TO = saoch.DATE_FROM
AND saoch.POSITION = aes.TEAM_CODE
LEFT OUTER JOIN Stage_App_Estate_Statuses a ON
a.TEAM_CODE = saoch.POSITION
AND a.ESTATE_RID = aes.ESTATE_RID
WHERE (saoch.ACTION_OFFICER_RID IS NULL)
OR (saoch.ACTION_OFFICER_RID IS NOT NULL AND a.ESTATE_STATUS_RID IS NOT NULL)
我简化了表格以删除不相关的位并添加了一些虚拟数据;对于虚拟数据,我使用此查询 returns 与原始结果相同但查询计划不同——这意味着您必须对其进行测试以确定其性能是否更好。
我从上面的答案中得到灵感后的最终工作代码。
WITH curr AS
(SELECT est.ESTATE_RID
,est.TEAM_CODE
,staff.POSITION
,ISNULL(staff.USER_ID, su.System_User_ID) as USERID
,est.CHANGE_DATE
,ROW_NUMBER() OVER (ORDER BY est.ESTATE_RID DESC, est.CHANGE_DATE ASC) AS RowNum
FROM Stage_App_Estate_Statuses est
LEFT OUTER JOIN Stage_STAF_ACTION_OFFICERS staff ON
est.TEAM_CODE = staff.POSITION and
est.CHANGE_DATE BETWEEN staff.DATE_FROM AND staff.DATE_TO
LEFT OUTER JOIN Stage_System_User_Mapping su on
est.TEAM_CODE = su.System_User_ID
WHERE CHANGE_TYPE LIKE '%T%'
)
SELECT curr.ESTATE_RID,
curr.TEAM_CODE,
ISNULL(curr.USERID,curr.TEAM_CODE) AS Current_UserID,
prev.USERID AS Previous_UserID,
curr.CHANGE_DATE as Date_From,
helper.CHANGE_DATE as Date_To
FROM curr
LEFT OUTER JOIN curr prev ON curr.RowNum = (prev.RowNum + 1) and
curr.ESTATE_RID = prev.ESTATE_RID
LEFT OUTER JOIN curr helper ON curr.RowNum = (helper.RowNum - 1) and
curr.ESTATE_RID = helper.ESTATE_RID
ORDER BY ESTATE_RID ASC, curr.CHANGE_DATE ASC
我在我的一个数据库中有一个视图,它从一些表和视图中检索以前和当前的办案人员(思考者)。问题是这些记录仅由结束日期 (saoh.Date_TO) 链接,与另一个案件官员的开始日期 (saoh.Date_FROM) 相同。
为了在这些记录之间创建连接,我目前正在执行外部连接和内部连接。 (这可以在下面的脚本中看到)。问题是视图有大约 300 万条记录。这意味着查询此视图需要很长时间(2-3 小时)。
有没有人对如何改进下面SQL的基本设计有什么建议。
更多信息
环境: MSSQL 服务器 2008
其他信息: Snapshot_Period 是一种报告工具,未链接到案件官员日期。
ALTER VIEW [dbo].[vw_Stage_Estate_Case_Officer_Source] AS
SELECT sp.SNAPSHOT_PERIOD_START_DATETIME,
sp.SNAPSHOT_PERIOD_END_DATETIME,
aes.APPLICATION_RID,
aes.ESTATE_RID,
aes.TRUSTEE_NUMBER,
aes.TRUSTEE_TYPE,
aes.TEAM_CODE,
saoh.POSITION,
saoh.DATE_FROM,
saoh.DATE_TO,
saoh.ERROR_CONDITION,
saoch.USER_ID as PRIOR_CASE_OFFICER
FROM Stage_App_Estate_Statuses aes
/*Standard snapshot period new join for MonthlyITS and yearlyTIS*/
INNER JOIN Stage_Snapshot_Period_New sp ON
change_date < sp.SNAPSHOT_PERIOD_END_DATETIME and
sp.SNAPSHOT_PERIOD_IS_FINALISED_INDICATOR = 'No'and
(sp.SNAPSHOT_PERIOD_TYPE_NAME = 'MonthlyITS' or sp.SNAPSHOT_PERIOD_TYPE_NAME = 'YearlyITS')
/*This should be inner joining to the staging table that links Case officers to team codes by region*/
INNER JOIN [DEV_STAGING].[dbo].[STAGE_STAF_ACTION_OFFICERS] saoh ON
aes.TEAM_CODE = saoh.POSITION AND LEFT(aes.ESTATE_RID,3) = LEFT(saoh.ACTION_OFFICER_RID,3)
/*This should be inner joining to App_Estate_Statues again to get the previous UserID*/
LEFT OUTER JOIN (SELECT staf.USER_ID,
staf.DATE_FROM,
staf.DATE_TO,
a.TEAM_CODE,
a.ESTATE_RID
FROM [DEV_STAGING].[dbo].[STAGE_STAF_ACTION_OFFICERS] staf
INNER JOIN Stage_App_Estate_Statuses a ON
a.TEAM_CODE = staf.POSITION) saoch ON
saoh.DATE_TO = saoch.DATE_FROM AND saoch.ESTATE_RID = aes.ESTATE_RID
GO
Table 定义
快照周期:无关/必须保持原样。
Stage App Estate 状态:
CREATE TABLE [dbo].[Stage_App_Estate_Statuses](
[ESTATE_STATUS_RID] [nvarchar](20) NOT NULL,
[ESTATE_RID] [nvarchar](30) NULL,
[CATEGORY] [decimal](1, 0) NULL,
[CHANGE_DATE] [datetime2](0) NULL,
[CHANGE_TYPE] [nvarchar](3) NULL,
[STATUS] [nvarchar](1) NULL,
[TEAM_CODE] [nvarchar](4) NULL,
[TRUSTEE_NUMBER] [decimal](22, 0) NULL,
[TRUSTEE_TYPE] [nvarchar](10) NULL,
[APPLICATION_RID] [nvarchar](30) NULL,
[DML_TYPE] [nvarchar](1) NOT NULL,
[AUDIT_KEY] [int] NOT NULL
) ON [PRIMARY]
Stage_STAF_ACTION_OFFICERS
CREATE TABLE [dbo].[Stage_STAF_ACTION_OFFICERS](
[ACTION_OFFICER_RID] [nvarchar](15) NOT NULL,
[POSITION] [nvarchar](13) NULL,
[DATE_FROM] [date] NULL,
[DATE_TO] [date] NULL,
[USER_ID] [nvarchar](32) NULL,
[ERROR_CONDITION] [nvarchar](100) NULL,
[EXTRACTED_DATE] [date] NULL,
[DML_TYPE] [nvarchar](1) NULL,
[AUDIT_KEY] [int] NOT NULL
) ON [PRIMARY]
很难没有任何东西可以测试,所以就把它作为一个起点
SELECT
sp.SNAPSHOT_PERIOD_START_DATETIME,
sp.SNAPSHOT_PERIOD_END_DATETIME,
aes.APPLICATION_RID,
aes.ESTATE_RID,
aes.TRUSTEE_NUMBER,
aes.TRUSTEE_TYPE,
aes.TEAM_CODE,
saoh.POSITION,
saoh.DATE_FROM,
saoh.DATE_TO,
saoh.ERROR_CONDITION,
(select USER_ID
from [DEV_STAGING].[dbo].[STAGE_STAF_ACTION_OFFICERS] sao
where sao.DATE_FROM = saoh.DATE_TO
and (select ESTATE_RID from Stage_App_Estate_Statuses where TEAM_CODE = sao.POSITION) = aes.ESTATE_RID
)as PRIOR_CASE_OFFICER --Even if this approach doesnt help, keep the original join as outer because there might not be a prior_case_officer
FROM
Stage_App_Estate_Statuses aes
/<em>This should be inner joining to the staging table that links Case officers to team codes by region</em>/
INNER JOIN [DEV_STAGING].[dbo].[STAGE_STAF_ACTION_OFFICERS] saoh
ON aes.TEAM_CODE = saoh.POSITION
AND LEFT(aes.ESTATE_RID,3) = LEFT(saoh.ACTION_OFFICER_RID,3)
/<em>Standard snapshot period new join for MonthlyITS and yearlyTIS</em>/
INNER JOIN (select<br>
SNAPSHOT_PERIOD_START_DATETIME,
SNAPSHOT_PERIOD_END_DATETIME,<br>
from Stage_Snapshot_Period_New
where
SNAPSHOT_PERIOD_IS_FINALISED_INDICATOR = 'No'
and SNAPSHOT_PERIOD_TYPE_NAME in ('MonthlyITS','YearlyITS')
) sp
ON change_date < sp.SNAPSHOT_PERIOD_END_DATETIME </pre>
在这种情况下,您可以将 "inner join in the outer join" 变成多个左连接,尽管您的 where 子句随后需要处理子查询中的内部连接所处理的情况,例如:
... query same up to the left join ...
/*This should be inner joining to App_Estate_Statues again to get the previous UserID*/
LEFT OUTER JOIN [dbo].[STAGE_STAF_ACTION_OFFICERS] saoch
ON saoh.DATE_TO = saoch.DATE_FROM
AND saoch.POSITION = aes.TEAM_CODE
LEFT OUTER JOIN Stage_App_Estate_Statuses a ON
a.TEAM_CODE = saoch.POSITION
AND a.ESTATE_RID = aes.ESTATE_RID
WHERE (saoch.ACTION_OFFICER_RID IS NULL)
OR (saoch.ACTION_OFFICER_RID IS NOT NULL AND a.ESTATE_STATUS_RID IS NOT NULL)
我简化了表格以删除不相关的位并添加了一些虚拟数据;对于虚拟数据,我使用此查询 returns 与原始结果相同但查询计划不同——这意味着您必须对其进行测试以确定其性能是否更好。
我从上面的答案中得到灵感后的最终工作代码。
WITH curr AS
(SELECT est.ESTATE_RID
,est.TEAM_CODE
,staff.POSITION
,ISNULL(staff.USER_ID, su.System_User_ID) as USERID
,est.CHANGE_DATE
,ROW_NUMBER() OVER (ORDER BY est.ESTATE_RID DESC, est.CHANGE_DATE ASC) AS RowNum
FROM Stage_App_Estate_Statuses est
LEFT OUTER JOIN Stage_STAF_ACTION_OFFICERS staff ON
est.TEAM_CODE = staff.POSITION and
est.CHANGE_DATE BETWEEN staff.DATE_FROM AND staff.DATE_TO
LEFT OUTER JOIN Stage_System_User_Mapping su on
est.TEAM_CODE = su.System_User_ID
WHERE CHANGE_TYPE LIKE '%T%'
)
SELECT curr.ESTATE_RID,
curr.TEAM_CODE,
ISNULL(curr.USERID,curr.TEAM_CODE) AS Current_UserID,
prev.USERID AS Previous_UserID,
curr.CHANGE_DATE as Date_From,
helper.CHANGE_DATE as Date_To
FROM curr
LEFT OUTER JOIN curr prev ON curr.RowNum = (prev.RowNum + 1) and
curr.ESTATE_RID = prev.ESTATE_RID
LEFT OUTER JOIN curr helper ON curr.RowNum = (helper.RowNum - 1) and
curr.ESTATE_RID = helper.ESTATE_RID
ORDER BY ESTATE_RID ASC, curr.CHANGE_DATE ASC