使用 Union Select 优化 SQL 查询
Posted
技术标签:
【中文标题】使用 Union Select 优化 SQL 查询【英文标题】:SQL Query Optimization with Union Select 【发布时间】:2016-01-16 10:59:47 【问题描述】:我有这个查询,它返回 570 行,但运行 2m 35s。在 SQL 中执行查询,但在我的解决方案中,它给出了超时。我怎样才能优化它以在 1m 下运行,pref 30s。
SELECT [Region] = Region.FirstName,
[Patient] = Patient.Name,
[PatientStatus] = AccountRating.Name,
[MedicalAid] = AccountType.Name,
[QuoteAmount] = ( SELECT TOP 1 A.Response
FROM dbo.Questionnaire Q
JOIN dbo.QuestionnaireDefinition QRD
ON QRD.QuestionnaireDefinitionID = Q.QuestionnaireDefinitionID
AND QRD.Name = 'Internal Admin'
LEFT JOIN QuestionDefinition QD
ON Q.QuestionnaireDefinitionID = QD.QuestionnaireDefinitionID
AND QD.QuestionDefinitionID = 5966
LEFT OUTER JOIN Answer A
ON A.QuestionnaireID = Q.QuestionnaireID
AND A.QuestionDefinitionID = QD.QuestionDefinitionID
WHERE Q.IsActive = 1
AND Q.SubscriberID = 240
AND Q.AccountID = Patient.AccountID
),
[InvoiceAmount] = ( SELECT TOP 1 A.Response
FROM dbo.Questionnaire Q
JOIN dbo.QuestionnaireDefinition QRD
ON QRD.QuestionnaireDefinitionID = Q.QuestionnaireDefinitionID
AND QRD.Name = 'Internal Admin'
LEFT JOIN QuestionDefinition QD
ON Q.QuestionnaireDefinitionID = QD.QuestionnaireDefinitionID
AND QD.QuestionDefinitionID = 5969
LEFT OUTER JOIN Answer A
ON A.QuestionnaireID = Q.QuestionnaireID
AND A.QuestionDefinitionID = QD.QuestionDefinitionID
WHERE Q.IsActive = 1
AND Q.SubscriberID = 240
AND Q.AccountID = Patient.AccountID
),
[DateSubmitted] = ( SELECT TOP 1 A.Response
FROM dbo.Questionnaire Q
JOIN dbo.QuestionnaireDefinition QRD
ON QRD.QuestionnaireDefinitionID = Q.QuestionnaireDefinitionID
AND QRD.Name = 'Internal Admin'
LEFT JOIN QuestionDefinition QD
ON Q.QuestionnaireDefinitionID = QD.QuestionnaireDefinitionID
AND QD.QuestionDefinitionID = 5965
LEFT OUTER JOIN Answer A
ON A.QuestionnaireID = Q.QuestionnaireID
AND A.QuestionDefinitionID = QD.QuestionDefinitionID
WHERE Q.IsActive = 1
AND Q.SubscriberID = 240
AND Q.AccountID = Patient.AccountID
),
[DateApprovedDeclined] = ( SELECT TOP 1 A.Response
FROM dbo.Questionnaire Q
JOIN dbo.QuestionnaireDefinition QRD
ON QRD.QuestionnaireDefinitionID = Q.QuestionnaireDefinitionID
AND QRD.Name = 'Internal Admin'
LEFT JOIN QuestionDefinition QD
ON Q.QuestionnaireDefinitionID = QD.QuestionnaireDefinitionID
AND QD.QuestionDefinitionID = 5968
LEFT OUTER JOIN Answer A
ON A.QuestionnaireID = Q.QuestionnaireID
AND A.QuestionDefinitionID = QD.QuestionDefinitionID
WHERE Q.IsActive = 1
AND Q.SubscriberID = 240
AND Q.AccountID = Patient.AccountID
),
[IntAdmFormCreatedDate]= Q.DateCreated,
[HasAdminForm] = 'Yes',
[CreatedByUser] = PatientCreatedBy.Name
FROM dbo.Account AS Patient
JOIN dbo.AccountRating
ON Patient.AccountRatingID = AccountRating.AccountRatingID
JOIN dbo.AccountType
ON Patient.AccountTypeID = AccountType.AccountTypeID
JOIN dbo.[User] Region
ON Patient.UserID = Region.UserID
JOIN dbo.[User] PatientCreatedBy
ON Patient.CreatedBy = PatientCreatedBy.UserID
JOIN dbo.Questionnaire Q
ON Patient.AccountID = Q.AccountID
WHERE Patient.SubscriberID = 240
AND (Q.DateCreated < DATEADD(D, 26, DATEADD(MONTH, DATEDIFF(MONTH, CONVERT(DATETIME, '1900-01-01 00:00:00', 102), GETDATE()), CONVERT(DATETIME, '1900-01-01 00:00:00', 102))))
AND Q.QuestionnaireDefinitionID = 235
AND Q.IsActive = 1
AND Region.FirstName <> 'Rubbish'
UNION SELECT [Region] = Region.FirstName,
[Patient] = Patient.Name,
[PatientStatus] = AccountRating.Name,
[MedicalAid] = AccountType.Name,
[QuoteAmount] = '0',
[InvoiceAmount] = '0',
[DateSubmitted] = '',
[DateApprovedDeclined] = '',
[IntAdmFormCreatedDate] = '',
[HasAdminForm] = 'No',
[CreatedByUser] = PatientCreatedBy.Name
FROM dbo.Account AS Patient
JOIN dbo.AccountRating
ON Patient.AccountRatingID = AccountRating.AccountRatingID
JOIN dbo.AccountType
ON Patient.AccountTypeID = AccountType.AccountTypeID
JOIN dbo.[User] AS Region
ON Patient.UserID = Region.UserID
JOIN dbo.[User] AS PatientCreatedBy
ON Patient.CreatedBy = PatientCreatedBy.UserID
WHERE NOT EXISTS( SELECT *
FROM Questionnaire AS Q
WHERE Patient.AccountID = Q.AccountID
AND Q.QuestionnaireDefinitionID = 235
AND Patient.SubscriberID = 240
AND Q.SubscriberID = 240
)
AND Patient.SubscriberID = 240
AND Patient.DateCreated < DATEADD(D, 26, DATEADD(MONTH, DATEDIFF(MONTH, CONVERT(DATETIME, '1900-01-01 00:00:00', 102), GETDATE()), CONVERT(DATETIME, '1900-01-01 00:00:00', 102)))
AND Region.FirstName <> 'Rubbish'
这是我尝试过的另一个版本的查询,但也在同一时间运行。
SELECT [Region] = Region.FirstName,
[Patient] = Patient.Name,
[PatientStatus] = AccountRating.Name,
[MedicalAid] = AccountType.Name,
[QuoteAmount] = Q1.Response,
[InvoiceAmount] = Q2.Response,
[DateSubmitted] = Q3.Response,
[DateApprovedDeclined] = Q4.Response,
[IntAdmFormCreatedDate]= Q.DateCreated,
[HasAdminForm] = 'Yes',
[CreatedByUser] = PatientCreatedBy.Name
FROM dbo.Account AS Patient
JOIN dbo.AccountRating
ON Patient.AccountRatingID = AccountRating.AccountRatingID
JOIN dbo.AccountType
ON Patient.AccountTypeID = AccountType.AccountTypeID
JOIN dbo.[User] Region
ON Patient.UserID = Region.UserID
JOIN dbo.[User] PatientCreatedBy
ON Patient.CreatedBy = PatientCreatedBy.UserID
JOIN dbo.Questionnaire Q
ON Patient.AccountID = Q.AccountID
OUTER APPLY
(
SELECT TOP 1 Q.AccountID,
A.Response
FROM dbo.Questionnaire Q
JOIN dbo.QuestionnaireDefinition QRD
ON QRD.QuestionnaireDefinitionID = Q.QuestionnaireDefinitionID
AND QRD.Name = 'Internal Admin'
LEFT JOIN QuestionDefinition QD
ON Q.QuestionnaireDefinitionID = QD.QuestionnaireDefinitionID
AND QD.QuestionDefinitionID = 5966
LEFT OUTER JOIN Answer A
ON A.QuestionnaireID = Q.QuestionnaireID
AND A.QuestionDefinitionID = QD.QuestionDefinitionID
WHERE Q.IsActive = 1
Q.SubscriberID = 240
AND Q.AccountID = Patient.AccountID
) Q1
OUTER APPLY
(
SELECT TOP 1 Q.AccountID,
A.Response
FROM dbo.Questionnaire Q
JOIN dbo.QuestionnaireDefinition QRD
ON QRD.QuestionnaireDefinitionID = Q.QuestionnaireDefinitionID
AND QRD.Name = 'Internal Admin'
LEFT JOIN QuestionDefinition QD
ON Q.QuestionnaireDefinitionID = QD.QuestionnaireDefinitionID
AND QD.QuestionDefinitionID = 5969
LEFT OUTER JOIN Answer A
ON A.QuestionnaireID = Q.QuestionnaireID
AND A.QuestionDefinitionID = QD.QuestionDefinitionID
WHERE Q.IsActive = 1
AND Q.AccountID = Patient.AccountID
) Q2
OUTER APPLY
(
SELECT TOP 1 Q.AccountID,
A.Response
FROM dbo.Questionnaire Q
JOIN dbo.QuestionnaireDefinition QRD
ON QRD.QuestionnaireDefinitionID = Q.QuestionnaireDefinitionID
AND QRD.Name = 'Internal Admin'
LEFT JOIN QuestionDefinition QD
ON Q.QuestionnaireDefinitionID = QD.QuestionnaireDefinitionID
AND QD.QuestionDefinitionID = 5965
LEFT OUTER JOIN Answer A
ON A.QuestionnaireID = Q.QuestionnaireID
AND A.QuestionDefinitionID = QD.QuestionDefinitionID
WHERE Q.IsActive = 1
AND Q.AccountID = Patient.AccountID
) Q3
OUTER APPLY
(
SELECT TOP 1 Q.AccountID,
A.Response
FROM dbo.Questionnaire Q
JOIN dbo.QuestionnaireDefinition QRD
ON QRD.QuestionnaireDefinitionID = Q.QuestionnaireDefinitionID
AND QRD.Name = 'Internal Admin'
LEFT JOIN QuestionDefinition QD
ON Q.QuestionnaireDefinitionID = QD.QuestionnaireDefinitionID
AND QD.QuestionDefinitionID = 5968
LEFT OUTER JOIN Answer A
ON A.QuestionnaireID = Q.QuestionnaireID
AND A.QuestionDefinitionID = QD.QuestionDefinitionID
WHERE Q.IsActive = 1
AND Q.AccountID = Patient.AccountID
) Q4
WHERE Patient.SubscriberID = 240
AND (Q.DateCreated < DATEADD(D, 26, DATEADD(MONTH, DATEDIFF(MONTH, CONVERT(DATETIME, '1900-01-01 00:00:00', 102), GETDATE()), CONVERT(DATETIME, '1900-01-01 00:00:00', 102))))
AND Q.QuestionnaireDefinitionID = 235
AND Q.IsActive = 1
AND Region.FirstName <> 'Rubbish'
UNION SELECT [Region] = Region.FirstName,
[Patient] = Patient.Name,
[PatientStatus] = AccountRating.Name,
[MedicalAid] = AccountType.Name,
[QuoteAmount] = '0',
[InvoiceAmount] = '0',
[DateSubmitted] = '',
[DateApprovedDeclined] = '',
[IntAdmFormCreatedDate] = '',
[HasAdminForm] = 'No',
[CreatedByUser] = PatientCreatedBy.Name
FROM dbo.Account AS Patient
JOIN dbo.AccountRating
ON Patient.AccountRatingID = AccountRating.AccountRatingID
JOIN dbo.AccountType
ON Patient.AccountTypeID = AccountType.AccountTypeID
JOIN dbo.[User] AS Region
ON Patient.UserID = Region.UserID
JOIN dbo.[User] AS PatientCreatedBy
ON Patient.CreatedBy = PatientCreatedBy.UserID
WHERE NOT EXISTS( SELECT *
FROM Questionnaire AS Q
WHERE Patient.AccountID = Q.AccountID
AND Q.QuestionnaireDefinitionID = 235
AND Patient.SubscriberID = 240
AND Q.SubscriberID = 240
)
AND Patient.SubscriberID = 240
AND Patient.DateCreated < DATEADD(D, 26, DATEADD(MONTH, DATEDIFF(MONTH, CONVERT(DATETIME, '1900-01-01 00:00:00', 102), GETDATE()), CONVERT(DATETIME, '1900-01-01 00:00:00', 102)))
AND Region.FirstName <> 'Rubbish'
【问题讨论】:
我建议你再问一个问题。描述你试图解决的问题和你正在使用的数据。您的查询看起来太复杂了。 @GordonLinoff,我正在尝试优化我的查询,dnoeth 帮助了我,我已经解决了。非常感谢您的反馈 【参考方案1】:您的标量子查询都共享相同的连接,只是 QD.QuestionDefinitionID 不同。
您可以使用单个派生表重写这 4 个 TOP 并加入它:
...
LEFT JOIN
(
SELECT
Q.AccountID,
MAX(CASE WHEN QD.QuestionDefinitionID = 5966 THEN A.Response END) AS [DateSubmitted]
MAX(CASE WHEN QD.QuestionDefinitionID = 5968 THEN A.Response END) AS [DateApprovedDeclined]
...
FROM dbo.Questionnaire Q
JOIN dbo.QuestionnaireDefinition QRD
ON QRD.QuestionnaireDefinitionID = Q.QuestionnaireDefinitionID
AND QRD.NAME = 'Internal Admin'
LEFT JOIN QuestionDefinition QD
ON Q.QuestionnaireDefinitionID = QD.QuestionnaireDefinitionID
LEFT OUTER JOIN Answer A
ON A.QuestionnaireID = Q.QuestionnaireID
AND A.QuestionDefinitionID = QD.QuestionDefinitionID
WHERE Q.IsActive = 1
AND Q.SubscriberID = 240
GROUP BY Q.AccountID
) AS Q
ON Q.AccountID = Patient.AccountID
我使用 MAX 是因为您的子查询中没有 ORDER BY,所以确切的值要么无关紧要,要么每个值只有一行。
【讨论】:
哇,组合查询的好方法...谢谢。 我的查询花了 38 秒,但仍然超时,将尝试对此进行更多优化,但非常感谢您的努力!!! @Hennie:您可能需要添加一些索引。分别运行两个 SELECT 并检查性能。您可能可以切换到 UNION ALL 以避免 DISTINCT 处理(但我认为这实际上不会改善时间) 我正忙着将选择分开以查看哪个部分较慢,我也会尝试联合所有。以上是关于使用 Union Select 优化 SQL 查询的主要内容,如果未能解决你的问题,请参考以下文章