質問

月次ステータス データベース ビューに基づいてレポートを作成する必要があります。ビュー内のデータは次のようになります。

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 年に売上があったため、上記のデータ例の 4 行目が返されないことです。

これはおそらく一般的なクエリ/問題ですが、フロントエンド開発を長年行ってきたため、私の SQL は錆び付いています。ご協力をよろしくお願いいたします。

ところで、このクエリには SQL 2005 を使用しているので、役立つ新機能があればお知らせください。

役に立ちましたか?

解決

Case Statement は私の SQL の親友です。両方の月で 0 rev を生成する時間の表も必要です。

仮定は、次のテーブルが利用できるかどうかに基づいています。

販売:カテゴリ|収益|年|月

そして

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 を含む 12 行を入力します。
3.SP1 ロジックを使用して「今年」列を更新します
4.SP2 ロジックを使用して「昨年」列を更新します
5.一時テーブルから選択

もちろん、これを実現するストアド プロシージャを作成できるという前提で作業していると思います。技術的にはこのバッチ全体をインラインで実行できるかもしれませんが、そのような醜さはほとんど見られません。SP を作成できない場合は、サブクエリを介した完全外部結合に頼ることをお勧めしますが、どちらの年でも売上がなかった月には行を取得できません。

値下げについて - そうですね、それはイライラします。エディターは私の HTML テーブルをプレビューしましたが、投稿後は表示されなくなりました。そのため、投稿からすべての HTML 書式設定を削除する必要がありました...

@kcrumley 同様の結論に達したと思います。このクエリは非常に醜くなりやすいです。実際、あなたの回答を読む前に、同様の(ただし異なるアプローチ)を使用してこれを解決しました。レポート データベース上にストアド プロシージャと関数を作成するためのアクセス権があります。製品カテゴリと会計年度をパラメータとして受け入れるテーブル値関数を作成しました。これに基づいて、関数は 12 行を含むテーブルにデータを入力します。利用可能な販売がある場合、行にはビューからのデータが入力されます。販売できない場合、行の値は 0 になります。

次に、関数によって返された 2 つのテーブルを結合します。すべてのテーブルに 12 個のローブがあることがわかっているので、割り当てがより簡単になり、製品カテゴリと月に参加できます。

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

GetFinancial Year 関数は非常に面倒なので、あなたのアプローチはおそらくもう少しきれいだと思います。しかし、少なくともそれは機能します - 今のところ私は満足しています ;)

秘訣は、ISNULL を使用して FULL JOIN を実行し、どちらかのテーブルから結合された列を取得することです。通常、これをビューまたは派生テーブルにラップします。それ以外の場合は、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 年のいずれか、あるいはその両方の売上を含む行が得られるはずです。どちらの年にも売上がない行を取得するには、1 ~ 12 の数値テーブルに INNER JOIN するだけです ( そのうちの1つを持っています, 、 そうでしょう?)。

私が間違っている可能性がありますが、左結合だけではなく完全な外部結合を使用するべきではないでしょうか?こうすることで、両方のテーブルから「空」の列を取得できます。

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

ピボットと動的 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