从温度和ID数据库创建Google Charts API数据表
我想使用数据库中的池温数据创建Annotation Chart。你可以看看数据库结构here on sqlfiddle或here on rextester,但你救一下,这里是我的工作结构:从温度和ID数据库创建Google Charts API数据表
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');
好了,所以基本上,我创建了一个控制器,将返回正确的格式与AJAX和google.visualization.DataTable(使用JSON),像这样:
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
然后,我可以很容易地去通过,并创建数据表。我不确定如何在MySQL中做到这一点,或者即使这是一个好主意。
感谢您抽出时间,并提前感谢您的帮助。我希望我很清楚。
PS。我想我到目前为止最接近的东西是Mysql query to dynamically convert rows to columns。我要玩弄一些这方面更多...
只要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
我不知道,如果返回null
将工作
在任一SQL链接为了确保数据是动态的,如果未来还添加了另一个池,我决定使用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 )。所以,也许这不是最快捷的方法,但它对我来说是个诀窍。进一步挖掘并找到其他优化会很有趣。现在,我对此感到满意。
希望这会有所帮助,作为一般规则,您可以在服务器上处理的越多,与客户端相比,页面加载速度越快 - 建议尽可能多地将逻辑推入sql,尽可能与javascript对比。 – WhiteHat
非常感谢。这让我想到我将如何处理我的图表。你是对的,图表能够自行处理一些细节。而不是在需要存储过程的疯狂查询中处理这个问题,我负责使用循环和array_pad()来组织数据。尽快回答我自己的问题......再次感谢!这让我走上了正轨。 – dhildreth