views:

95

answers:

3

Hello to all!

I have a SQL statement in which I do this

... group by date having date between '2010-07-01' and '2010-07-10';

The result looks like:

sum(test)        day
--------------------
20        2010-07-03
120       2010-07-07
33        2010-07-09
42        2010-07-10

So I have these results, but is it possible, that I can write a statement that returns me for every day in the "between" condition a result row in this kind:

sum(test)        day
--------------------
0         2010-07-01
0         2010-07-02
20        2010-07-03
0         2010-07-04
0         2010-07-05
0         2010-07-06
120       2010-07-07
...       ...
42        2010-07-10

Otherwise, if this is not possible, I have to do it in my program logic.

Thanks a lot in advance & Best Regards.

Update: Perhaps it will be better if I will show you the full SQL statement:

select COALESCE(sum(DUR), 0) AS "r", 0 AS "opt", DATE_FORMAT(date, '%d.%m.%Y') AS "day" from (
    select a.id as ID, a.dur as DUR, DATE(FROM_UNIXTIME(REVTSTMP / 1000)) as date, 
        a_au.re as RE, a_au.stat as STAT from b_c
        join c on b_c.c_id = c.id
        join a on c.id = a.c_id
        join a_au on a.id = a_au.id
        join revi on a_au.re = revi.re
        join (
            select a.id as ID, DATE(FROM_UNIXTIME(REVTSTMP / 1000)) as date, 
            max(a_au.re) as MAX_RE from b_c
                join c on b_c.c_id = c.id
                join a on c.id = a.c_id
                join a_au on a.id = a_au.id
                join revi on a_au.re = revi.re
                where b_c.b_id = 30 group by ID, date) x on
                x.id = a.id and x.date = date and x.MAX_RE = a_au.rev
                where a_au.stat != 7
            group by ID, x.date)
         AS SubSelTable where date between '2010-07-01' and '2010-07-15' group by date;

Update: My new SQL statement (-> Dave Rix):

select coalesce(`theData`.`real`, 0) as 'real', 0 as 'opt', DATE_FORMAT(`DT`.`ddDate`, '%d.%m.%Y') as 'date'
    from `dimdates` as DT 
    left join (
        select coalesce(sum(DUR), 0) AS 'real', 0 AS 'opt', date 
            from (
                select a.id as ID, a.dur as DUR, DATE(FROM_UNIXTIME(REVTSTMP / 1000)) as date, a_au.RE as RE, a_au.stat as STAT 
                    from b_c 
                        join c on b_c.c_id = c.id 
                        join a on c.id = a.c_id 
                        join a_au on a.id = a_au.id 
                        join revi on a_au.RE = revi.RE 
                        join ( 
                            select a.id as ID, DATE(FROM_UNIXTIME(REVTSTMP / 1000)) as date, max(a_au.RE) as MAX_RE 
                                from b_c 
                                join c on b_c.c_id = c.id 
                                join a on c.id = a.c_id 
                                join a_au on a.id = a_au.id 
                                join revi on a_au.RE = revi.RE 
                            where b_c.b_id = 30 GROUP BY ID, date
                        ) x 
                    on x.id = a.id and x.date = date and x.MAX_RE = a_au.RE 
                    where a_au.stat != 20 
                    group by ID, x.date
            ) AS SubTable 
        where date between '2010-07-01' and '2010-07-10' group by date) AS theData 
    ON `DT`.`ddDate` = `theData`.`date` where `DT`.`ddDate` between '2010-07-01' and '2010-07-15';
+1  A: 

Put the Between Logic in a Where Clause

Select Sum(day), day
From Table
Where day Between date1 and date2
Group By day

EDIT: Having should only be used to filter data in the aggregates... i.e.

Having Sum(day) > 10
DaveShaw
But with this SQL statement I do not get the desired result?!But I change this, thanks a lot for this hint.
Tim
I updated my first post with my full SQL statement.
Tim
+2  A: 

Hi Tim, Check out my answer to the following question;

http://stackoverflow.com/questions/3093924/select-all-months-within-given-date-span-including-the-ones-with-0-values

This may be just what you are looking for :)

You can modify your query above as follows (you could integrate this, but this way is simpler!);


SELECT COALESCE(`theData`.`opt`, 0), `DT`.`myDate`
FROM `dateTable` AS DT
LEFT JOIN (
        ... INSERT YOUR QUERY HERE ...
) AS theData
ON `DT`.`myDate` = `theData`.`date`

and you will also need to change the DATE_FORMAT(date, '%d.%m.%Y') AS "day" in your query to just date E.g.

select COALESCE(sum(DUR), 0) AS "r", 0 AS "opt", `date` from

As for @OMG Ponies answer, you will need to pre-populate the dateTable with plenty of rows of data!

Does anyone know how I can post my SQL dump of this table as a file which can be attached? It's quite big, but can be useful...

Dave Rix
So I should create a new table MYDATE with one column of type DATE?And then do a left join on it? Okay... and this is possible to integrate with my given SQL statement (see my updated first post)?
Tim
Updated my answer to include an answer to Tim's question! Basically you can do a single field table, but my linked example provides much more long term flexibility
Dave Rix
Thanks a lot. Hm, I think you can not upload a file here. You have to choose a fileshare service like rapidshare, I do not have a webspace.Oh, but with your implementation in 2012(?) I will have problem with the 29th of February :-)
Tim
Here you go, grab it from my site - GZipped SQL dump from phpMyAdmin. http://www.analysemygolf.net/dimDates.sql.gz
Dave Rix
Ah, okay, I see this is a list of full dates. I thought you only have the dates in there like "20.10.", "21.10", ... but that's all, days, years, ... so I will be in trouble in 2050. I should make a comment in my source code: Warning: only running in the next 40 years, then it will crash :-)
Tim
Indeed! I used Excel 2003 to prepare the data, hence the 65535 rows! Would be easy enough to add the next 179 years... :)
Dave Rix
I updated my first post which the SQL statement which is working fine. Thanks a lot for your help. But perhaps there is something I can change to make the statement "nicer"?
Tim
It's difficult to say without spending a lot of time on it, but maybe create views to handle some of the sub-queries, run the SELECT prefixed with "EXPLAIN EXTENDED" and then do a "SHOW WARNINGS" (when using command line connection) which will show you how the query optimizer will actually run your query. That can help to show how to clean up the code somewhat. You should certainly be able to combine the first two SELECTS so that the LEFT JOIN is part of the original query instead of joining to a nested query. Just remember, MySQL doesn't cache subqueries very well if at all. Hope that helps!
Dave Rix
+1  A: 

Assuming that your date column is a DATETIME column, you need to use something to change time values to be the same for proper grouping to happen. IE:

  SELECT SUM(t.test),
         DATE_FORMAT(t.date, '%Y-%m-%d') AS day
    FROM TABLE t
   WHERE t.date BETWEEN @start AND @end
GROUP BY DATE_FORMAT(t.date, '%Y-%m-%d')

But if there's no record for a given date, the date will not appear in the result set. In other words, no dates with zero will appear in your output.

To solve that, you need to LEFT JOIN to a table of dates, which MySQL doesn't have the ability to generate. It can't even generate a list of numbers, so you have to create a table with a single column:

DROP TABLE IF EXISTS `example`.`numbers`;
CREATE TABLE  `example`.`numbers` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

...and populate it:

INSERT INTO numbers (id) VALUES (NULL)

...before you can use the number value to generate a list of dates using the DATE_ADD function:

  SELECT COALESCE(SUM(t.test), 0),
         x.the_date AS day
     FROM (SELECT DATE_FORMAT(DATE_ADD(NOW(), INTERVAL n.id-1 DAY), '%Y-%m-%d') AS the_date
             FROM NUMBERS n) x
LEFT JOIN your_table yt ON DATE_FORMAT(yt.date, '%Y-%m-%d') = x.the_date
    WHERE x.the_date BETWEEN @start AND @end
 GROUP BY x.the_date
OMG Ponies
thanks a lot, I posted the full SQL statement in my first post.
Tim
How can I use @start AND @end? Should I replace it with '2010-07-01' and '2010-07-10'?
Tim
@Tim: Yes, that was a placeholder for whatever values you want to supply.
OMG Ponies
Okay, this is done, and I created a table called "dates" and execute this statement:INSERT INTO numbers (id) VALUES (NULL)which generates one row id=1. Is this okay?
Tim
@Tim: You'll need to run the insert statement at least 31 times to get the id value up to 31 to cover an entire month. If you need more than that, you need to insert more.
OMG Ponies
Oh, okay, and if two of these SQL statements will run "together", can this cause an error? I have to say, I do not understand exactly what I do. COALESCE is clear, I included it in my statement, but the other things: it seems that the new table is used as kind of a "helper"?! - 366 rows created...
Tim
@Tim: Run together? The `numbers` table needs to be populated before anything else happens. The numbers table contains an incrementing value, which the DATE_ADD uses to add to the current date in order to generate a list of consecutive days. The LEFT JOIN onto your data is to see where the gaps in your data are...
OMG Ponies
Ah, okay, it is really only a helper for MySQL to use the list of int id values. I think it is clear for me now what happens.Now I am struggling to join your statement with mine.If I put the (SELECT DATE_FORMAT(...)...) after my first ... from (...) I do not know how I can join my new "dates" table with my other tables.
Tim