1

大家下午好。我将在它的全部荣耀中发布存储过程。随意将其撕成碎片。作者不会介意的。

DECLARE @itemTypeID INT
SELECT @itemTypeID=ItemTypeID FROM dbo.ItemTypes WHERE ItemTypeName = 'Advert'

BEGIN
 SELECT a.Active,
   a.ParentClass,
   a.Classification,
   a.Variant,
   FV."Full Views",
   PV."Print Views",
   EE."Email Enquiries",
   a.ItemRef,
   a.SiteID
 FROM 
 (
 SELECT DISTINCT i.ItemID,
   i.ItemRef,
   i.SiteID,
   i.ParentClass,
   i.Classification,
   i.Summary AS "Variant",       
   i.Active
 FROM Items i
 JOIN Actions a
 ON a.ItemID = i.ItemID
 JOIN ActionTypes at 
 ON a.ActionTypeID = at.ActionTypeID
 WHERE i.ItemTypeID = 1
 AND a.DateAndTime BETWEEN @startDate AND @endDate
 AND at.ActionTypeName IN ('Full view', 'Print view', 'Email enquiry')
 AND ((@siteID = -1) OR (i.SiteID = @siteID))
 AND ((@parentClass = '%') OR (i.ParentClass = @parentClass))
 AND ((@class = '%') OR (i.classification = @class))
 ) a LEFT JOIN
 (
 SELECT i.ItemID, 
   COUNT(*) AS "Full Views"
 FROM CustomerSites cs JOIN Items i
 ON cs.SiteID = i.SiteID
 JOIN Actions a
 ON a.ItemID = i.ItemID
 JOIN ActionTypes at 
 ON a.ActionTypeID = at.ActionTypeID
 JOIN Sites s
 ON cs.SiteID = s.SiteID
 WHERE a.DateAndTime BETWEEN @startDate AND @endDate
 AND i.ItemTypeID = @itemTypeID
 AND at.ActionTypeName = 'Full view'
 AND ((@customerID IS NULL) OR (cs.CustomerID = @customerID))
 AND ((@siteID = -1) OR (cs.SiteID = @siteID))
 AND ((@parentClass = '%') OR (i.ParentClass = @parentClass))
 AND ((@class = '%') OR (i.classification = @class))
 GROUP BY
   i.ItemID
 ) FV 
 ON a.ItemID = FV.ItemID
 LEFT JOIN
 (
 SELECT i.ItemID,
   COUNT(*) AS "Print Views"
 FROM CustomerSites cs JOIN Items i
 ON cs.SiteID = i.SiteID
 JOIN Actions a
 ON a.ItemID = i.ItemID
 JOIN ActionTypes at 
 ON a.ActionTypeID = at.ActionTypeID
 JOIN Sites s
 ON cs.SiteID = s.SiteID
 WHERE a.DateAndTime BETWEEN @startDate AND @endDate
 AND i.ItemTypeID = @itemTypeID
 AND at.ActionTypeName = 'Print view'
 AND ((@customerID IS NULL) OR (cs.CustomerID = @customerID))
 AND ((@siteID = -1) OR (cs.SiteID = @siteID))
 AND ((@parentClass = '%') OR (i.ParentClass = @parentClass))
 AND ((@class = '%') OR (i.classification = @class))
 GROUP BY
   i.ItemID
 ) PV 
 ON a.ItemID = PV.ItemID
 LEFT JOIN
 (
 SELECT i.ItemID,
   COUNT(*) AS "Email Enquiries"
 FROM CustomerSites cs JOIN Items i
 ON cs.SiteID = i.SiteID
 JOIN Actions a
 ON a.ItemID = i.ItemID
 JOIN ActionTypes at 
 ON a.ActionTypeID = at.ActionTypeID
 JOIN Sites s
 ON cs.SiteID = s.SiteID
 WHERE a.DateAndTime BETWEEN @startDate AND @endDate
 AND i.ItemTypeID = @itemTypeID
 AND at.ActionTypeName = 'Email enquiry'
 AND ((@customerID IS NULL) OR (cs.CustomerID = @customerID))
 AND ((@siteID = -1) OR (cs.SiteID = @siteID))
 AND ((@parentClass = '%') OR (i.ParentClass = @parentClass))
 AND ((@class = '%') OR (i.classification = @class))
 GROUP BY
   i.ItemID
 ) EE
 ON a.ItemID = EE.ItemID
 UNION
 SELECT '0','','','','','','','',''

现在最终所有这一切都是返回一些记录以及针对它们发生特定操作的次数。

一个小子集看起来像。

项目 描述 查看打印的电子邮件

Item1 Desc1 12 NULL 1
Item2 Desc2 NULL NULL 3
Item3 Desc3 5 6 2

希望你能看到发生了什么。

我想要一个在特定日期范围内针对特定站点的特定客户对它们执行过操作的项目列表,并且查询应该可以根据父类和分类进行过滤。好的

第一个选择返回所有符合选择条件的不同项目。

其他 3 个查询都只返回针对每个项目的 1 种操作类型的计数。即使针对少量数据,查询也很慢。这永远不会上线,它只是行不通。

希望您能看到“作者”方式的错误并纠正他/她。

4

5 回答 5

5

这是我的格式样式的原始查询:

SELECT  
    a.Active
        ,a.ParentClass
        ,a.Classification
        ,a.Variant
        ,FV."Full Views"
        ,PV."Print Views"
        ,EE."Email Enquiries"
        ,a.ItemRef
        ,a.SiteID
    FROM (SELECT DISTINCT
              i.ItemID,
                  ,i.ItemRef
                  ,i.SiteID
                  ,i.ParentClass
                  ,i.Classification
                  ,i.Summary AS "Variant"
                  ,i.Active
              FROM Items              i
                  JOIN Actions        a ON a.ItemID = i.ItemID
                  JOIN ActionTypes   at ON a.ActionTypeID = at.ActionTypeID
              WHERE i.ItemTypeID = 1
                  AND a.DateAndTime BETWEEN @startDate AND @endDate
                  AND at.ActionTypeName IN ('Full view', 'Print view', 'Email enquiry')
                  AND ((@siteID = -1) OR (i.SiteID = @siteID))
                  AND ((@parentClass = '%') OR (i.ParentClass = @parentClass))
                  AND ((@class = '%') OR (i.classification = @class))
        ) a
        LEFT JOIN (SELECT  
                       i.ItemID
                           ,COUNT(*) AS "Full Views"
                       FROM CustomerSites          cs
                           JOIN Items               i ON cs.SiteID = i.SiteID
                           JOIN Actions             a ON a.ItemID = i.ItemID
                           JOIN ActionTypes        at ON a.ActionTypeID = at.ActionTypeID
                           JOIN Sites               s ON cs.SiteID = s.SiteID
                      WHERE a.DateAndTime BETWEEN @startDate AND @endDate
                          AND i.ItemTypeID = @itemTypeID
                          AND at.ActionTypeName = 'Full view'
                          AND ((@customerID IS NULL) OR (cs.CustomerID = @customerID))
                          AND ((@siteID = -1) OR (cs.SiteID = @siteID))
                          AND ((@parentClass = '%') OR (i.ParentClass = @parentClass))
                          AND ((@class = '%') OR (i.classification = @class))
                      GROUP BY i.ItemID
                  ) FV ON a.ItemID = FV.ItemID
        LEFT JOIN (SELECT
                       i.ItemID
                           ,COUNT(*) AS "Print Views"
                       FROM CustomerSites      cs 
                           JOIN Items           i ON cs.SiteID = i.SiteID
                           JOIN Actions         a ON a.ItemID = i.ItemID
                           JOIN ActionTypes    at ON a.ActionTypeID = at.ActionTypeID
                           JOIN Sites           s ON cs.SiteID = s.SiteID
                       WHERE a.DateAndTime BETWEEN @startDate AND @endDate
                           AND i.ItemTypeID = @itemTypeID
                           AND at.ActionTypeName = 'Print view'
                           AND ((@customerID IS NULL) OR (cs.CustomerID = @customerID))
                           AND ((@siteID = -1) OR (cs.SiteID = @siteID))
                           AND ((@parentClass = '%') OR (i.ParentClass = @parentClass))
                           AND ((@class = '%') OR (i.classification = @class))
                       GROUP BY i.ItemID
                  ) PV ON a.ItemID = PV.ItemID
        LEFT JOIN (SELECT
                       i.ItemID
                           ,COUNT(*) AS "Email Enquiries"
                       FROM CustomerSites   cs
                           JOIN Items        i ON cs.SiteID = i.SiteID
                           JOIN Actions      a ON a.ItemID = i.ItemID
                           JOIN ActionTypes at ON a.ActionTypeID = at.ActionTypeID
                           JOIN Sites        s ON cs.SiteID = s.SiteID
                       WHERE a.DateAndTime BETWEEN @startDate AND @endDate
                           AND i.ItemTypeID = @itemTypeID
                           AND at.ActionTypeName = 'Email enquiry'
                           AND ((@customerID IS NULL) OR (cs.CustomerID = @customerID))
                           AND ((@siteID = -1) OR (cs.SiteID = @siteID))
                           AND ((@parentClass = '%') OR (i.ParentClass = @parentClass))
                           AND ((@class = '%') OR (i.classification = @class))
                       GROUP BY i.ItemID
                  ) EE ON a.ItemID = EE.ItemID
UNION
SELECT '0','','','','','','','',''

这应该会有所帮助:

;WITH CustomerSitesCounts AS
(
SELECT
    at.ActionTypeName
        ,i.ItemID
        ,COUNT(*) AS "Print Views"
    FROM CustomerSites      cs 
        JOIN Items           i ON cs.SiteID = i.SiteID
        JOIN Actions         a ON a.ItemID = i.ItemID
        JOIN ActionTypes    at ON a.ActionTypeID = at.ActionTypeID
        JOIN Sites           s ON cs.SiteID = s.SiteID
    WHERE a.DateAndTime BETWEEN @startDate AND @endDate
        AND i.ItemTypeID = @itemTypeID
        AND at.ActionTypeName IN ( 'Print view','Full view','Email enquiry')
        AND ((@customerID IS NULL) OR (cs.CustomerID = @customerID))
        AND ((@siteID = -1) OR (cs.SiteID = @siteID))
        AND ((@parentClass = '%') OR (i.ParentClass = @parentClass))
        AND ((@class = '%') OR (i.classification = @class))
    GROUP BY at.ActionTypeName,i.ItemID
)
SELECT  
    a.Active
        ,a.ParentClass
        ,a.Classification
        ,a.Variant
        ,FV."Full Views"
        ,PV."Print Views"
        ,EE."Email Enquiries"
        ,a.ItemRef
        ,a.SiteID
    FROM (SELECT DISTINCT
              i.ItemID,
                  ,i.ItemRef
                  ,i.SiteID
                  ,i.ParentClass
                  ,i.Classification
                  ,i.Summary AS "Variant"
                  ,i.Active
              FROM Items              i
                  JOIN Actions        a ON a.ItemID = i.ItemID
                  JOIN ActionTypes   at ON a.ActionTypeID = at.ActionTypeID
              WHERE i.ItemTypeID = 1
                  AND a.DateAndTime BETWEEN @startDate AND @endDate
                  AND at.ActionTypeName IN ('Full view', 'Print view', 'Email enquiry')
                  AND ((@siteID = -1) OR (i.SiteID = @siteID))
                  AND ((@parentClass = '%') OR (i.ParentClass = @parentClass))
                  AND ((@class = '%') OR (i.classification = @class))
        ) a
        LEFT JOIN CustomerSitesCounts FV ON a.ItemID = FV.ItemID AND FV.ActionTypeName='Full view'
        LEFT JOIN CustomerSitesCounts PV ON a.ItemID = PV.ItemID AND PV.ActionTypeName='Print view'
        LEFT JOIN CustomerSitesCounts EE ON a.ItemID = EE.ItemID AND EE.ActionTypeName='Email enquiry'
UNION
SELECT '0','','','','','','','',''
于 2009-09-04T14:08:39.123 回答
4

问题:

  1. “包罗万象”类型的查询无法优化。解决方案:参数化动态SQL。
  2. UNION 强制排序。解决方案:改用 UNION ALL (除非你真的需要它强制的隐式 DISTINCT)。

这应该是最快的解决方案:

DECLARE @sql AS VARCHAR(MAX)

SELECT @sql = '
;WITH cteCommon as (
    SELECT 
        i.ItemID,
        ,i.ItemRef
        ,i.SiteID
        ,i.ParentClass
        ,i.Classification
        ,i.Summary
        ,i.Active
        ,at.ActionTypeName
    FROM Items              i
        JOIN Actions        a ON a.ItemID = i.ItemID
        JOIN ActionTypes   at ON a.ActionTypeID = at.ActionTypeID
    WHERE at.ActionTypeName IN (''Full view'', ''Print view'', ''Email enquiry'')
        --NOTE: if you max-out this date range, then you mant want to exclude it also, RBarryYoung
        AND a.DateAndTime BETWEEN @startDate AND @endDate 
        ' 
        + CASE @siteid WHEN -1 THEN '' ELSE 'AND (i.SiteID = @siteID) 
        ' END
        + CASE @parentClass WHEN '%' THEN '' ELSE 'AND (i.ParentClass = @parentClass)
        ' END
        + CASE @class WHEN '%' THEN '' ELSE 'AND (i.classification = @class)
        ' END
        + '
)
, cteA as (
    SELECT DISTINCT
          i.ItemID,
          ,i.ItemRef
          ,i.SiteID
          ,i.ParentClass
          ,i.Classification
          ,i.Summary AS "Variant"
          ,i.Active
      FROM cteA as i
      WHERE i.ItemTypeID = 1
)
, cteCountViews AS (
    SELECT  
        i.ItemID
        ,i.ActionType
        ,COUNT(*) AS "ViewCount"
    FROM cteCommon i
        JOIN CustomerSites       cs ON cs.SiteID = i.SiteID
        JOIN Sites               s ON cs.SiteID = s.SiteID
    WHERE i.ItemTypeID = @itemTypeID
        '
        + CASE WHEN @customerid IS NULL THEN '' ELSE '(cs.CustomerID = @customerID)' END
        + '
    GROUP BY i.ItemID
        ,i.ActionType
    )
SELECT  
    a.Active
    ,a.ParentClass
    ,a.Classification
    ,a.Variant
    ,FV."Full Views"
    ,PV."Print Views"
    ,EE."Email Enquiries"
    ,a.ItemRef
    ,a.SiteID
FROM cteA AS a
LEFT JOIN (
        SELECT i.ItemID, ViewCount AS "Full Views"
        FROM cteCountViews i
        WHERE i.ActionTypeName = ''Full view''
    ) FV ON a.ItemID = FV.ItemID
LEFT JOIN (
        SELECT i.ItemID, ViewCount AS "Print Views"
        FROM cteCountViews i
        WHERE i.ActionTypeName = ''Print view''
    ) PV ON a.ItemID = PV.ItemID
LEFT JOIN (
        SELECT i.ItemID, ViewCount AS "Email Enquiries"
        FROM cteCountViews i
        WHERE i.ActionTypeName = ''Email enquiry''
      ) EE ON a.ItemID = EE.ItemID
UNION ALL
SELECT ''0'','''','''','''','''','''','''','''',''''
'
EXEC sp_ExecuteSQL @sql
    ,'@startdate DATETIME,@enddate DATETIME,@siteid INT,@parentclass VARCHAR(MAX),@class VARCHAR(MAX),@itemtypeid INT,@customerid INT'
    , @startdate, @enddate, @siteid, @parentclass, @class, @itemtypeid, @customerid

注意:您在其中一些(类、站点等)上使用通配符和 JOIN 可能会导致源行和大量结果集的交叉乘法。

于 2009-09-04T16:48:36.743 回答
1

首先,您可以避免拥有 3 个绝对相同的子查询的副本,购买一个通用的子查询并使用 WITH 语句,以便您可以重复使用它。

然后,为什么在不需要时进行子选择。

然后,再次删除一些您不需要的连接(以及因此的 DISTINCT)。

你会得到一些东西(自然没有经过测试):

DECLARE @itemTypeID INT
SELECT @itemTypeID=ItemTypeID FROM dbo.ItemTypes WHERE ItemTypeName = 'Advert'

BEGIN
    WITH ItemTypeSummary
    AS (SELECT  i.ItemID,
                at.ActionTypeName,
                COUNT(*) AS CNT
        FROM    Items i
        JOIN    Actions a
            ON  a.ItemID = i.ItemID
        JOIN    ActionTypes at 
            ON  a.ActionTypeID = at.ActionTypeID
            AND at.ActionTypeName IN ('Full view', 'Print view', 'Email enquiry')
        WHERE   a.DateAndTime BETWEEN @startDate AND @endDate
            --// not sure you need those below as they are all part of Items filter anyways in the main query
            /*
            AND i.ItemTypeID = @itemTypeID
            AND ((@customerID IS NULL) OR (cs.CustomerID = @customerID))
            AND ((@siteID = -1) OR (cs.SiteID = @siteID))
            AND ((@parentClass = '%') OR (i.ParentClass = @parentClass))
            AND ((@class = '%') OR (i.classification = @class))
            */
        GROUP BY    i.ItemID,
                    at.ActionTypeName
    )

    SELECT DISTINCT i.ItemID,
                    i.ItemRef,
                    i.SiteID,
                    i.ParentClass,
                    i.Classification,
                    i.Summary AS "Variant",
                    i.Active,
                    FV.CNT AS "Full views",
                    PV.CNT AS "Print views",
                    EE.CNT AS "Email enquiries"
    FROM        Items i
    JOIN        CustomerSites cs
            ON  cs.SiteID = i.SiteID
    LEFT JOIN   ItemTypeSummary FV
            ON  i.ItemID = FV.ItemID
            AND FV.ActionTypeName = 'Full view'
    LEFT JOIN   ItemTypeSummary PV
            ON  i.ItemID = PV.ItemID
            AND PV.ActionTypeName = 'Print view'
    LEFT JOIN   ItemTypeSummary EE
            ON  i.ItemID = EE.ItemID
            AND EE.ActionTypeName = 'Email enquiry'

    WHERE   i.ItemTypeID = @itemTypeID
        AND ((@customerID IS NULL) OR (cs.CustomerID = @customerID))
        AND ((@siteID = -1) OR (i.SiteID = @siteID))
        AND ((@parentClass = '%') OR (i.ParentClass = @parentClass))
        AND ((@class = '%') OR (i.classification = @class))
END

还要记住,如果可以避免使用这些 OR 语句来过滤所有或特定项目,这并不是很酷,因为 SQLServer 将无法生成最佳执行计划。

于 2009-09-04T14:16:32.577 回答
1

另一种选择是设置表变量或临时#Table 来保存结果。在 ItemID 上添加唯一约束。

将子查询分解为单独的步骤。首先将要计数的记录插入到此表中。对每种视图类型的数据运行单独的更新——更新打印视图计数、更新完整视图计数、更新电子邮件查询计数。然后返回结果。如有必要,将 OR 条件拆分为单独的查询。

这种方法多次遍历您的数据,但它避免了未编制索引的 LEFT JOIN 和子查询。

在我们的应用程序中,具有多个步骤似乎比一个非常复杂的查询执行得更好。您的结果可能会有所不同。

于 2009-09-04T15:28:30.187 回答
1

重写:

WITH base AS (
  SELECT i.ItemID,
         i.ItemRef,
         i.SiteID,
         i.ParentClass,
         i.Classification,
         i.Summary,
         i.Active,
         i.ItemTypeID,
         at.ActionTypeName 
    FROM ITEMS i
    JOIN ACTIONS a ON a.ItemID = i.ItemID
    JOIN ACTIONTYPES at ON at.ActionTypeID = a.ActionTypeID
   WHERE a.DateAndTime BETWEEN @startDate AND @endDate
     AND (@siteID = -1 OR i.SiteID = @siteID)
     AND (@parentClass = '%' OR i.ParentClass = @parentClass)
     AND (@class = '%' OR i.classification = @class)),
     items AS (
    SELECT b.ItemID,
           b.ItemRef,
           b.SiteID,
           b.ParentClass,
           b.Classification,
           b.Summary AS "Variant",
           b.Active
      FROM base b
     WHERE b.itemtypeid = 1
       AND b.actiontypename IN ('Full view', 'Print view', 'Email enquiry')
  GROUP BY i.ItemID, i.ItemRef, i.SiteID, i.ParentClass, i.Classification, i.Summary, i.Active),
     full_views AS (
    SELECT b.ItemID, 
           COUNT(*) AS num_full_Views
      FROM base b
      JOIN CUSTOMERSITES cs ON cs.siteid = b.siteid
      JOIN SITES s ON s.siteid = b.siteid
     WHERE b.itemtypeid = @itemTypeID
       AND b.ActionTypeName = 'Full view'
       AND (@customerID IS NULL OR cs.CustomerID = @customerID)
  GROUP BY b.itemid),
     print_views AS (
   SELECT b.ItemID, 
          COUNT(*) AS num_print_views
     FROM base b
     JOIN CUSTOMERSITES cs ON cs.siteid = b.siteid
     JOIN SITES s ON s.siteid = b.siteid
    WHERE b.itemtypeid = @itemTypeID
      AND b.ActionTypeName = 'Print view'
      AND (@customerID IS NULL OR cs.CustomerID = @customerID)
 GROUP BY b.itemid),
     email_queries AS (
 SELECT b.ItemID, 
        COUNT(*) AS num_email_enquiries
   FROM base b
   JOIN CUSTOMERSITES cs ON cs.siteid = b.siteid
  JOIN SITES s ON s.siteid = b.siteid
 WHERE b.itemtypeid = @itemTypeID
       AND b.ActionTypeName = 'Email enquiry'
       AND (@customerID IS NULL OR cs.CustomerID = @customerID)
  GROUP BY b.itemid)
   SELECT a.Active,
          a.ParentClass,
          a.Classification,
          a.Variant,
          ISNULL(fv.num_full_Views, 0) AS "Full Views",
          ISNULL(pv.num_print_views, 0) AS "Print Views",
          ISNULL(ee.num_email_enquiries, 0) AS "Email Enquiries",
          a.ItemRef,
          a.SiteID
     FROM items a
LEFT JOIN full_views fv ON fv.itemid = a.itemid
LEFT JOIN print_views pv ON pv.itemid = a.itemid
LEFT JOIN email_queries ee ON ee.itemid = a.itemid

为了获得更好的性能,我会将其转换为动态 SQL,以删除如下参数检查:

AND (@siteID = -1 OR i.SiteID = @siteID)

...因为对sargability的负面影响。

于 2009-09-05T03:11:24.780 回答