使用 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 查询的主要内容,如果未能解决你的问题,请参考以下文章

mysql sql优化之expain

MySQL执行计划

sql 查询结果合并union all用法_数据库技巧

union all 效率问题

SQL学习之组合查询(UNION)

SQL-子查询;union;limit