Filling Gaps in Dates Returned from Database - Pure SQL Solution Possible

Tsql script to fill holes on a time line

Cross-join with a numbers table or left join from a numbers table.

See this example regarding date sequences.

MySQL - fill missing dates

In general, you can generate a series of N integers in MySQL using:

    select (@i:=@i+1)-1 as `myval` from someTable,(SELECT @i:=0) gen_sub limit N

Note that the table that you join on (someTable) must have at least N rows. The -1 above is to make it base-zero... remove it and you'll get 1,2,3 for N=3.

You can feed those integers into the DATE_ADD function to turn it into a series of dates. To make it easier to follow, let's use some user variables for the dates.

SET @date_min = '2016-03-04';
SET @date_max = '2016-03-10';

select DATE_ADD(@date_min, INTERVAL (@i:=@i+1)-1 DAY) as `date`
from information_schema.columns,(SELECT @i:=0) gen_sub
where DATE_ADD(@date_min,INTERVAL @i DAY) BETWEEN @date_min AND @date_max

That will return rows for those days and every day between them. Now it's just a matter of joining against your table... I haven't tried it since I don't have your db structure, but something like the following should work:

SET @date_min = '2016-03-04';
SET @date_max = '2016-03-10';

SELECT
date_generator.date,
ifnull(SUM(val1),0) as sum_val
from (
select DATE_ADD(@date_min, INTERVAL (@i:=@i+1)-1 DAY) as `date`
from information_schema.columns,(SELECT @i:=0) gen_sub
where DATE_ADD(@date_min,INTERVAL @i DAY) BETWEEN @date_min AND @date_max
) date_generator
left join table1 on table1.date = date_generator.date
GROUP BY date;

SQL to determine minimum sequential days of access?

The answer is obviously:

SELECT DISTINCT UserId
FROM UserHistory uh1
WHERE (
SELECT COUNT(*)
FROM UserHistory uh2
WHERE uh2.CreationDate
BETWEEN uh1.CreationDate AND DATEADD(d, @days, uh1.CreationDate)
) = @days OR UserId = 52551

EDIT:

Okay here's my serious answer:

DECLARE @days int
DECLARE @seconds bigint
SET @days = 30
SET @seconds = (@days * 24 * 60 * 60) - 1
SELECT DISTINCT UserId
FROM (
SELECT uh1.UserId, Count(uh1.Id) as Conseq
FROM UserHistory uh1
INNER JOIN UserHistory uh2 ON uh2.CreationDate
BETWEEN uh1.CreationDate AND
DATEADD(s, @seconds, DATEADD(dd, DATEDIFF(dd, 0, uh1.CreationDate), 0))
AND uh1.UserId = uh2.UserId
GROUP BY uh1.Id, uh1.UserId
) as Tbl
WHERE Conseq >= @days

EDIT:

[Jeff Atwood] This is a great fast solution and deserves to be accepted, but Rob Farley's solution is also excellent and arguably even faster (!). Please check it out too!

How to get the count of current month Sunday's in psql?

The total number of Sundays for a given date can only be either 0 or 1.

But if you want the number of Sundays within a given date range, then your best bet is a calendar table. To find how many Sundays are in February this year, I'd just

select count(*) 
from calendar
where cal_date between '2011-02-01' and '2011-02-28' and
day_of_week = 'Sun';

or

select count(*)
from calendar
where year_of_date = 2011 and
month_of_year = 2 and
day_of_week = 'Sun';

Here's a basic calendar table that you can start with. I also included a PostgreSQL function to populate the calendar table. I haven't tested this in 8.3, but I'm pretty sure I'm not using any features that 8.3 doesn't support.

Note that the "dow" parts assume your days are in English. But you can easily edit those parts to match any language. (I think. But I could be wrong about "easily".)

-- Table: calendar

-- DROP TABLE calendar;

CREATE TABLE calendar
(
cal_date date NOT NULL,
year_of_date integer NOT NULL,
month_of_year integer NOT NULL,
day_of_month integer NOT NULL,
day_of_week character(3) NOT NULL,
CONSTRAINT calendar_pkey PRIMARY KEY (cal_date),
CONSTRAINT calendar_check CHECK (year_of_date::double precision = date_part('year'::text, cal_date)),
CONSTRAINT calendar_check1 CHECK (month_of_year::double precision = date_part('month'::text, cal_date)),
CONSTRAINT calendar_check2 CHECK (day_of_month::double precision = date_part('day'::text, cal_date)),
CONSTRAINT calendar_check3 CHECK (day_of_week::text =
CASE
WHEN date_part('dow'::text, cal_date) = 0::double precision THEN 'Sun'::text
WHEN date_part('dow'::text, cal_date) = 1::double precision THEN 'Mon'::text
WHEN date_part('dow'::text, cal_date) = 2::double precision THEN 'Tue'::text
WHEN date_part('dow'::text, cal_date) = 3::double precision THEN 'Wed'::text
WHEN date_part('dow'::text, cal_date) = 4::double precision THEN 'Thu'::text
WHEN date_part('dow'::text, cal_date) = 5::double precision THEN 'Fri'::text
WHEN date_part('dow'::text, cal_date) = 6::double precision THEN 'Sat'::text
ELSE NULL::text
END)
)
WITH (
OIDS=FALSE
);
ALTER TABLE calendar OWNER TO postgres;

-- Index: calendar_day_of_month

-- DROP INDEX calendar_day_of_month;

CREATE INDEX calendar_day_of_month
ON calendar
USING btree
(day_of_month);

-- Index: calendar_day_of_week

-- DROP INDEX calendar_day_of_week;

CREATE INDEX calendar_day_of_week
ON calendar
USING btree
(day_of_week);

-- Index: calendar_month_of_year

-- DROP INDEX calendar_month_of_year;

CREATE INDEX calendar_month_of_year
ON calendar
USING btree
(month_of_year);

-- Index: calendar_year_of_date

-- DROP INDEX calendar_year_of_date;

CREATE INDEX calendar_year_of_date
ON calendar
USING btree
(year_of_date);

And a rudimentary function to populate the table. I haven't tested this in 8.3 either.

-- Function: insert_range_into_calendar(date, date)

-- DROP FUNCTION insert_range_into_calendar(date, date);

CREATE OR REPLACE FUNCTION insert_range_into_calendar(from_date date, to_date date)
RETURNS void AS
$BODY$

DECLARE
this_date date := from_date;
BEGIN

while (this_date <= to_date) LOOP
INSERT INTO calendar (cal_date, year_of_date, month_of_year, day_of_month, day_of_week)
VALUES (this_date, extract(year from this_date), extract(month from this_date), extract(day from this_date),
case when extract(dow from this_date) = 0 then 'Sun'
when extract(dow from this_date) = 1 then 'Mon'
when extract(dow from this_date) = 2 then 'Tue'
when extract(dow from this_date) = 3 then 'Wed'
when extract(dow from this_date) = 4 then 'Thu'
when extract(dow from this_date) = 5 then 'Fri'
when extract(dow from this_date) = 6 then 'Sat'
end);
this_date = this_date + interval '1 day';
end loop;

END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;

Selecting elements that don't exist

This will return NULL if a code is not assigned:

SELECT  assigned_codes.code
FROM codes
LEFT JOIN
assigned_codes
ON assigned_codes.code = codes.code
WHERE codes.code = @code

This will return all non-assigned codes:

SELECT  codes.code
FROM codes
LEFT JOIN
assigned_codes
ON assigned_codes.code = codes.code
WHERE assigned_codes.code IS NULL

There is no pure SQL way to do exactly the thing you want.

In Oracle, you can do the following:

SELECT  lvl
FROM (
SELECT level AS lvl
FROM dual
CONNECT BY
level <=
(
SELECT MAX(code)
FROM elements
)
)
LEFT OUTER JOIN
elements
ON code = lvl
WHERE code IS NULL

In PostgreSQL, you can do the following:

SELECT  lvl
FROM generate_series(
1,
(
SELECT MAX(code)
FROM elements
)) lvl
LEFT OUTER JOIN
elements
ON code = lvl
WHERE code IS NULL


Related Topics



Leave a reply



Submit