(MVC) Is this an efficient way of displaying chart data by date? - json

I'm just wondering if anyone can tell me if I'm on the right track here.
I have a database that contains temperature values tracked by a device and sorted by datetime. My goal is to create a reporting chart (ex. line chart) via ChartJS. Now the thing is that this table contains thousands of rows and I've never worked with this much data before.
I'm thinking of prompting for a date range and using a date query similar here. I would then return it as JsonResult and have ChartJS make use of it. Is this good enough?

Below are the results from some naive tests I ran (code included too) with 1 chart and 1 dataset on IE11. You'd have to run your own tests specific to the type of chart you are using by adjusting each of the chart options available (read ahead before you start :-)).
Returning a subset of the data will definitely have a positive impact, but the question on whether this is noticeable to compensate for the compromise is very subjective and will need actual measurement (if justified) to figure out.
When you are considering end to end performance, there is no alternative to instrumentation, turning and more instrumentation with a production like environment and of course micro-optimization is the root of all evil (and many missed coffee breaks). A short (and by no means complete) list of other factors to consider would be serialization / deserialization performance, network time, server configuration (compression, et. al), etc.
The below tests are for the client side on a desktop and that too just for time. If mobile is a target environment, you definitely want to run some tests for the environment to look at memory / CPU usage as well.
Stupidly Simple Test
var POINTS = 5000;
var ANIMATION = false;
var BEZIERCURVE = false;
var SCALEOVERRIDE = false;
var ITERATIONS = 10;
// date generation
var data = [];
var labels = [];
for (var i = 0; i < POINTS; i++) {
labels.push("A")
data.push(Math.random() * 100)
}
var chartData = {
labels: labels,
datasets: [
{
data: data
}
]
};
// our charting function
function drawChart() {
var chart;
var startTime = (new Date()).getTime();
chart = new Chart(document.getElementById("myChart").getContext("2d")).Line(chartData, {
animation: ANIMATION,
scaleOverride: SCALEOVERRIDE,
scaleSteps: 10,
scaleStepWidth: 10,
scaleStartValue: 0,
bezierCurve: BEZIERCURVE,
onAnimationComplete: function () {
output.push((new Date()).getTime() - startTime);
if (chart !== undefined)
chart.destroy();
j++;
if (j < ITERATIONS)
setTimeout(drawChart, 0);
else
console.log(output);
}
});
}
var j = 0;
var output = [];
drawChart();
Results
To be taken with a pinch of salt, lime and tequila. Times are in ms and based on a 10 iterations.
----------------------------------------------------------------------------------------
| Type | Points | Animation | Bezier | Scale Override | Mean | Median |
----------------------------------------------------------------------------------------
| Bar | 10 | N | - | N | 2.7 | 3 |
| Bar | 100 | N | - | N | 14 | 13.5 |
| Bar | 1000 | N | - | N | 128.5 | 127.5 |
| Bar | 5000 | N | - | N | 637.4 | 626.5 |
| Bar | 10 | Y | - | N | 997.2 | 997 |
| Bar | 100 | Y | - | N | 1003.5 | 1006.5 |
| Bar | 1000 | Y | - | N | 3417.1 | 3418.5 |
| Bar | 5000 | Y | - | N | 17086.6 | 17085 |
| Bar | 10 | N | - | Y | 3.2 | 3 |
| Bar | 100 | N | - | Y | 14.5 | 14.5 |
| Bar | 1000 | N | - | Y | 127.2 | 125.5 |
| Bar | 5000 | N | - | Y | 638 | 632.5 |
| Bar | 10 | Y | - | Y | 996.6 | 997 |
| Bar | 100 | Y | - | Y | 999.4 | 999 |
| Bar | 1000 | Y | - | Y | 3441.9 | 3433.5 |
| Bar | 5000 | Y | - | Y | 16985.6 | 16959.5 |
| Line | 10 | N | Y | Y | 3.6 | 4 |
| Line | 100 | N | Y | Y | 16.4 | 16 |
| Line | 1000 | N | Y | Y | 146.7 | 145.5 |
| Line | 5000 | N | Y | Y | 821.5 | 820.5 |
| Line | 10 | N | N | Y | 2.9 | 3 |
| Line | 100 | N | N | Y | 14.3 | 14 |
| Line | 1000 | N | N | Y | 131 | 127 |
| Line | 5000 | N | N | Y | 643.9 | 635.5 |
| Line | 10 | N | N | N | 3.1 | 3 |
| Line | 100 | N | N | N | 15.6 | 15 |
| Line | 1000 | N | N | N | 131.9 | 133.5 |
| Line | 5000 | N | N | N | 666 | 660 |
As expected, scale overrides have an impact (but only a little), turning off Bezier curves has a noticeable impact, there not much difference between using a bar chart vs line chart (at least for the configurations I ran). Animation have a noticeable impact as the number of points go up (however I'd assume a simpler easing function will be faster)

Related

MySQL, Insert Row in-between two others

I'm coding a website for a photographer and I'm currently working on gallery implimentation.
I need to be able to take a row from point n.a and move it to point n.b
Here's an example of the raw table:
|gallery_img |
|--------------------------|
| id | fk_gal | fk_img | o |
| | | | |
| 0 | 16 | 240 | 1 |
| 1 | 16 | 322 | 2 |
| 2 | 27 | 240 | 1 |
| 3 | 16 | 245 | 3 |
| 4 | 16 | 210 | 4 |
| 5 | 27 | 530 | 2 |
All fields are INT(11). 'id' Auto_increments. 'fk_gal' and 'fk_img' are linked to other, irrelevant, tables via FOREIGN_KEY.
Now, 'o' is the field I'm focusing on. It determines what order the images will be displayed on the website. This value needs to always be unique for each table. To clarify, If I only call one table, 'o' should be different in every row. However, if I call the entire table, 'o[0]' might reoccur a few times.
So here's what I need. Firstly, I'm only going to be running this function on only one gallery at a time so all visuals of the table from here on out are going to be filtered with 'SELECT * FROM gallery_img WHERE fk_gal = 16'.
I need to change 'o' from n to n2 which will effectively move it on the database.
|gallery_img |
|--------------------------|
| id | fk_gal | fk_img | o |
| | | | |
| 0 | 16 | 240 | 1 |
| | | | | <--
| 1 | 16 | 322 | 2 |+ |
| 3 | 16 | 245 | 3 |+ |
| 4 | 16 | 210 | 4 | --|
The code needs to move the desired row (in this example 'o=4') to 1 and simultaneously move all of the next rows down to prevent any reoccurrences.
Here's my code I have right now. I'm coding my MySql scripts via PHP.
I am using the $n variable here. It includes the following data:
$n = array(gallery_id,img_id,target_o);
sql("UPDATE gallery_img SET o = o + 1 ORDER BY o ASC LIMIT ". ($n[2] - 1) .", 18446744073709551615;");
sql("UPDATE gallery_img SET o = ". ($n[2] + 2) ." WHERE fk_img = $n[1] AND fk_gal = $n[0];");
The problem I'm having with this is that when I execute it I get one of these two outputs:
|gallery_img |
|--------------------------|
| id | fk_gal | fk_img | o |
| | | | |
| 0 | 16 | 240 | 1 |
| 4 | 16 | 210 | 1 | <-- Shouldn't be duplicate
| 1 | 16 | 322 | 2 |
| 3 | 16 | 245 | 3 |
|gallery_img |
|--------------------------|
| id | fk_gal | fk_img | o |
| | | | |
| 0 | 16 | 240 | 1 |
| 4 | 16 | 210 | 2 |
| 1 | 16 | 322 | 4 |-|
| 3 | 16 | 245 | 4 | |-- Shouldn't be duplicate
| 5 | 16 | 273 | 4 | |
| 6 | 16 | 14 | 4 |-|
A good way to think of it is as so:
UPDATE
If you have any questions please let me know!
Thanks ahead of time for your help!
So if you're wanting to change the row WHERE o=4 to o=1 then increment the number to be replaced and all greater numbers.
UPDATE gallery_img SET o = (o + 1) WHERE o >= 1
Then update the row that you want to be o=1:
UPDATE gallery_img SET o = 1 WHERE fk_img = something1 AND fk_gal = something2
Or if you only know the o use o=(4+1) since it changed in the last UPDATE:
UPDATE gallery_img SET o = 1 WHERE o = 5
Can I suggest a hack? For the column o don't use an integer number, but a DOUBLE PRECISION one.
It would be much easier to insert a row in between, just by averaging the values of the previous and next row. If you need to insert between 3 and 4, you can just insert a row with 3.5.
Of course, after some time (after 50 times at least) you would like to re-number those values, since a DOUBLE PRECISION has 53 bits for the mantissa.

How to insert a cell with value on every Nth row in a column in Google Sheets

There is a list of different values as a continuos list (array) in a column.
We need them grouped and separated every Nth row (7th) in another column by inserting some text between them. Not a whole row. Just a cell. As a result they will be "pushed-down" the column.
+------+-----------+-----------+
| | Col2 | Col3 |
+------+-----------+-----------+
| 1 | Sunday | Group 01 |
| 2 | Monday | Sunday |
| 3 | Tuesday | Monday |
| 4 | Wednesday | Tuesday |
| 5 | Thursday | Wednesday |
| 6 | Friday | Thursday |
| 7 | Saturday | Friday |
| 8 | 10 | Saturday |
| 9 | 20 | Group 02 |
| 10 | 30 | 10 |
| 11 | 40 | 20 |
| 12 | 50 | 30 |
| 13 | 60 | 40 |
| 14 | 70 | 50 |
| 15 | MERCURY | 60 |
| 16 | MARS | 70 |
| 17 | JUPITER | Group 03 |
| 18 | VENUS | MERCURY |
| 19 | SATURN | MARS |
| 20 | EARTH | JUPITER |
| 21 | NEPTUNE | VENUS |
| 22 | Mary | SATURN |
| 23 | John | EARTH |
| 24 | Paul | NEPTUNE |
| 25 | Ann | Group 04 |
| 26 | ... | Mary |
| 27 | ... | John |
| 28 | | Paul |
| 29 | | Ann |
| 30 | | |
| 31 | | |
| 32 | | |
| 33 | | |
+------+-----------+-----------+
I have so far tried and succeeded on having the desired result by using a simple query:
=QUERY(({"***Group";B1:B7;"***Group";B8:B14;"***Group";B15:B21;"***Group";B22:B25;}),"SELECT *")
I wonder though. Is there a more "elegant" as well as general way using either formula or script.
I suggest you the following solution based on Apps Script:
function myFunction() {
var sheet=SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
var firstRow=1;
var column=2;
var destColumn=3;
var increment=7;
var lastRow=sheet.getLastRow();
var values=sheet.getRange(firstRow, column, lastRow,1).getValues();
var array=[];
var k=1;
Logger.log(lastRow);
for(var i=0;i<lastRow;i++){
if(i%increment==0){
array.push("Group "+k);
k++;
}
array.push(values[i][0]);
}
var outerArray = [];
for(var j=0;j<array.length;j++){
var tempArray = [];
tempArray.push(array[j]);
Logger.log(array[j]);
Logger.log(tempArray);
outerArray.push(tempArray);
}
sheet.getRange(1, destColumn, array.length, 1).setValues(outerArray);
}
Workflow:
Push the contents of the column of interest into an array
Insert an additional entry with content every incrementth time
Transpose the array into the [column][row] syntax
Insert the values into the destination column
References:
SpreadsheetApp
getRange
getValues
setValues
You could use Array.splice to insert arrays every Nth row:
/**
* Inserts string every nth row
*
* #customfunction
* #param {Object[][]} arr Input Column
* #param {number} every Nth row to insert
* #param {string} str String to insert
*/
function insertText(arr, every, str) {
for (
var i = 0, j = 0;
i < arr.length - 1 && arr.splice(i++, 0, [str + ++j]);
i += every
);
return arr;
}
const arr = [ //Input col A1:A25 looks like this
['Sunday'],
['Monday'],
['Tuesday'],
['Wednesday'],
['Thursday'],
['Friday'],
['Saturday'],
['10'],
['20'],
['30'],
['40'],
['50'],
['60'],
['70'],
['MERCURY'],
['MARS'],
['JUPITER'],
['VENUS'],
['SATURN'],
['EARTH'],
['NEPTUNE'],
['Mary'],
['John'],
['Paul'],
['Ann'],
];
console.log(insertText(arr, 7, 'Group'));

MySQL select all pinned and some other flagged record to reach limit

I have a "products" table where are stored all the store products, some of them are flagged with "hp" flag and some also with "hppin" flag
I need 2 flags because i pick 10 random products from all of the "hp" flagged ( they could 100 or more ) but I need to get all the pinned one.
The pinned need to be always inside the 10 selected products
I need 10 records = all "hppin" flag + random "hp" flag
example table
PRODUCTS
| id | name | hppin | hp |
| 1 | prod1 | y | y |
| 2 | prod2 | n | y |
| 3 | prod3 | y | y |
| 4 | prod4 | n | y |
| 5 | prod5 | n | n |
| 6 | prod6 | y | y |
| 7 | prod7 | n | y |
| 8 | prod8 | n | y |
| 9 | prod9 | n | y |
| 10 | prod10 | n | y |
| 11 | prod11 | n | y |
| 12 | prod12 | n | y |
| 13 | prod13 | n | y |
| 14 | prod14 | n | n |
| 15 | prod15 | n | y |
I could solve this with 2 query but I would like to know if is possible
to do it just in one query.
The result should be record 1,3,6 + 7 random record with hp = y
You can phrase your query using a union between the two sets of data which you want to obtain. The first half of the below union retrives hppin yes records. The second half obtains hp yes records. We then apply a limit of 10 records using an ordering which gives preference to the hppin matches first. The hp records would only enter the result set if there were fewer than 10 hppin records, which would be the case for your sample data.
SELECT id, name, hppin, hp, 1 AS position
FROM PRODUCTS
WHERE hppin = 'y'
UNION ALL
SELECT id, name, hppin, hp, 2
FROM PRODUCTS
WHERE hp = 'y'
ORDER BY
position, RAND()
LIMIT 10

Interdependent MySQL Calculation Triggers

I am currently developing a large MySQL database that has 4 main tables within it as follows:
Table Damage Loop
---------------------------------------------------------
| Damage Loop No | Description | Minimum Remaining Life |
---------------------------------------------------------
| abc | pie | 5 |
| xyz | apple | 15 |
| ... | ... | ... |
---------------------------------------------------------
Table Equipment
---------------------------------------------------------------------------
| Damage Loop No | Equipment No | Description | Minimum Remaining Life |
---------------------------------------------------------------------------
| abc | X100 | pie slice 1 | 5 |
| abc | X200 | pie slice 2 | 20 |
| xyz | B100 | apple slice 1 | 15 |
| xyz | B200 | apple slice 2 | 30 |
| ... | ... | ... | ... |
---------------------------------------------------------------------------
Table Monitoring Location
--------------------------------------------------------------------------------------------------------------------------------------
| Damage Loop No | Equipment No | CML No | Description | Corrosion Rate | Wall Thickness | Allowable Wall Thickness | Remaining Life |
--------------------------------------------------------------------------------------------------------------------------------------
| abc | X100 | 1 | Location 1 | 0.2 | 3 | 2 | 5
| abc | X100 | 2 | Location 2 | 0.2 | 4 | 2 | 10
| xyz | B100 | 1 | Location 1 | 0.2 | 5 | 2 | 15
| xyz | B100 | 2 | Location 2 | 0.2 | 6 | 2 | 20
| ... | ... | ... | ... | ... | ... | ... | ...
--------------------------------------------------------------------------------------------------------------------------------------
Table Inspection
---------------------------------------------------------------------------
| Damage Loop No | Equipment No | CML No | Inspection No | Wall Thickness |
---------------------------------------------------------------------------
| abc | X100 | 1 | 1 | 5 |
| abc | X100 | 1 | 2 | 10 |
| abc | X100 | 1 | 3 | 3 |
| ... | ... | ... | ... | ... |
---------------------------------------------------------------------------
Now these tables all have interdependent calculations that I would like to perform as triggers based on what values the user changes in the database. For example if we look at [Damage Loop].[Minimum Remaining Life]. It is calculated as follows...
[Damage Loop].[Minimum Remaining Life] = Min([Equipment].[Minimum Remaining Life] For Associated Records)
Where [Equipment].[Minimum Remaining Life] is calculated as follows...
[Equipment].[Minimum Remaining Life] = Min([Monitoring Location].[Remaining Life] For Associated Records)
Where the [Monitoring Location].[Remaining Life] is calculated as follows...
[Monitoring Location].[Remaining Life] = ([Wall Thickness] - [Allowable Wall Thickness])/[Corrosion Rate]
Where...
[Wall Thickness] = Min([Inspection].[Wall Thickness] for associated records)
[Allowable Wall Thickness] = Manual input
[Corrosion Rate] = Manual input
So if the user changes the [Monitoring Location].[Allowable Wall Thickness] field I would like the trigger to only recalculate the following fields...
[Monitoring Location].[Remaining Life]
[Equipment].[Remaining Life]
[Damage Loop].[Remaining Life]
But if the user changes the [Inspection].[Wall Thickness] field I would like the trigger to perform the following calculations...
[Monitoring Location].[Wall Thickness]
[Monitoring Location].[Remaining Life]
[Equipment].[Remaining Life]
[Damage Loop].[Remaining Life]
i.e. only re-calculate the fields that are dependent on the field the user changes.
I basically have hundreds of calculations like these in my system and would like a really efficient and structured way of only recalculating dependent fields based on what the user changes in the database. I would like to avoid having to write specific triggers for every possible case in the system. Any ideas of how I can achieve this?

In MySQL, how do I write a query to select ranges of data from locations in another table?

I am currently trying to process a large block of simulation data (~2Gb worth). The data is in a table which looks like:
Table: Simulation Data
+-------+--------+----------+-------+
| id | run_id | timestep | value |
+-------+--------+----------+-------+
| 1 | 1 | 1 | 0.00 |
| 2 | 1 | 2 | 0.003 |
| : | : | : | : |
| 9543 | 1 | 9543 | 0.23 |
| 9544 | 2 | 1 | 0.00 |
| : | : | : | : |
+-------+--------+----------+-------+
So for each run (identified by a run_id) there are a number of time steps with corresponding data (in the case of run_id 1, there were 9543 time steps).
Durring a simulation run, there are events which take place. These event time steps are recorded in another table:
Table: Simulation Events
+-------+--------+----------+
| id | run_id | timestep |
+-------+--------+----------+
| 1 | 1 | 152 |
| 2 | 1 | 193 |
| 3 | 1 | 382 |
| : | : | : |
| 143 | 1 | 9382 |
| 144 | 2 | 137 |
| : | : | : |
+-------+--------+----------+
So for this set of data, with run_id 1, there were events at time step 152, 193, 382, ... 9382. run_id 2 has its first event at time step 137, etc. I am interested in what happens in the 3-timesteps before, the time step of, and the 3-timesteps after each event for each run_id. I would like to put together a query that returns something that looks like:
+--------+----------------+----------+-------+
| run_id | event_timestep | delta_ts | value |
+--------+----------------+----------+-------+
| 1 | 152 | -3 | 0.053 |
| 1 | 152 | -2 | 0.042 |
| 1 | 152 | -1 | 0.031 |
| 1 | 152 | 0 | 0.003 |
| 1 | 152 | 1 | 0.532 |
| 1 | 152 | 2 | 0.736 |
| 1 | 152 | 3 | 1.138 |
| 1 | 193 | -3 | 0.049 |
| : | : | : | : |
| 1 | 9382 | -3 | 0.068 |
| : | : | : | : |
| 1 | 9382 | 3 | 1.523 |
+--------+----------------+----------+-------+
Where the first row, with delta_ts = -3 would be the value from timestep 149, -2 would be from timestep 150, -1 from timestep 151, etc. Any thoughts on putting together a query that would do this?
There's two differing points of view on this:
You can use blank joins (cartesian products) select ... from table t1, table t2 where ..., but you have to figure out a condition that links two rows if and only if they're "related". Also keep in mind that pairs are commutative in your example, so add a condition like t1.id<t2.id -- also excludes self-joins.
or you can use a cursor in a stored procedure, and store values for the previous n steps, and correlate them manually. This is slower, uses more memory, but it's easier to write.