Используя ThorsSerizlier и тот же ввод, что и.
Предположим, что вход input.json
#include "ThorSerialize/JsonThor.h"
#include "ThorSerialize/SerUtil.h"
#include "ThorSerialize/Traits.h"
#include
#include
struct Data
{
std::vector> values; // The values input
};
ThorsAnvil_MakeTrait(Data, values); // Describes what can be serialized
// There is a definition for all standard
// types in SerUtil.h
int main()
{
std::ifstream dataStream("input.json");
Data data;
// Automatically reads data into C++ structure.
// Directly from a stream.
dataStream >> ThorsAnvil::Serialize::jsonImport(data);
// Converts a C++ structure into JSON for output to a stream.
std::cout << ThorsAnvil::Serialize::jsonExport(data);
}
Когда вам нужно что-то подобное на стороне сервера, вы обычно создаете таблицу, которая содержит все возможные даты между двумя точками во времени, а затем левая присоединяется к этой таблице с результатами запроса. Что-то вроде этого:
create procedure sp1(d1 date, d2 date)
declare d datetime;
create temporary table foo (d date not null);
set d = d1
while d <= d2 do
insert into foo (d) values (d)
set d = date_add(d, interval 1 day)
end while
select foo.d, count(date)
from foo left join table on foo.d = table.date
group by foo.d order by foo.d asc;
drop temporary table foo;
end procedure
В этом конкретном случае было бы лучше поставить небольшую проверку на стороне клиента, если текущая дата не предшествует + 1, поместите некоторые строки добавления.
Вы можете использовать объект DateTime :
use DateTime;
my $dt;
while ( my ($date, $sum) = $sth->fetchrow ) {
if (defined $dt) {
print CSV $dt->ymd . ",0\n" while $dt->add(days => 1)->ymd lt $date;
}
else {
my ($y, $m, $d) = split /-/, $date;
$dt = DateTime->new(year => $y, month => $m, day => $d);
}
print CSV, "$date,$sum\n";
}
. Что делает вышеприведенный код, это сохранение последней печатной даты, сохраненной в объекте DateTime
$dt
, и если текущая дата больше одного дня в будущем, она увеличивает $dt
на один день (и печатает ее в строке CSV
) до тех пор, пока она не будет такой же, как текущая дата.
Таким образом вам не нужны дополнительные таблицы, и вам не нужно забирать все ваши строки заранее.
Когда мне приходилось справляться с этой проблемой, чтобы заполнить отсутствующие даты, я на самом деле создал справочную таблицу, в которой содержались все даты, которые мне интересны, и присоединился к таблице данных в поле даты. Это грубо, но это работает.
SELECT DATE(r.date),count(d.date)
FROM dates AS r
LEFT JOIN table AS d ON d.date = r.date
GROUP BY DATE(r.date)
ORDER BY r.date ASC;
Что касается вывода, я бы просто использовал SELECT INTO OUTFILE вместо генерации CSV вручную. Остается нам не беспокоиться о том, чтобы избежать специальных символов.
Используйте некоторые модули Perl для выполнения расчетов по дате, например, рекомендуемые DateTime или Time :: Piece (ядро из 5.10). Просто увеличьте дату и дату печати, а 0 до даты будет соответствовать текущему.
Я не знаю, будет ли это работать, но как насчет создания новой таблицы, содержащей все возможные даты (это может быть проблема с этой идеей, если диапазон дат будет непредсказуемым образом изменяться). .), а затем сделать левое соединение на двух таблицах? Я предполагаю, что это сумасшедшее решение, если существует огромное количество возможных дат или нет способа предсказать первую и последнюю дату, но если диапазон дат является либо фиксированным, либо легким в работе, это может сработать.
Так как вы не знаете, где пробелы, и все же вы хотите, чтобы все значения (предположительно) с первой даты в вашем списке до последнего, выполните что-то вроде:
use DateTime;
use DateTime::Format::Strptime;
my @row = $sth->fetchrow;
my $countdate = strptime("%Y-%m-%d", $firstrow[0]);
my $thisdate = strptime("%Y-%m-%d", $firstrow[0]);
while ($countdate) {
# keep looping countdate until it hits the next db row date
if(DateTime->compare($countdate, $thisdate) == -1) {
# counter not reached next date yet
print CSV $countdate->ymd . ",0\n";
$countdate = $countdate->add( days => 1 );
$next;
}
# countdate is equal to next row's date, so print that instead
print CSV $thisdate->ymd . ",$row[1]\n";
# increase both
@row = $sth->fetchrow;
$thisdate = strptime("%Y-%m-%d", $firstrow[0]);
$countdate = $countdate->add( days => 1 );
}
Хм, это оказалось сложнее, чем я думал, что это будет .. Надеюсь, это имеет смысл!
не тупой, это не то, что делает MySQL, вставляя пустые значения даты. Я делаю это в perl с двухэтапным процессом. Сначала загрузите все данные из запроса в хэш, организованный по дате. Затем я создаю объект Date :: EzDate и увеличиваю его на день, поэтому ...
my $current_date = Date::EzDate->new();
$current_date->{'default'} = '{YEAR}-{MONTH NUMBER BASE 1}-{DAY OF MONTH}';
while ($current_date <= $final_date)
{
print "$current_date\t|\t%hash_o_data{$current_date}"; # EzDate provides for automatic stringification in the format specfied in 'default'
$current_date++;
}
, где конечная дата - другой объект EzDate или строка, содержащая конец диапазона дат.
EzDate сейчас не работает в CPAN, но вы, вероятно, можете найти еще один мотив perl, который будет сравнивать дату и предоставить инкремент даты.
Надеюсь, вы поймете остальное.
select * from (
select date_add('2003-01-01 00:00:00.000', INTERVAL n5.num*10000+n4.num*1000+n3.num*100+n2.num*10+n1.num DAY ) as date from
(select 0 as num
union all select 1
union all select 2
union all select 3
union all select 4
union all select 5
union all select 6
union all select 7
union all select 8
union all select 9) n1,
(select 0 as num
union all select 1
union all select 2
union all select 3
union all select 4
union all select 5
union all select 6
union all select 7
union all select 8
union all select 9) n2,
(select 0 as num
union all select 1
union all select 2
union all select 3
union all select 4
union all select 5
union all select 6
union all select 7
union all select 8
union all select 9) n3,
(select 0 as num
union all select 1
union all select 2
union all select 3
union all select 4
union all select 5
union all select 6
union all select 7
union all select 8
union all select 9) n4,
(select 0 as num
union all select 1
union all select 2
union all select 3
union all select 4
union all select 5
union all select 6
union all select 7
union all select 8
union all select 9) n5
) a
where date >'2011-01-02 00:00:00.000' and date < NOW()
order by date
С помощью
select n3.num*100+n2.num*10+n1.num as date
вы получите столбец с номерами от 0 до max (n3) * 100 + max (n2) * 10 + max (n1)
Так как здесь max n3 равно 3, SELECT вернет 399, плюс 0 -> 400 записей (даты в календаре).
Вы можете настроить свой динамический календарь, ограничив его, например, с min (date), который вы должны сейчас ().
Я думаю, что самым простым общим решением этой проблемы было бы создание таблицы Ordinal
с наибольшим количеством требуемых строк (в вашем случае 31 * 3 = 93).
CREATE TABLE IF NOT EXISTS `Ordinal` (
`n` int(10) unsigned NOT NULL AUTO_INCREMENT, PRIMARY KEY (`n`)
);
INSERT INTO `Ordinal` (`n`)
VALUES (NULL), (NULL), (NULL); #etc
Затем сделайте LEFT JOIN
из Ordinal
на свои данные. Вот простой случай, получаемый каждый день на прошлой неделе:
SELECT CURDATE() - INTERVAL `n` DAY AS `day`
FROM `Ordinal` WHERE `n` <= 7
ORDER BY `n` ASC
Две вещи, которые вам нужно изменить, это начальная точка и интервал. Я использовал синтаксис SET @var = 'value'
для ясности.
SET @end = CURDATE() - INTERVAL DAY(CURDATE()) DAY;
SET @begin = @end - INTERVAL 3 MONTH;
SET @period = DATEDIFF(@end, @begin);
SELECT @begin + INTERVAL (`n` + 1) DAY AS `date`
FROM `Ordinal` WHERE `n` < @period
ORDER BY `n` ASC;
Таким образом, окончательный код выглядел бы примерно так, если бы вы присоединились к числу сообщений в день за последние три месяца:
SELECT COUNT(`msg`.`id`) AS `message_count`, `ord`.`date` FROM (
SELECT ((CURDATE() - INTERVAL DAY(CURDATE()) DAY) - INTERVAL 3 MONTH) + INTERVAL (`n` + 1) DAY AS `date`
FROM `Ordinal`
WHERE `n` < (DATEDIFF((CURDATE() - INTERVAL DAY(CURDATE()) DAY), ((CURDATE() - INTERVAL DAY(CURDATE()) DAY) - INTERVAL 3 MONTH)))
ORDER BY `n` ASC
) AS `ord`
LEFT JOIN `Message` AS `msg`
ON `ord`.`date` = `msg`.`date`
GROUP BY `ord`.`date`
Советы и комментарии:
Ordinal
. Для сравнения, преобразование этой целочисленной последовательности в даты было простым. Ordinal
для всех ваших потребностей в непрерывной последовательности. Просто убедитесь, что он содержит больше строк, чем ваша самая длинная последовательность. Ordinal
для нескольких последовательностей, например, перечисляя каждый будний день (1-5) за последние семь (1-7 ) недель. Ordinal
, но она будет менее гибкой. Таким образом, вам нужна только одна таблица Ordinal
, независимо от того, сколько раз вы ее используете. Тем не менее, если скорость стоит того, попробуйте синтаксис INSERT INTO ... SELECT
.