Frage

Ich habe eine Monatliche Status-Datenbank anzeigen, ich muss einen Bericht erstellen, basierend auf.Die Daten in der view sieht wie folgt aus:

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


...Und so weiter

Die Ansicht hat eine Produkt-Kategorie, ein Umsatz, ein Jahr und einen Monat.Ich möchte einen Bericht erstellen, Vergleich der Jahre 2007 und 2008 zeigen 0 für die Monate ohne Umsatz.So der Bericht sollte in etwa so Aussehen:

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


Die wichtige Sache zu beachten ist, wie man 1 Monat nur hat 2008 den Umsatz, und daher ist 0 für das Jahr 2007.Auch, Monat 4 nur hat keine der Umsatz in 2008, daher die 0, während sich der Umsatz im Jahr 2007 und noch zeigen.

Auch der Bericht ist eigentlich für das Geschäftsjahr - also ich würde Liebe zu haben in leere Spalten mit 0 in beide, wenn es keine Verkäufe in sage Monat 5 für entweder 2007 oder 2008.

Die Abfrage, die ich habe sieht wie folgt aus:

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

Das problem bei dieser Abfrage ist, dass es nicht zurückkehren würde die vierte Zeile in meinem Beispiel oben angeführten Daten, da wir nicht jedes Verkäufe im Jahr 2008, aber wir haben tatsächlich in 2007.

Dies ist wahrscheinlich eine gemeinsame query/problem, aber mein SQL ist rostigen nach front-end-Entwicklung für so lange.Jede Hilfe wird sehr geschätzt!

Oh, btw, I ' m using SQL 2005 für diese Abfrage also, wenn es irgendwelche hilfreiche neue Funktionen, die mir dabei helfen können, lasst es mich wissen.

War es hilfreich?

Lösung

Die Case-Anweisung ist meine beste sql-Freund.Benötigen Sie auch eine Tabelle für die Zeit zu generieren 0 rev in beiden Monaten.

Annahmen basieren auf der Verfügbarkeit der folgende Tabellen:

Vertrieb:Kategorie | Umsatz | Yearh | Monat

und

tm:Jahr | Monat (gefüllt mit allen Termine erforderlich für die Berichterstattung)

Beispiel 1 ohne leere Zeilen:

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

RÜCKKEHR:

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

Beispiel 2 mit leeren Zeilen:Ich werde die Verwendung einer sub-query (aber andere auch nicht), und gibt eine leere Zeile für jedes Produkt und Jahr-Monat-combo.

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

RÜCKKEHR:

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

Beachten Sie, dass die meisten reporting-tools wird dies tun, Kreuztabelle oder matrix-Funktionalität, und jetzt, dass ich denke, dass es in SQL Server 2005 hat pivot-syntax, wird dies ebenfalls tun.

Hier sind einige zusätzliche Ressourcen.FALL http://www.4guysfromrolla.com/webtech/102704-1.shtml SQL SERVER 2005-PIVOT http://msdn.microsoft.com/en-us/library/ms177410.aspx

Andere Tipps

@Christian -- markdown editor -- UGH;vor allem, wenn die Vorschau und die Finale version von Ihrem post nicht einverstanden...@Christian -- full outer join -- der full outer join wird aufgehoben durch die Tatsache, dass es Verweise auf SP1 in der WHERE-Klausel, und die WHERE-Klausel angewendet wird, nachdem die VERKNÜPFUNG.Ein full outer join mit Filter auf einem der Tische, die Sie benötigen, um Ihre WHERE-Klausel in einer Unterabfrage, so dass die Filterung passiert bevor die beizutreten, oder versuchen, zu bauen, WO alle Ihre Kriterien auf die JOIN-Klausel, die ist wahnsinnig hässlich.Nun, es gibt eigentlich keine schönen Weg, dies zu tun eins.

@Jonas:Angesichts dieser:

Auch der Bericht ist eigentlich für das Geschäftsjahr - also Ich würde Liebe zu haben in leere Spalten mit 0 in beide, wenn es keine Verkäufe in sage Monat 5 für entweder 2007 oder 2008.

und die Tatsache, dass dieser job kann nicht getan werden mit einem ziemlich Abfrage, ich würde auf jeden Fall versuchen um die Ergebnisse zu bekommen, die Sie tatsächlich wollen.Kein Sinn, mit einem hässlichen Abfrage und auch nicht immer die genaue Daten Sie eigentlich wollen.;)

Also, ich würde vorschlagen, dies zu tun, in 5 Schritte:
1.erstellen einer temporären Tabelle in die format Sie wollen Ihre Ergebnisse übereinstimmen
2.füllen Sie es mit zwölf Reihen, mit 1-12 in der Spalte Monat
3.aktualisieren Sie die "in Diesem Jahr" Spalte, und verwenden Sie Ihre Logik SP1
4.aktualisieren Sie die "Letzten Jahr" Spalte, und verwenden Sie Ihre Logik SP2
5.wählen Sie aus der temporären Tabelle

Natürlich, ich denke, ich bin die Arbeit mit der Vermutung, dass Sie können eine gespeicherte Prozedur erstellen, um dies zu erreichen.Könnten Sie technisch in der Lage sein zu laufen, das ganze Stapel inline -, aber diese Art von Hässlichkeit ist sehr selten gesehen.Wenn Sie können nicht machen eine SP, schlage ich vor, Sie fallen zurück auf die full outer join über Unterabfrage, aber es gewann ' T erhalten Sie eine Zeile, wenn ein Monat keine Verkaufs-Jahr.

Über die markdown - Ja, das ist frustrierend.Der editor hat eine Vorschau meiner HTML-Tabelle an, aber nach der Buchung, es war gegangen - So musste entfernen Sie alle HTML-Formatierungen werden von der post...

@kcrumley ich denke, wir haben erreicht ähnliche Schlussfolgerungen.Diese Abfrage einfach wird ' s echt hässlich.Ich habe tatsächlich das Problem gelöst, vor dem Lesen Ihrer Antwort, mit einer ähnlichen (aber doch anders-Ansatz).Ich habe Zugriff zum erstellen von gespeicherten Prozeduren und Funktionen der reporting-Datenbank.Erstellt habe ich eine Tabellenwertfunktion der Annahme einer Produkt-Kategorie, und ein Geschäftsjahr als parameter.Basierend auf, dass die Funktion füllt eine Tabelle mit 12 Zeilen.Die Zeilen mit Daten gefüllt werden, die aus der Ansicht, wenn keine Verkäufe zur Verfügung, wenn nicht die Zeile wird 0-Werte.

Ich habe dann join der beiden Tabellen zurückgegeben, die von den Funktionen.Da ich weiß, dass alle Tische haben zwölf Stränen es zuzuteilen leichter, und ich kann join-on-Produkt, Kategorie und Monat:

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

Ich denke, Ihr Ansatz ist wahrscheinlich ein bisschen sauberer als die GetFinancialYear Funktion ist ziemlich chaotisch!Aber zumindest funktioniert - das macht mich glücklich für jetzt ;)

Der trick ist, um eine VOLLE BEITRETEN, mit ISNULL, um die verknüpften Spalten aus beiden Tabellen.Ich in der Regel wickeln Sie diese in einer Ansicht oder abgeleitete Tabelle, ansonsten müssen Sie die Verwendung von ISNULL in der WHERE-Klausel als gut.

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

Das sollte für Sie, Ihre Mindestanforderungen - Zeilen mit einem Umsatz in entweder 2007 oder 2008 oder beides.Um Zeilen ohne Umsatz im Jahr, Sie brauchen nur die INNERE VERKNÜPFUNG, eine 1-12 zahlen-Tabelle (Sie tun einer von denen, don ' T you?).

Ich könnte falsch sein, aber sollten nicht auch Sie mit einem full outer join anstelle von left join?So werden Sie immer 'leer' Spalten aus beiden Tabellen.

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

Verwenden von pivot und Dynamic Sql können wir dieses Ergebnis erreichen

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

Ergebnis

Category    Month   2008    2007
----------------------------------
Bikes       1       10000   0
Bikes       2       12000   11000
Bikes       3       12000   11500
Bikes       4       0       15400
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top