可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have an SQL 2005 table, let's call it Orders, in the format:
OrderID, OrderDate, OrderAmount 1, 25/11/2008, 10 2, 25/11/2008, 2 3, 30/1002008, 5
Then I need to produce a report table showing the ordered amount on each day in the last 7 days:
Day, OrderCount, OrderAmount 25/11/2008, 2, 12 26/11/2008, 0, 0 27/11/2008, 0, 0 28/11/2008, 0, 0 29/11/2008, 0, 0 30/11/2008, 1, 5
The SQL query that would normally produce this:
select count(*), sum(OrderAmount) from Orders where OrderDate>getdate()-7 group by datepart(day,OrderDate)
Has a problem in that it will skip the days where there are no orders:
Day, OrderCount, OrderAmount 25/11/2008, 2, 12 30/11/2008, 1, 5
Normally I would fix this using a tally table and outer join against rows there, but I'm really looking for a simpler or more efficient solution for this. It seems like such a common requirement for a report query that some elegant solution should be available for this already.
So: 1. Can this result be obtain from a simple query without using tally tables?
and 2. If no, can we create this tally table (reliably) on the fly (I can create a tally table using CTE but recursion stack limits me to 100 rows)?
回答1:
SQL isn't "skipping" dates... because queries run against data that is actually in the table. So, if you don't have a DATE in the table for January 14th, then why would SQL show you a result :)
What you need to do is make a temp table, and JOIN to it.
CREATE TABLE #MyDates ( TargetDate DATETIME ) INSERT INTO #MyDates VALUES CONVERT(DATETIME, CONVERT(VARCHAR, GETDATE() - 0, 101)) INSERT INTO #MyDates VALUES CONVERT(DATETIME, CONVERT(VARCHAR, GETDATE() - 1, 101)) INSERT INTO #MyDates VALUES CONVERT(DATETIME, CONVERT(VARCHAR, GETDATE() - 2, 101)) INSERT INTO #MyDates VALUES CONVERT(DATETIME, CONVERT(VARCHAR, GETDATE() - 3, 101)) INSERT INTO #MyDates VALUES CONVERT(DATETIME, CONVERT(VARCHAR, GETDATE() - 4, 101)) INSERT INTO #MyDates VALUES CONVERT(DATETIME, CONVERT(VARCHAR, GETDATE() - 5, 101)) INSERT INTO #MyDates VALUES CONVERT(DATETIME, CONVERT(VARCHAR, GETDATE() - 6, 101)) INSERT INTO #MyDates VALUES CONVERT(DATETIME, CONVERT(VARCHAR, GETDATE() - 7, 101)) SELECT CONVERT(VARCHAR, TargetDate, 101) AS Date, COUNT(*) AS OrderCount FROM dbo.Orders INNER JOIN #MyDates ON Orders.Date = #MyDates.TargetDate GROUP BY blah blah blah (you know the rest)
There you go!
回答2:
I had the same problem and this is how I solved it:
SELECT datename(DW,nDays) TimelineDays, Convert(varchar(10), nDays, 101) TimelineDate, ISNULL(SUM(Counter),0) Totals FROM (Select GETDATE() AS nDays union Select GETDATE()-1 union Select GETDATE()-2 union Select GETDATE()-3 union Select GETDATE()-4 union Select GETDATE()-5 union Select GETDATE()-6) AS tDays Left Join (Select * From tHistory Where Account = 1000) AS History on (DATEPART(year,nDays) + DATEPART(MONTH,nDays) + DATEPART(day,nDays)) = (DATEPART(year,RecordDate) + DATEPART(MONTH,RecordDate) + DATEPART(day,RecordDate)) GROUP BY nDays ORDER BY nDays DESC
The ouput is:
TimelineDays, TimelineDate, Totals Tuesday 10/26/2010 0 Monday 10/25/2010 6 Sunday 10/24/2010 3 Saturday 10/23/2010 2 Friday 10/22/2010 0 Thursday 10/21/2010 0 Wednesday 10/20/2010 0
回答3:
If you want to see value zero than put the following query:
select count(*), sum(OrderAmount) from Orders where OrderDate>getdate()-7 and sum(OrderAmount) > 0 or sum(OrderAmount) = 0 group by datepart(day,OrderDate)
回答4:
Depending on how SQL Server handles temporary tables, you can more or less easily arrange to create a temporary table and populate it with the 7 (or was that 8?) dates you are interested in. You can then use that as your tally table. There isn't a cleaner way that I know of; you can only select data that exists in a table or that can be derived from data that exists in a table or set of tables. If there are dates not represented in the Orders table, you can't select those dates from the Orders table.
回答5:
Since you will want to use this date table frequently in other queries as well, I suggest you make it a permanent table and create a job to add the new year's dates once a year.
回答6:
CREATE PROCEDURE [dbo].[sp_Myforeach_Date] -- Add the parameters for the stored procedure here @SatrtDate as DateTime, @EndDate as dateTime, @DatePart as varchar(2), @OutPutFormat as int AS BEGIN -- SET NOCOUNT ON added to prevent extra result sets from -- interfering with SELECT statements. Declare @DateList Table (Date varchar(50)) WHILE @SatrtDate
Just put this Code and call the SP in This way
exec sp_Myforeach_Date @SatrtDate='03 Jan 2010',@EndDate='03 Mar 2010',@DatePart='dd',@OutPutFormat=106
Thanks *Suvabrata Roy ICRA Online Ltd. Kolkata*