我有一个每月状态数据库视图,我需要根据该视图构建报告。视图中的数据看起来像这样:

Category | Revenue  |  Yearh  |  Month
Bikes      10 000      2008        1
Bikes      12 000      2008        2
Bikes      12 000      2008        3
Bikes      15 000      2008        1
Bikes      11 000      2007        2
Bikes      11 500      2007        3
Bikes      15 400      2007        4


...等等

该视图包含产品类别、收入、年份和月份。我想创建一份比较 2007 年和 2008 年的报告,显示没有销售的月份为 0。所以报告应该是这样的:

Category  |  Month  |  Rev. This Year  |  Rev. Last Year
Bikes          1          10 000               0
Bikes          2          12 000               11 000
Bikes          3          12 000               11 500
Bikes          4          0                    15 400


需要注意的关键是第 1 个月仅 2008 年有销售额,因此 2007 年为 0。此外,第 4 个月仅在 2008 年没有销售,因此为 0,而在 2007 年有销售并且仍然显示。

另外,该报告实际上是针对财政年度的 - 因此,如果 2007 年或 2008 年的第 5 个月没有销售,我希望两列都为 0。

我得到的查询看起来像这样:

SELECT 
    SP1.Program,
    SP1.Year,
    SP1.Month,
    SP1.TotalRevenue,
    IsNull(SP2.TotalRevenue, 0) AS LastYearTotalRevenue

FROM PVMonthlyStatusReport AS SP1 
     LEFT OUTER JOIN PVMonthlyStatusReport AS SP2 ON 
                SP1.Program = SP2.Program AND 
                SP2.Year = SP1.Year - 1 AND 
                SP1.Month = SP2.Month
WHERE 
    SP1.Program = 'Bikes' AND
    SP1.Category = @Category AND 
    (SP1.Year >= @FinancialYear AND SP1.Year <= @FinancialYear + 1) AND
    ((SP1.Year = @FinancialYear AND SP1.Month > 6) OR 
     (SP1.Year = @FinancialYear + 1 AND SP1.Month <= 6))

ORDER BY SP1.Year, SP1.Month

此查询的问题在于,它不会返回上面示例数据中的第四行,因为我们在 2008 年没有任何销售,但我们实际上在 2007 年有销售。

这可能是一个常见的查询/问题,但是我的SQL做了这么长时间的前端开发已经生锈了。任何帮助是极大的赞赏!

哦,顺便说一句,我正在使用 SQL 2005 进行此查询,因此如果有任何有用的新功能可能对我有帮助,请告诉我。

有帮助吗?

解决方案

Case 语句是我最好的 sql 朋友。您还需要一个表格,列出在这两个月中产生 0 转速的时间。

假设基于下表的可用性:

销售量:类别|收入|年|月

Tm值:年月(填充所有报告所需的日期)

没有空行的示例 1:

select
    Category
    ,month
    ,SUM(CASE WHEN YEAR = 2008 THEN Revenue ELSE 0 END) this_year
    ,SUM(CASE WHEN YEAR = 2007 THEN Revenue ELSE 0 END) last_year

from
    sales

where
    year in (2008,2007)

group by
    Category
    ,month

返回:

Category  |  Month  |  Rev. This Year  |  Rev. Last Year
Bikes          1          10 000               0
Bikes          2          12 000               11 000
Bikes          3          12 000               11 500
Bikes          4          0                    15 400

具有空行的示例 2:我将使用子查询(但其他查询可能不会),并将为每个产品和年月组合返回一个空行。

select
    fill.Category
    ,fill.month
    ,SUM(CASE WHEN YEAR = 2008 THEN Revenue ELSE 0 END) this_year
    ,SUM(CASE WHEN YEAR = 2007 THEN Revenue ELSE 0 END) last_year

from
    sales
    Right join (select distinct  --try out left, right and cross joins to test results.
                   product
                   ,year
                   ,month
               from
                  sales --this ideally would be from a products table
                  cross join tm
               where
                    year in (2008,2007)) fill


where
    fill.year in (2008,2007)

group by
    fill.Category
    ,fill.month

返回:

Category  |  Month  |  Rev. This Year  |  Rev. Last Year
Bikes          1          10 000               0
Bikes          2          12 000               11 000
Bikes          3          12 000               11 500
Bikes          4          0                    15 400
Bikes          5          0                    0
Bikes          6          0                    0
Bikes          7          0                    0
Bikes          8          0                    0

请注意,大多数报告工具都会执行此交叉表或矩阵功能,现在我认为 SQL Server 2005 具有枢轴语法,也可以执行此操作。

这里有一些额外的资源。案件http://www.4guysfromrolla.com/webtech/102704-1.shtmlSQL Server 2005 数据透视http://msdn.microsoft.com/en-us/library/ms177410.aspx

其他提示

@Christian——降价编辑器——呃;特别是当你的帖子的预览和最终版本不一致时......@Christian -- 完全外连接 -- 完全外连接被 WHERE 子句中引用 SP1 的事实所否决,并且 WHERE 子句在 JOIN 之后应用。要对其中一个表进行完全外连接过滤,您需要将 WHERE 子句放入子查询中,以便进行过滤 连接,或者尝试将所有 WHERE 标准构建到 JOIN ON 子句上,这非常难看。嗯,实际上没有什么好方法可以做到这一点。

@乔纳斯:考虑到这一点:

此外,该报告实际上是针对财政年度的 - 所以 如果 2007 年或 2008 年第 5 个月没有销售,我希望两列都为 0。

事实上,这项工作无法通过漂亮的查询来完成,我肯定会尝试获得您真正想要的结果。进行丑陋的查询甚至无法获得您真正想要的确切数据是没有意义的。;)

因此,我建议分 5 个步骤进行:
1.以您希望结果匹配的格式创建临时表
2.用十二行填充它,月份列中包含 1-12
3.使用 SP1 逻辑更新“今年”列
4.使用 SP2 逻辑更新“去年”列
5.从临时表中选择

当然,我想我是假设您可以创建一个存储过程来完成此任务。从技术上讲,您可能能够内联运行整个批次,但这种丑陋的情况很少见。如果您无法创建 SP,我建议您通过子查询依靠完整的外部联接,但是当某一个月没有销售时,它不会为您带来一行。

关于降价 - 是的,这令人沮丧。编辑器确实预览了我的 HTML 表格,但发布后它就消失了 - 因此必须从帖子中删除所有 HTML 格式...

@kcrumley 我认为我们已经得出了类似的结论。这个查询很容易变得非常难看。实际上,在阅读您的答案之前,我使用类似(但不同的方法)解决了这个问题。我有权在报告数据库上创建存储过程和函数。我创建了一个表值函数,接受产品类别和财政年度作为参数。在此基础上,该函数将填充一个包含 12 行的表。如果有可用的销售,则行将使用视图中的数据填充,否则该行将具有 0 值。

然后,我连接函数返回的两个表。因为我知道所有桌子都有十二个轮子,所以分配更容易,我可以加入产品类别和月份:

SELECT 
    SP1.Program,
    SP1.Year,
    SP1.Month,
    SP1.TotalRevenue AS ThisYearRevenue,
    SP2.TotalRevenue AS LastYearRevenue
FROM GetFinancialYear(@Category, 'First Look',  2008) AS SP1 
     RIGHT JOIN GetFinancialYear(@Category, 'First Look',  2007) AS SP2 ON 
         SP1.Program = SP2.Program AND 
         SP1.Month = SP2.Month

我认为你的方法可能更干净一些,因为 GetFinancialYear 函数非常混乱!但至少它有效 - 这让我现在很高兴;)

诀窍是执行 FULL JOIN,使用 ISNULL 从任一表中获取连接列。我通常将其包装到视图或派生表中,否则您还需要在 WHERE 子句中使用 ISNULL。

SELECT 
    Program,
    Month,
    ThisYearTotalRevenue,
    PriorYearTotalRevenue
FROM (
    SELECT 
        ISNULL(ThisYear.Program, PriorYear.Program) as Program,
        ISNULL(ThisYear.Month, PriorYear.Month),
        ISNULL(ThisYear.TotalRevenue, 0) as ThisYearTotalRevenue,
        ISNULL(PriorYear.TotalRevenue, 0) as PriorYearTotalRevenue
    FROM (
        SELECT Program, Month, SUM(TotalRevenue) as TotalRevenue 
        FROM PVMonthlyStatusReport 
        WHERE Year = @FinancialYear 
        GROUP BY Program, Month
    ) as ThisYear 
    FULL OUTER JOIN (
        SELECT Program, Month, SUM(TotalRevenue) as TotalRevenue 
        FROM PVMonthlyStatusReport 
        WHERE Year = (@FinancialYear - 1) 
        GROUP BY Program, Month
    ) as PriorYear ON
        ThisYear.Program = PriorYear.Program
        AND ThisYear.Month = PriorYear.Month
) as Revenue
WHERE 
    Program = 'Bikes'
ORDER BY 
    Month

这应该可以满足您的最低要求 - 2007 年或 2008 年或两者都有销售的行。要获取任一年份都没有销售额的行,您只需 INNER JOIN 到 1-12 数字表(您可以 有其中之一, ,不是吗?)。

我可能是错的,但您不应该使用完整的外连接而不是左连接吗?这样您将从两个表中获得“空”列。

http://en.wikipedia.org/wiki/Join_(SQL)#Full_outer_join

使用pivot和Dynamic Sql我们可以实现这个结果

SET NOCOUNT ON
IF OBJECT_ID('TEMPDB..#TEMP') IS NOT NULL
DROP TABLE #TEMP

;With cte(Category , Revenue  ,  Yearh  ,  [Month])
AS
(
SELECT 'Bikes', 10000, 2008,1 UNION ALL
SELECT 'Bikes', 12000, 2008,2 UNION ALL
SELECT 'Bikes', 12000, 2008,3 UNION ALL
SELECT 'Bikes', 15000, 2008,1 UNION ALL
SELECT 'Bikes', 11000, 2007,2 UNION ALL
SELECT 'Bikes', 11500, 2007,3 UNION ALL
SELECT 'Bikes', 15400, 2007,4
)
SELECT * INTO #Temp FROM cte

Declare @Column nvarchar(max),
        @Column2 nvarchar(max),
        @Sql nvarchar(max)


SELECT @Column=STUFF((SELECT DISTINCT ','+ 'ISNULL('+QUOTENAME(CAST(Yearh AS VArchar(10)))+','+'''0'''+')'+ 'AS '+ QUOTENAME(CAST(Yearh AS VArchar(10)))
FROM #Temp order by 1 desc FOR XML PATH ('')),1,1,'')

SELECT @Column2=STUFF((SELECT DISTINCT ','+ QUOTENAME(CAST(Yearh AS VArchar(10)))
FROM #Temp FOR XML PATH ('')),1,1,'')

SET @Sql= N'SELECT Category,[Month],'+ @Column +'FRom #Temp
            PIVOT
            (MIN(Revenue) FOR yearh IN ('+@Column2+')
            ) AS Pvt

            '
EXEC(@Sql)
Print @Sql

结果

Category    Month   2008    2007
----------------------------------
Bikes       1       10000   0
Bikes       2       12000   11000
Bikes       3       12000   11500
Bikes       4       0       15400
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top