3

我想使用數據庫中的池溫數據創建Annotation Chart。你可以看看數據庫結構here on sqlfiddlehere 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。我要玩弄一些這方面更多...

回答

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

我不知道,如果返回null將工作

+0

希望這會有所幫助,作爲一般規則,您可以在服務器上處理的越多,與客戶端相比,頁面加載速度越快 - 建議儘可能多地將邏輯推入sql,儘可能與javascript對比。 – WhiteHat

+0

非常感謝。這讓我想到我將如何處理我的圖表。你是對的,圖表能夠自行處理一些細節。而不是在需要存儲過程的瘋狂查詢中處理這個問題,我負責使用循環和array_pad()來組織數據。儘快回答我自己的問題......再次感謝!這讓我走上了正軌。 – dhildreth

0

在任一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 )。所以,也許這不是最快捷的方法,但它對我來說是個訣竅。進一步挖掘並找到其他優化會很有趣。現在,我對此感到滿意。