嵌套查询和使用临时表使它们更具可读性

时间:2014-01-12 12:17:04

标签: sql-server tsql

我有一个选择查询,它有很多连接,它有5个内部查询。

使用嵌套查询时,它看起来像

Query(NestedQuery1(NestedQuery2(NestedQuery3(NestedQuery4(NestedQuery5)as C)as M)as X)as D)

它变得非常混乱......

另一方面,我可以像

那样做
NestedQuery5 INTO #C
NestedQuery4 INTO #M From #C 
.
.
.
Query From D

我的问题是你会使用嵌套或使用临时表的哪一个?两者都不是一个选项

p.s我是一个不到一周的SQL经验的菜鸟,但无论我使用第一种方法还是第二种方法,查询都能正常工作。

/*set period*/
DECLARE @DATE1 DATETIME 
SET @DATE1 ='01.01.2000' 
DECLARE @DATE2 DATETIME 
SET @DATE2 = '01.01.2050' 

SELECT contactid, 
       country, 
       city, 
       itemsbought, 
       lastlogindate, 
       email, 
       lastbuydate, 
       openscount, 
       clickscount, 
       newslettersrecived, 
       averagereadtime, 
       lastnewsletterdate, 
       menge                                        'BuyCount', 
       ( Rank() 
           OVER ( 
             ORDER BY menge DESC) )                 mengeRank, 
       ekavg                                        'EKØ', 
       ( Rank() 
           OVER ( 
             ORDER BY ekavg DESC) )                 ekavgRank, 
       ekt                                          'EKTotal', 
       ( Rank() 
           OVER ( 
             ORDER BY ekt DESC) )                   ektRank, 
       vkavg                                        'VKØ', 
       ( Rank() 
           OVER ( 
             ORDER BY vkavg DESC) )                 vkavgRank, 
       vkt                                          'VKTotal', 
       ( Rank() 
           OVER ( 
             ORDER BY vkt DESC) )                   vktRank, 
       XS.margent / XS.menge                        'MargenØ', 
       ( Rank() 
           OVER ( 
             ORDER BY XS.margent / XS.menge DESC) ) MarginAVGRank, 
       margent                                      'MargenTotal', 
       ( Rank() 
           OVER ( 
             ORDER BY margent DESC) )               margentotalRank, 
       CASE 
         WHEN Isnull(vkt, 0) != 0 
              AND Isnull(menge, 0) > 1 THEN ( ( vkt - ekt ) / vkt ) * 100 
         ELSE 0 
       END                                          AS 'Margen%' 
FROM   (SELECT RR.*, 
               CASE 
                 WHEN Isnull(menge, 0) != 0 THEN ekt / menge 
                 ELSE NULL 
               END       AS EKAVG, 
               vkt - ekt MargenT, 
               CASE 
                 WHEN Isnull(menge, 0) != 0 THEN vkt / menge 
                 ELSE NULL 
               END       AS VKAVG 
        FROM   (SELECT Max(orderdate) lastbuydate, 
                       itemsbought, 
                       contactid, 
                       country, 
                       city, 
                       email, 
                       lastlogindate, 
                       Sum(menge)     Menge, 
                       Sum(ek)        EKT, 
                       Sum(vk)        VKT 
                FROM   (SELECT AA.*, 
                               menge * Isnull(ekprice, 0) 
                               AS EK, 
                               ( Isnull(unitprice, 0) / producttotal ) * 
                               Isnull(otax, 0) 
                                       otaxamount, 
                               ( Isnull(unitprice, 0) / producttotal ) * Isnull( 
                               odiscount, 0) 
                                       discountamount, 
                               ( Isnull(unitprice, 0) / producttotal ) * 
                               Isnull(coupondiscount, 0) 
                                       coupondiscountamount, 
                               menge * ( Isnull(unitprice, 0) - ( 
                                         Isnull(unitprice, 0) / producttotal ) 
                                                                * 
                                                                Isnull(otax, 0) 
                                         - ( Isnull(unitprice, 0) / producttotal 
                                           ) * 
                                           Isnull(odiscount, 0) - 
                                         ( 
                                         Isnull( 
                                                 unitprice, 0) / producttotal ) 
                                         * 
                                         Isnull( 
                                         coupondiscount 
                                         , 0) 
                                       ) 
                               AS VK 
                        FROM   (SELECT i.catalogid, 
                                       c.contactid, 
                                       c.city, 
                                       c.country, 
                                       c.firstname, 
                                       c.lastname, 
                                       c.email, 
                                       c.lastlogindate, 
                                       i.supplierid, 
                                       p.cname, 
                                       i.ekprice, 
                                       i.unitprice, 
                                       o.odate, 
                                       o.otax, 
                                       o.odiscount, 
                                       o.oshipcost, 
                                       o.coupondiscount, 
                                       o.producttotal, 
                                       s.name, 
                                       Stuff((SELECT 
                                       ',' 
                                       + COALESCE(Ltrim(Rtrim(i.catalogid)), '') 
                                              FROM   orders o 
                                                     INNER JOIN oitems i 
                                                             ON o.orderid = 
                                                                i.orderid 
                                              WHERE  o.ocustomerid = c.contactid 
                                              FOR xml path('')), 1, 1, '') AS 
                                       ItemsBought, 
                                       CASE WHEN o.oshippeddate BETWEEN @Date1 
                                       AND 
                                       @Date2 THEN 
                                       Isnull(i.f2, 0) 
                                               ELSE 0 
                                       END + CASE WHEN o.oshippeddate2 BETWEEN 
                                       @Date1 
                                       AND 
                                       @Date2 THEN 
                                               Isnull(i.f3, 0) 
                                       ELSE 0 END + CASE WHEN o.oshippeddate3 
                                       BETWEEN 
                                       @Date1 
                                       AND @Date2 
                                       THEN 
                                       Isnull(i.f4, 0) ELSE 0 END          AS 
                                       Menge, 
                                       CASE 
                                         WHEN o.oshippeddate IS NOT NULL 
                                               OR o.oshippeddate2 IS NOT NULL 
                                               OR o.oshippeddate3 IS NOT NULL 
                                       THEN 
                                         odate 
                                         ELSE NULL 
                                       END                                 AS 
                                       orderdate 
                                FROM   orders o 
                                       INNER JOIN oitems i 
                                               ON i.orderid = o.orderid 
                                       LEFT OUTER JOIN products p 
                                                    ON i.catalogid = p.catalogid 
                                       LEFT OUTER JOIN suppliers s 
                                                    ON 
                                       i.supplierid = s.supplierid 
                                       RIGHT OUTER JOIN customers c 
                                                     ON 
                                       o.ocustomerid = c.contactid)AS 
                               AA) T 
                GROUP  BY T.contactid, 
                          T.email, 
                          T.itemsbought, 
                          T.country, 
                          T.lastlogindate, 
                          T.city) AS RR 
        GROUP  BY RR.ekt, 
                  RR.menge, 
                  RR.vkt, 
                  RR.country, 
                  RR.lastlogindate, 
                  RR.contactid, 
                  RR.itemsbought, 
                  RR.city, 
                  RR.email, 
                  RR.lastbuydate) XS 
       INNER JOIN customerstatistics cst 
               ON XS.contactid = cst.id 
ORDER  BY buycount

1 个答案:

答案 0 :(得分:1)

我承认:你并没有过于复杂化。您寻求的统计数据确实需要这么多级别的(子)查询。

当然,如果您将中间值存储在事务表或其他余额表中,查询将变得更加简单,但这意味着您将不得不维护冗余数据,这可能是令人讨厌的。您是否发现附加列和/或表格的可接受程度也是一种品味问题。

关于功能的一点评论。我对统计数据知之甚少,但对我来说,按数字范围而不是精确数量对结果进行排名似乎更合乎逻辑;最微小的差异可能已经导致两个几乎相等的销售额不计入排名。

从技术上讲,查询对我来说非常好。我注意到在最里面的查询(AA)中,您正在检索的数据比您当时真正需要的数据多。

  • 无需加入供应商; AFAICT,它在查询的其余部分中未使用。
  • 无需加入客户;它的数据只是“泡沫”到最高层。仅传递 ocustomerid ,然后在最高级别连接表 customers

在最里面的查询中加入供应商和客户只有一个好处:您可以在另一个上下文中重复使用完全相同的查询来检索销售订单详细信息。在较大的查询由较小的查询自动组成的环境中可能很有用。如果全部是手工劳动,那么您也可以删除供应商加入并将客户加入到外部。它会略微提高可读性和性能(尽管你已经指出这目前不是问题)。

至于你原来的问题(嵌套子查询与临时表),我认为它不会产生太大的影响。理论上,嵌套子查询是最好的;让查询优化器决定什么是最佳策略。

总而言之,我认为你做得很好。