ما هي الطريقة الأكثر مباشرة إلى لوحة فارغة التواريخ في sql النتائج (على أي mysql و perl النهاية)?

StackOverflow https://stackoverflow.com/questions/75752

  •  09-06-2019
  •  | 
  •  

سؤال

أنا بناء سريعة csv من الجدول ماي مع استعلام مثل:

select DATE(date),count(date) from table group by DATE(date) order by date asc;

ومجرد إلقاء لهم ملف في بيرل على:

while(my($date,$sum) = $sth->fetchrow) {
    print CSV "$date,$sum\n"
}

هناك تاريخ الثغرات في البيانات ، على الرغم من:

| 2008-08-05 |           4 | 
| 2008-08-07 |          23 | 

أود أن لوحة البيانات إلى ملء المفقودين في أيام صفر-عدد المشاركات نهاية المطاف مع:

| 2008-08-05 |           4 | 
| 2008-08-06 |           0 | 
| 2008-08-07 |          23 | 

لقد صفع معا صعب (ويكاد يكون من المؤكد عربات التي تجرها الدواب) الحل مع مجموعة من الأيام في الشهر وبعض الرياضيات ، ولكن يجب أن يكون هناك شيء أكثر وضوحا إما على mysql و perl الجانب.

أي عبقرية الأفكار/الصفعات على الوجه لمدة لماذا أنا أنا كوني غبية ؟


لقد ذهبت مع إجراء مخزن الذي ولدت temp طاولة نطاق التاريخ في السؤال لعدة أسباب:

  • أعرف تاريخ مجموعة سأكون تبحث عن كل مرة
  • الخادم في السؤال للأسف لم أتمكن من تثبيت وحدات بيرل على أجهزة الصراف الآلي ، والدولة من كان البالية بما فيه الكفاية أنه لم يكن لديهم أي شيء بعد الآن::-y تثبيت

Perl التاريخ/التاريخ والوقت-بالتكرار الإجابات كانت أيضا جيدة جدا ، أتمنى أن اختيار إجابات متعددة!

هل كانت مفيدة؟

المحلول

عندما كنت بحاجة الى شيء مثل هذا على جانب الملقم, يمكنك عادة إنشاء الجدول الذي يحتوي على كل التواريخ الممكنة بين نقطتين في الوقت المناسب ، ثم غادر الانضمام إلى هذا الجدول مع نتائج الاستعلام.شيء من هذا القبيل:

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, وضع إضافة بعض السلاسل.

نصائح أخرى

عندما اضطررت إلى التعامل مع هذه المشكلة ، لملء في عداد المفقودين المواعيد أنا خلقت في الواقع إشارة الجدول التي تحتوي على جميع التواريخ أنا مهتم وانضم إلى جدول البيانات في حقل التاريخ.إنه الخام ، لكنه يعمل.

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 باليد.يترك لنا خالية من القلق حول الهروب من الأحرف الخاصة كذلك.

ليس غباء, هذا ليس شيئا الخلية ، إدراج فارغة قيم التاريخ.أفعل هذا في بيرل مع عملية من خطوتين.أولا, تحميل جميع البيانات من الاستعلام إلى تجزئة تنظمها التاريخ.ثم إنشاء نسخة التاريخ::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 الآن ولكن ربما يمكنك أن تجد آخر بيرل وزارة الدفاع التي سوف لا يقارن وتقديم موعد incrementor.

هل يمكن استخدام التاريخ والوقت وجوه:

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  * 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 إلى الحد الأقصى(n3)*100+ماكس(n2)*10+ماكس(n1)

منذ لدينا هنا ماكس n3 3 ، حدد سيعود 399, بالإضافة إلى 0 -> 400 السجلات (التواريخ في التقويم).

يمكنك ضبط ديناميكية التقويم عن طريق الحد من ذلك ، على سبيل المثال ، من دقيقة(تاريخ) لديك إلى الآن().

منذ كنت لا تعرف أين توجد الفجوات, و الآن أنت تريد كل القيم (يفترض) من أول التاريخ في القائمة الخاصة بك إلى واحد آخر ، أن تفعل شيئا مثل:

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 );
}

همم, التي تحولت إلى أن تكون أكثر تعقيدا مما كنت اعتقد انه سيكون..أتمنى فمن المنطقي!

أعتقد أن أبسط العامة حل المشكلة سيكون لإنشاء 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 بناء الجملة.

استخدام بعض Perl module للقيام حسابات التاريخ ، مثل أوصى التاريخ والوقت أو الوقت::قطعة (الأساسية من 5.10).فقط الاضافة تاريخ تاريخ الطباعة و 0 حتى موعد المباراة الحالي.

أنا لا أعرف إذا كان هذا العمل ، ولكن ماذا عن إذا قمت بإنشاء جدول جديد الذي يتضمن كل التواريخ الممكنة (قد تكون المشكلة مع هذه الفكرة ، إذا كان نطاق من التواريخ هو الذهاب الى تغيير بشكل غير متوقع...) ومن ثم القيام اليسار الانضمام على اثنين من الجداول ؟ أعتقد أنه مجنون الحل إذا كان هناك عدد كبير من التواريخ الممكنة ، أو أي وسيلة للتنبؤ التاريخ الأول والأخير ، ولكن إذا كان نطاق من التواريخ إما ثابتة أو من السهل العمل بها ، ثم ربما هذا العمل.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top