【问题标题】:How to use WHERE clause with a PIVOT如何将 WHERE 子句与 PIVOT 一起使用
【发布时间】:2014-03-20 00:10:56
【问题描述】:

为什么,我这里的 where 子句会被忽略,不管作为参数输入的值如何,它都会对发票进行求和,在这上面花了几个小时然后发疯了!

ALTER PROCEDURE [dbo].[Usp_custom_dash_metric_invoicespend] 
  @CompanyGUID  UNIQUEIDENTIFIER, 
  @UserGUID UNIQUEIDENTIFIER, 
  @CompanyUserGUID UNIQUEIDENTIFIER, 
  @InvoiceYearFrom CHAR(4), 
  @InvoiceYearTo CHAR(4), 
  @CompanySupplierGUID UNIQUEIDENTIFIER 
AS 
BEGIN 
 WITH _d 
   AS (SELECT a.totalgrossvaluehome1 AS tgvh1, 
              invoicedate            AS ind, 
              c.guid                 AS cug, 
              a.companysupplierguid  AS cus, 
              c.userguid             AS ug, 
              b.guid                 AS cg 
       FROM   invoices a (nolock) 
              JOIN companies b (nolock) 
                ON ( a.companyguid = b.guid ) 
              JOIN companyusers c (nolock) 
                ON ( c.userguid = a.createdbyguid ) 
       WHERE  a.invoicedate BETWEEN Cast(Isnull(NULL, Cast(Datepart(year 
                                         , 
                                         Dateadd( 
                                         year, -1, 
                                                 Getdate()))AS NVARCHAR( 
                                         max 
                                         ))) 
                                         + '-01-01' AS DATETIME) AND 
                                    Cast( 
              Isnull(NULL, Cast(Datepart(year, 
              Getdate() 
              )AS 
              NVARCHAR(max))) 
              + '-12-31' AS DATETIME) 
              AND c.guid = @CompanyUserGUID 
               OR @CompanyUserGUID IS NULL 
                  AND a.companysupplierguid = @CompanySupplierGUID 
               OR @CompanySupplierGUID IS NULL 
                  AND c.userguid = @UserGUID 
               OR @UserGUID IS NULL 
                  AND a.companyguid = @CompanyGUID 
               OR @CompanyGUID IS NULL) 
SELECT * 
FROM   (SELECT Year(ind)                    AS [ayear], 
             LEFT(Datename(month, ind), 3)AS [amonth], 
             Isnull(tgvh1, 0)             AS Amount 
      FROM   _d 
      WHERE  cug = @CompanyUserGUID 
              OR @CompanyUserGUID IS NULL 
                 AND cus = @CompanySupplierGUID 
              OR @CompanySupplierGUID IS NULL 
                 AND ug = @UserGUID 
              OR @UserGUID IS NULL 
                 AND cg = @CompanyGUID 
              OR @CompanyGUID IS NULL)s 
     PIVOT ( Sum(amount) 
           FOR [amonth] IN (jan, 
                            feb, 
                            mar, 
                            apr, 
                            may, 
                            jun, 
                            jul, 
                            aug, 
                            sep, 
                            oct, 
                            nov, 
                            dec) )AS pivotal 
WHERE  pivotal.ayear >= Isnull(@InvoiceYearFrom, Datepart(year, 
                                               Dateadd(year, -1, 
                                                      Getdate() 
                                               ))) 
     AND pivotal.ayear <= Isnull(@InvoiceYearTo, Datepart(year, Getdate( 
                                                 )) 
                          ) 
ORDER  BY [ayear] 
END   

具体来说就是这个:

WHERE  a.invoicedate BETWEEN Cast(Isnull(@InvoiceDateFrom, Cast(Datepart(year 
                                         , 
                                         Dateadd( 
                                         year, -1, 
                                                 Getdate()))AS NVARCHAR( 
                                         max 
                                         ))) 
                                         + '-01-01' AS DATETIME) AND 
                                    Cast( 
              Isnull(@InvoiceDateTo, Cast(Datepart(year, 
              Getdate() 
              )AS 
              NVARCHAR(max))) 
              + '-12-31' AS DATETIME) 
              AND c.guid = @CompanyUserGUID 
               OR @CompanyUserGUID IS NULL 
                  AND a.companysupplierguid = @CompanySupplierGUID 
               OR @CompanySupplierGUID IS NULL 
                  AND c.userguid = @UserGUID 
               OR @UserGUID IS NULL 
                  AND a.companyguid = @CompanyGUID 
               OR @CompanyGUID IS NULL) 

例如,pivot 中返回的数据是所有数据的总和(我假设因为它从 CTX 中获取数据,它将包含过滤后的数据,所以我希望如果我传入 @Userguid =(一个真实的用户 id ) 它会将旋转计数限制为仅针对用户的发票的计数...它不会:'(

【问题讨论】:

  • 如果您在没有枢轴部分的情况下运行查询,您是否使用 WHERE 子句返回正确的?
  • 是的,从 CTX 返回的数据过滤正确。

标签: sql sql-server tsql sql-server-2008-r2 pivot


【解决方案1】:

查看您的WHERE 子句,我看到OR 用于未分组的语句。我认为您想用以下内容替换 WHERE 子句的最后一部分(请注意,之后您需要一个额外的结束括号来结束您的 CTE):

AND (c.guid = @CompanyUserGUID    -- Added opening paren (required)
 OR (@CompanyUserGUID IS NULL     -- Added parens around group (optional)
    AND a.companysupplierguid = @CompanySupplierGUID)
 OR (@CompanySupplierGUID IS NULL -- Added parens around group (optional)
    AND c.userguid = @UserGUID)
 OR (@UserGUID IS NULL            -- Added parens around group (optional)
    AND a.companyguid = @CompanyGUID)
 OR @CompanyGUID IS NULL)         -- Added closing paren

更新

更仔细地查看WHERE 子句的内容,我认为您可能想要添加一堆额外的分组。据我所知,我认为您的意图是仅在 所有以前的 GUID 参数为 NULL 时检查 GUID 参数。您当前的WHERE 子句检查前一个(但不一定是之前的那个)为NULL 的所有GUID 参数。这些分组应该可以解决这个问题。

AND (c.guid = @CompanyUserGUID
    OR (   @CompanyUserGUID IS NULL
       AND (a.companysupplierguid = @CompanySupplierGUID
           OR (   @CompanySupplierGUID IS NULL
              AND (c.userguid = @UserGUID
                  OR (   @UserGUID IS NULL
                     AND (  a.companyguid = @CompanyGUID
                         OR @CompanyGUID IS NULL))))))))

结束更新

据我了解 SQL-Server 中的操作顺序,单个 @param IS NULL OR column = @param 子句周围的括号是不必要的(除了使您的意图更清晰)。 另一方面,围绕整个分组的左括号和右括号是必要的。没有它,您的 SQL 将检查 (a.invoicedate BETWEEN ... AND c.guid = @CompanyUserGUID) OR [GUID clauses...]

此外,我强烈建议清理获取时间范围的 SQL。您可以使用以下方法大大简化它们(并使您的代码更易于阅读和诊断):

-- "Start of last year":
--     Get the number of years from 0 (1900-01-01) to this year from 0
--     Then add add that many years (minus 1 for last year) back to 0
DATEADD(YEAR, DATEDIFF(YEAR, 0, GETDATE())-1, 0)
-- "End of this year": Get "the start of next year" and subtract one day
DATEADD(DAY, -1, DATEADD(YEAR, DATEDIFF(YEAR, 0, GETDATE())+1, 0))

请注意,除非您的 invoicedate 列截断时间,否则您当前的逻辑实际上会排除 2014 年 12 月 31 日的记录,因为您的过滤器中的时间戳为 00:00:00。如果是这样的话,我建议明年年初去。

【讨论】:

  • 这是故意的,我希望它通过并在参数化之前暂时默认为空值。
  • @JoshuaHolden 我删除了关于ISNULL 用法的声明,并用我认为导致您的问题的原因更新了我的答案。
  • --> 谢谢这是我的父母!
【解决方案2】:

我认为你应该在这里添加适当的大括号,分组 OR & AND 条件很重要

AND c.guid = @CompanyUserGUID 
OR @CompanyUserGUID IS NULL 
 AND a.companysupplierguid = @CompanySupplierGUID 
  OR @CompanySupplierGUID IS NULL 
              AND c.userguid = @UserGUID 
           OR @UserGUID IS NULL 
              AND a.companyguid = @CompanyGUID 
           OR @CompanyGUID IS NULL

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-10-08
    • 2016-05-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-20
    • 1970-01-01
    相关资源
    最近更新 更多