3

我想使用数据库中的池温度数据创建一个注释图表。您可以在 sqlfiddlerextester上查看数据库结构,但为了节省您的点击,这是我正在使用的结构:

DROP TABLE IF EXISTS `temperatures`;
DROP TABLE IF EXISTS `pools`;

CREATE TABLE `pools` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE `temperatures` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `pool_id` int(10) unsigned NOT NULL,
  `temperature` double(8,1) NOT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `temperatures_pool_id_foreign` (`pool_id`),
  CONSTRAINT `temperatures_pool_id_foreign` FOREIGN KEY (`pool_id`) REFERENCES `pools` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=3173 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

INSERT INTO `pools` (`id`, `name`, `created_at`)
VALUES
    (1,'Pool #1','2017-04-08 22:48:03'),
    (2,'Pool #2','2017-04-08 22:48:03'),
    (3,'Pool #3','2017-04-08 22:48:03');

INSERT INTO `temperatures` (`id`, `pool_id`, `temperature`, `created_at`)
VALUES
    (31,1,100.1,'2017-04-09 02:44:56'),
    (32,2,104.2,'2017-04-09 02:44:56'),
    (33,3,97.0,'2017-04-09 02:44:56'),
    (34,1,100.1,'2017-04-09 03:00:04'),
    (35,2,98.4,'2017-04-09 03:00:04'),
    (36,3,96.6,'2017-04-09 03:00:04'),
    (37,1,100.1,'2017-04-09 03:37:13'),
    (38,2,101.8,'2017-04-09 03:37:13'),
    (39,3,96.4,'2017-04-09 03:37:13'),
    (40,1,100.1,'2017-04-09 04:00:04'),
    (41,2,101.8,'2017-04-09 04:00:04'),
    (42,3,96.5,'2017-04-09 04:00:04'),
    (43,1,100.1,'2017-04-09 05:00:04'),
    (44,2,101.8,'2017-04-09 05:00:04');

好的,基本上,我创建了一个控制器,它将返回格式正确的 JSON 以用于 ajax 和 google.visualization.DataTable(),如下所示:

var jsonData = $.ajax({
    url: "/data/pool-temperature-timeline",
    dataType: "json",
    async: false
}).responseText;

data = new google.visualization.DataTable(jsonData);
chart.draw(data, options);

当然,查看文档,注释图表期望事情遵循这种格式:

var data = new google.visualization.DataTable();
data.addColumn('date', 'Date');
data.addColumn('number', 'Kepler-22b mission');
data.addColumn('string', 'Kepler title');
data.addColumn('string', 'Kepler text');
data.addColumn('number', 'Gliese 163 mission');
data.addColumn('string', 'Gliese title');
data.addColumn('string', 'Gliese text');
data.addRows([
    [new Date(2314, 2, 15), 12400, undefined, undefined,
                            10645, undefined, undefined],
    [new Date(2314, 2, 16), 24045, 'Lalibertines', 'First encounter',
                            12374, undefined, undefined],
    [new Date(2314, 2, 17), 35022, 'Lalibertines', 'They are very tall',
                            15766, 'Gallantors', 'First Encounter'],
    [new Date(2314, 2, 18), 12284, 'Lalibertines', 'Attack on our crew!',
                            34334, 'Gallantors', 'Statement of shared principles'],
    [new Date(2314, 2, 19), 8476, 'Lalibertines', 'Heavy casualties',
                            66467, 'Gallantors', 'Mysteries revealed'],
    [new Date(2314, 2, 20), 0, 'Lalibertines', 'All crew lost',
                            79463, 'Gallantors', 'Omniscience achieved']
]);

var chart = new google.visualization.AnnotationChart(document.getElementById('chart_div'));

对了,就是这样设置,现在问题来了。组织数据的最佳方法是什么,以便 1.) 池 1、2 和 3 始终有相同日期时间的温度数据(我担心给定时间戳的数据集可能不完整)?我应该使用一些聪明的查询从 SQL 层开始组织它吗?或者我是否通过使用一堆 foreach 循环在控制器中组织它?这是我正在努力的目标:

$dataTable->addRow(['created_at', 
    'temperature1', 'title1', 'text1',
    'temperature2', 'title2', 'text2',
    'temperature2', 'title2', 'text2',
]);

我可以看到聪明的查询将是避免在控制器中执行一堆逻辑和 foreach 循环的好方法。也许如果数据按列组织,例如:

created_at, pool_1_temperature, pool_2_temperature, pool_3_temperature
------------------------------------------------
2017-04-09 02:44:56, 100.1, 104.2, 97.0
2017-04-09 03:00:04, 100.1, 98.4, 96.6
2017-04-09 03:37:13, 100.1, 101.8, 96.4

然后我可以很容易地完成它并创建 DataTable。我不确定如何在 MySQL 中执行此操作,或者即使这是一个好主意。

感谢您抽出宝贵的时间,并提前感谢您的帮助。我希望我足够清楚。

PS。我想到目前为止我遇到的最接近的事情是 Mysql query to dynamic convert rows to columns。我要再玩这个了……

4

2 回答 2

0

只要 x 轴(第一列)是日期,
您就不必担心...

池 1、2 和 3 始终有相同日期时间的温度数据

图表应该能够解决

因此,您可以使用类似于以下内容的查询...

select
  created_at,
  case when
    pool_id = 1
  then
    temperature
  else
    null
  end pool_1,
  case when
    pool_id = 2
  then
    temperature
  else
    null
  end pool_2,
  case when
    pool_id = 3
  then
    temperature
  else
    null
  end pool_3
from
  temperatures

我无法让提供的任何 SQL 链接正常工作,
所以我无法验证 sql

我不确定返回null是否可行

于 2017-04-11T14:17:20.817 回答
0

为了确保数据是动态的,以防将来添加另一个池,我决定使用填充数组,array_pad()并循环遍历温度数据集,边走边排序。我还使用了 Lavacharts,因为它使处理 Google DataTables 变得容易。所以,这是我的代码(注意,添加注释字段需要更多工作):

$dataTable = \Lava::DataTable();
$dataTable->addDateTimeColumn('DateTime');

// Add data column for each pool
$pools = \App\Pool::get();
foreach($pools as $pool) {
    $p = "Pool $pool->id";
    $dataTable->addNumberColumn("$p Temp");

    // TODO:  Create annotate fields for min and max temperatures
    // For this, we'll need to do some clever padding using array_pad()
    // and more clever index incrementing in the for() loop below.
    // Perhaps it's best to calculate and prepare in the temperatures query?
    //$dataTable->addStringColumn("$p Title");
    //$dataTable->addStringColumn("$p Text");
}

// Gather all the temperature data we wish to display.  A year ought to be enough.
// At one hour updates, that makes for about 8,766 datapoints.
$temperatures = \App\Temperature::where('created_at', '>=', \Carbon\Carbon::now()->subYear())
    ->orderBy('created_at', 'desc')
    ->orderBy('pool_id', 'asc')->get();

// Grab all the timestamps and organize into an array
$created_ats = \App\Temperature::groupBy('created_at')->pluck('created_at');

// Let's go through each datetime field and collect all temperatures recorded on that datetime.
// Then, let's store those temperatures into the appropriate index of the data row.
foreach($created_ats as $created_at) {
    $dataRow = [$created_at]; // Start the array off by adding date to beginning
    $dataRow = array_pad($dataRow, 1 + count($pools), null); // +1 to account for $created_at column
    //$dataRow = array_pad($dataRow, 1 + (count($pools) * 3), null); // TODO: multiply by 3 for annotation fields

    // Start going through each temperature recording and assign to proper spot in dataRow array
    // If temperature is not found for the datetime, the array_pad() above already accounts for null
    // in that index.  Note, the created_at comparison only accounts for the hour, not seconds or minutes.
    // TODO: Implement min and max temperature annotations.
    //$maxTemperature = 0;
    //$minTemperature = 999;
    foreach($temperatures as $temperature) {
        // TODO: Implement min and max temperature annotations.
        //$maxTemperature = ($temperature->temperature >= $maxTemperature) ? $temperature->temperature : $maxTemperature;
        //$minTemperature = ($temperature->temperature <= $minTemperature) ? $temperature->temperature : $minTemperature;

        // Compare date and hour, then assign to appropriate index of the data row according to pool id.
        // ie.  Pool ID #1 needs to be placed in [1], Pool ID #2 in [2] and so forth. Remember, [0] is date.
        if ($temperature->created_at->format('Y-m-d H') == $created_at->format('Y-m-d H')) {
            for ($i = 1; $i <= count($pools); $i++) {
                if($temperature->pool_id == $i) {
                    $dataRow[$i] = $temperature->temperature;
                }
            }
        }
    }

    // We've gone through all temperatures for this created_at datetime.  
    // Add the resulting dataRow to the dataTable.
    $dataTable->addRow($dataRow);
}   

// What we're left with is a bunch of rows that look like this!
// TODO: Add annoation fields for min and max temperatures.
// $dataTable->addRow(['created_at', 
//  'temperature1',
//  'temperature2',
//  'temperature2'
//  ]);
$jsonData = $dataTable->toJson();

// At this point, return $jsonData for use with google.visualization.DataTable(jsonData);
// Or, cache it and then return it, or whatever.

我建议缓存数据,因为在视图中渲染似乎需要一点时间(~1.9s)。所以,也许这不是最快的方法,但它对我有用。进一步挖掘并找到其他优化会很有趣。目前,我对此很满意。

于 2017-04-13T15:13:47.463 回答