SQL query that returns all dates not used in a tab

2019-01-25 22:52发布

So lets say I have some records that look like:

2011-01-01 Cat
2011-01-02 Dog
2011-01-04 Horse
2011-01-06 Lion

How can I construct a query that will return 2011-01-03 and 2011-01-05, ie the unused dates. I postdate blogs into the future and I want a query that will show me the days I don't have anything posted yet. It would look from the current date to 2 weeks into the future.

Update:

I am not too excited about building a permanent table of dates. After thinking about it though it seems like the solution might be to make a small stored procedure that creates a temp table. Something like:

CREATE PROCEDURE MISSING_DATES()
BEGIN
    CREATE TABLE TEMPORARY DATES (FUTURE DATETIME NULL)
    INSERT INTO DATES (FUTURE) VALUES (CURDATE())
    INSERT INTO DATES (FUTURE) VALUES (ADDDATE(CURDATE(), INTERVAL 1 DAY))
    ...
    INSERT INTO DATES (FUTURE) VALUES (ADDDATE(CURDATE(), INTERVAL 14 DAY))

    SELECT FUTURE FROM DATES WHERE FUTURE NOT IN (SELECT POSTDATE FROM POSTS)

    DROP TABLE TEMPORARY DATES
END 

I guess it just isn't possible to select the absence of data.

4条回答
爱情/是我丢掉的垃圾
2楼-- · 2019-01-25 22:57

One solution would be to create a separate table with one column to hold all dates from now until eternity (or whenever you expect to stop blogging). For example:

CREATE TABLE Dates (dt DATE);
INSERT INTO Dates VALUES ('2011-01-01');
INSERT INTO Dates VALUES ('2011-01-02');
...etc...
INSERT INTO Dates VALUES ('2099-12-31');

Once this reference table is set up, you can simply outer join to determine the unused dates like so:

SELECT d.dt 
FROM Dates d LEFT JOIN Blogs b ON d.dt = b.dt 
WHERE b.dt IS NULL

If you want to limit the search to two weeks in the future, you could add this to the WHERE clause:

AND d.dt BETWEEN NOW() AND ADDDATE(NOW(), INTERVAL 14 DAY)
查看更多
▲ chillily
3楼-- · 2019-01-25 23:00

You probably not going to like this:

select '2011-01-03', count(*) from TABLE where postdate='2011-01-03' 
  having count(*)=0 union
select '2011-01-04', count(*) from TABLE where postdate='2011-01-04' 
  having count(*)=0 union
select '2011-01-05', count(*) from TABLE where postdate='2011-01-05' 
  having count(*)=0 union
... repeat for 2 weeks

OR

create a table with all days in 2011, then do a left join, like

select a.days_2011
from all_days_2011
left join TABLE on a.days_2011=TABLE.postdate
where a.days_2011 between date(now()) and date(date_add(now(), interval 2 week))
and TABLE.postdate is null;
查看更多
Ridiculous、
4楼-- · 2019-01-25 23:12

The way to extract rows from the mysql database is via SELECT. Thus you cannot select rows that do not exist.

What I would do is fill my blog table with all possible dates (for a year, then repeat the process)

 create table blog (
    thedate date not null,
    thetext text null,
    primary key (thedate));

doing a loop to create all dates entries for 2011 (using a program, eg $mydate is the date you want to insert)

  insert IGNORE into blog (thedate,thetext) values ($mydate, null);

(the IGNORE keyword to not create an error (thedate is a primary key) if thedate exists already).
Then you insert the values normally

  insert into blog (thedate,thetext) values ($mydate, "newtext") 
  on duplicate key update thetext="newtext";

Finally to select empty entries, you just have to

  select thedate from blog where thetext is null;
查看更多
再贱就再见
5楼-- · 2019-01-25 23:15

You're right — SQL does not make it easy to identify missing data. The usual technique is to join your sequence (with gaps) against a complete sequence, and select those elements in the latter sequence without a corresponding partner in your data.

So, @BenHoffstein's suggestion to maintain a permanent date table is a good one.

Short of that, you can dynamically create that date range with an integers table. Assuming the integers table has a column i with numbers at least 0 – 13, and that your table has its date column named datestamp:

   SELECT candidate_date AS missing
     FROM (SELECT CURRENT_DATE + INTERVAL i DAY AS candidate_date
             FROM integers
            WHERE i < 14) AS next_two_weeks
LEFT JOIN my_table ON candidate_date = datestamp
    WHERE datestamp is NULL;
查看更多
登录 后发表回答