2009-08-13 14 views
7

Ich habe eine Tabelle mit Bestellinformationen in einem E-Commerce-Shop. Schema sieht wie folgt aus:Wählen Sie Daten aus SQL DB pro Tag

[Orders]
Id | Teilsumme | TaxAmount | ShippingAmount | Datecreated

Diese Tabelle enthält nur Daten für jeden Auftrag. Wenn also ein Tag ohne Bestellungen vergeht, sind für diesen Tag keine Verkaufsdaten vorhanden.

Ich möchte die Zwischensumme pro Tag für die letzten 30 Tage auswählen, einschließlich jener Tage ohne Verkauf.

Die resultset würde wie folgt aussehen:

Datum | SalesSum
2009-08-01 | 15235
2009-08-02 | 0
2009-08-03 | 340
2009-08-04 | 0
...

dies tun, gibt mir nur die Daten für jene Tage mit Aufträgen:

select DateCreated as Date, sum(ordersubtotal) as SalesSum 
from Orders 
group by DateCreated 

Sie eine Tabelle Termine genannt schaffen könnte, und von dieser Tabelle auswählen und die Orders-Tabelle verbinden. Aber ich möchte das wirklich vermeiden, weil es nicht gut genug funktioniert, wenn es um verschiedene Zeitzonen und Dinge geht ...

Bitte nicht lachen. SQL ist nicht meine Art von Sache ... :)

+0

Was verwenden Sie? Die Antworten können variieren, wenn Sie MySQL oder SQL Server oder etwas anderes verwenden ... – AnonJr

+0

Ich benutze MS SQL Server – MartinHN

Antwort

2

Erstellen Sie eine Funktion, die ein Datum Tabelle erzeugen können wie folgt:
(bestohlen http://www.codeproject.com/KB/database/GenerateDateTable.aspx)

Create Function dbo.fnDateTable 
(
    @StartDate datetime, 
    @EndDate datetime, 
    @DayPart char(5) -- support 'day','month','year','hour', default 'day' 
) 
Returns @Result Table 
(
    [Date] datetime 
) 
As 
Begin 
    Declare @CurrentDate datetime 
    Set @[email protected] 
    While @CurrentDate<[email protected] 
    Begin 
    Insert Into @Result Values (@CurrentDate) 
    Select @CurrentDate= 
    Case 
    When @DayPart='year' Then DateAdd(yy,1,@CurrentDate) 
    When @DayPart='month' Then DateAdd(mm,1,@CurrentDate) 
    When @DayPart='hour' Then DateAdd(hh,1,@CurrentDate) 
    Else 
     DateAdd(dd,1,@CurrentDate) 
    End 
    End 
    Return 
End 

Dann treten Sie gegen diese Tabelle

SELECT dates.Date as Date, sum(SubTotal+TaxAmount+ShippingAmount) 
FROM [fnDateTable] (dateadd("m",-1,CONVERT(VARCHAR(10),GETDATE(),111)),CONVERT(VARCHAR(10),GETDATE(),111),'day') dates 
LEFT JOIN Orders 
ON dates.Date = DateCreated 
GROUP BY dates.Date 
+0

Mit einer leicht modifizierten Select-Anweisung funktioniert es: SELECT dates.date, ISNULL (SUM (ordersubtotal), 0) als Sales FROM dbo]. [DateTable] ('2009-08-01', '2009-08-31', 'Tag') DatenLEFT JOIN Orders ON CONVERT (VARCHAR (10), Orders.datecreated, 111) = Datum.Datumsgruppe nach Datum .date – MartinHN

+0

Ich habe die zweite Abfrage aktualisiert. Ich hatte das falsche Datumsfeld in der Auswahl/Gruppe nach, so dass es nur auf Termine mit Verkäufen beschränkt war. Ich habe diese Version getestet, und ich denke, es gibt, was Sie wollen. Es hat NULL anstelle von 0s für Tage ohne Verkäufe, aber Sie könnten einfach die COALESCE-Funktion verwenden, um diese auszulöschen. – JamesMLV

+0

Ich kann das nicht für Käse arbeiten. Kann mir jemand die genaue richtige Antwort zeigen? – philbird

2
declare @oldest_date datetime 
declare @daily_sum numeric(18,2) 
declare @temp table(
    sales_date datetime, 
    sales_sum numeric(18,2) 
) 
select @oldest_date = dateadd(day,-30,getdate()) 

while @oldest_date <= getdate() 
begin 
    set @daily_sum = (select sum(SubTotal) from SalesTable where DateCreated = @oldest_date) 
    insert into @temp(sales_date, sales_sum) values(@oldest_date, @daily_sum) 
    set @oldest_date = dateadd(day,1,@oldest_date) 
end 

select * from @temp 

OK - Ich habe diesen "letzten 30 Tage" Teil verpasst. Das Bit darüber, obwohl nicht so sauber, IMHO, als die Datum Tabelle, sollte funktionieren. Eine andere Variante wäre, die while-Schleife zu verwenden, um eine temporäre Tabelle nur mit den letzten 30 Tagen zu füllen, und einen linken äußeren Join mit dem Ergebnis meiner ursprünglichen Abfrage durchzuführen.

+0

Fügen Sie einfach eine Where-Klausel für die letzten 30 Tage hinzu – jdelator

+4

Dies wird keine Tage enthalten, die nicht ' t haben irgendwelche Verkäufe in ihnen (wie ursprünglich angefordert) – scwagner

+0

Wenn war einfach so einfach :) Ich brauche jeden Tag, für die letzten 30 Tage. – MartinHN

-1
SELECT DateCreated, 
SUM(SubTotal) AS SalesSum 
FROM Orders 
GROUP BY DateCreated 
1

jenen Tagen ohne Verkäufe inklusive.

Das ist der schwierige Teil. Ich denke nicht, dass die erste Antwort Ihnen dabei helfen wird. Ich habe etwas ähnliches mit einer separaten Tabelle gemacht.

Sie können die Richtungen finden, wie dies hier zu tun:

Date Table

+0

Eigentlich ging ich davon aus, dass Tage ohne Umsatz nicht in der Tabelle wären. Wenn nicht, ignoriere meine Antwort. – DavidStein

+0

Es ist nicht. Die Tabelle enthält nur Daten pro Bestellung. Wenn also ein Tag ohne Bestellungen vergeht, gibt es keine Verkaufsdaten für diesen bestimmten Tag. – MartinHN

+1

Dann würde ich eine Datumstabelle mit allen Daten erstellen und dann eine linke Verknüpfung zu einer Abfrage, die die Verkäufe pro Tag zusammenfassen würde. Sie würden auch daran denken, eine coalesce-Anweisung zu verwenden, um für Tage ohne Verkäufe null statt null zurückzugeben. Ich musste dies in vielen verschiedenen Situationen tun und die externe Tabelle war der richtige Weg. – DavidStein

0

Ich tat das heute tatsächlich. Wir haben auch eine E-Commerce-Anwendung. Ich möchte unsere Datenbank nicht mit "nutzlosen" Daten füllen. Ich mache einfach die Gruppe nach und erstelle alle Tage für die letzten N Tage in Java und schaue sie mit den Datums-/Verkaufsergebnissen aus der Datenbank an.

0

Wo wird das letztendlich enden? Ich frage nur, weil es einfacher sein kann, die leeren Tage mit dem Programm zu füllen, das mit den Daten zu tun hat, anstatt es in SQL zu erledigen.

SQL ist eine wunderbare Sprache, und es ist in der Lage zu sehr vielen Dingen, aber manchmal ist es besser, stattdessen die Feinheiten der Daten im Programm zu bearbeiten.

1

Ich habe eine Protokolltabelle mit LogID einen Index, den ich nie irgendwelche Datensätze löschen. es hat einen Index von 1 bis ~ 10000000. Mit Hilfe dieser Tabelle I

select 
    s.ddate, SUM(isnull(o.SubTotal,0)) 
from 
    (
     select 
      cast(datediff(d,LogID,getdate()) as datetime) AS ddate 
     from 
      Log 
     where 
      LogID <31 
    ) s right join orders o on o.orderdate = s.ddate 
group by s.ddate 
0

(Revised ein bisschen - schlug ich zu früh eingeben) schreiben kann

fing ich an diese stoßen, und wie es einige ziemlich knifflige SQL Konzepte trifft es schnell in die wuchs folgende Monster. Wenn es machbar ist, könnte es besser sein, die Lösung von THen anzupassen; oder, wie viele andere empfehlen, könnte die Verwendung von Anwendungscode zum Ausfüllen der Lücken vorzuziehen sein.

-- A temp table holding the 30 dates that you want to check 
DECLARE @Foo Table (Date smalldatetime not null) 

-- Populate the table using a common "tally table" methodology (I got this from SQL Server magazine long ago) 
;WITH 
    L0 AS (SELECT 1 AS C UNION ALL SELECT 1), --2 rows 
    L1 AS (SELECT 1 AS C FROM L0 AS A, L0 AS B),--4 rows 
    L2 AS (SELECT 1 AS C FROM L1 AS A, L1 AS B),--16 rows 
    L3 AS (SELECT 1 AS C FROM L2 AS A, L2 AS B),--256 rows 
    Tally AS (SELECT ROW_NUMBER() OVER(ORDER BY C) AS Number FROM L3) 
INSERT @Foo (Date) 
select dateadd(dd, datediff(dd, 0, dateadd(dd, -number + 1, getdate())), 0) 
from Tally 
where Number < 31 

Schritt 1 besteht darin, eine temporäre Tabelle mit den 30 Daten zu erstellen, die Sie betreffen. Diese abstrakte Gewissheit ist der schnellste Weg, um eine Tabelle aufeinanderfolgender Ganzzahlen zu erstellen. Fügen Sie weitere Unterabfragen hinzu, und Sie können Millionen oder mehr in wenigen Sekunden auffüllen. Ich nehme die ersten 30 und verwende dateadd und das aktuelle Datum/die aktuelle Uhrzeit, um sie in Daten zu konvertieren. Wenn Sie bereits eine "feste" Tabelle haben, die 1-30 hat, können Sie diese verwenden und den CTE vollständig überspringen (indem Sie die Tabelle "Tally" durch Ihre Tabelle ersetzen).

Die äußeren zwei Datumsfunktionsaufrufe entfernen den Zeitabschnitt der generierten Zeichenfolge.

(Beachten Sie, dass ich davon ausgehen, dass Ihr Bestelldatum auch keine Zeit Teil hat - sonst haben Sie ein anderes häufiges Problem bekommen zu lösen.)

Zu Testzwecken I Tabelle #orders gebaut, und das bekommt man den Rest:

SELECT f.Date, sum(ordersubtotal) as SalesSum 
from @Foo f 
    left outer join #Orders o 
    on o.DateCreated = f.Date 
group by f.Date 
+0

Dies basiert auf SQL Server 2005, wobei allgemeine Tabellenausdrücke (diese hässliche "WITH" -Klausel) eingeführt wurden. Es gibt andere Möglichkeiten, diese Ergebnisse zu erhalten, aber das CTE ist am schnellsten für Ad-hoc-Tally-Tabellen-Builds. –

0

Ich erstellte die Funktion DateTable als JamesMLV auf mich hingewiesen.

Und dann sieht das SQL wie folgt aus:

SELECT dates.date, ISNULL(SUM(ordersubtotal), 0) as Sales FROM [dbo].[DateTable] ('2009-08-01','2009-08-31','day') dates 
LEFT JOIN Orders ON CONVERT(VARCHAR(10),Orders.datecreated, 111) = dates.date 
group by dates.date 
Verwandte Themen