Implementing DISTINCT ON in CubeJS - javascript

I have a Postgres table like this, with device ID, timestamp, and the status of the device at that time:
dev_id | timestamp | status
----------------------------------------
1 | 2020-08-06 23:00:00 | 1
2 | 2020-08-06 23:00:00 | 0
3 | 2020-08-06 23:00:00 | 1
2 | 2020-08-06 23:05:00 | 1
3 | 2020-08-06 23:05:00 | 0
1 | 2020-08-06 23:10:00 | 0
I want to see in their respective latest timestamp, how many of devices were functioning and how many not functioning. In Postgres, I can use DISTINCT ON and write the query like this:
SELECT status, COUNT(status)
FROM
(
SELECT DISTINCT ON (dev_id) dev_id,
timestamp,
status
FROM
sample_metrics_data
ORDER BY
dev_id,
timestamp DESC
) sub
GROUP BY status;
This will result in:
value | count
---------------
0 | 2
1 | 1
(2 devices, #1 & #3, have a status of 0, while 1, #2, has a status of 1.)
How can I create something like this in CubeJS? Is DISTINCT ON supported, and if not, what is the way around it?
Alternatively, the query can be written using inner join:
SELECT status,
Count(status)
FROM sample_metrics_data
JOIN (SELECT dev_id id,
Max(timestamp) ts
FROM sample_metrics_data
GROUP BY dev_id) max_ts
ON timestamp = max_ts.ts
AND dev_id = max_ts.id
GROUP BY status;
I would need to do an inner join, but it seems only LEFT JOIN is available.

In your case, if you need to build a graph of how many devices were online, then a typical solution to your problem would be
Build a cube in which there will be data on the change in the number of devices online.
Create measures with rollingWindow
For example, I made a table as in your question
And create this cube
cube(`SampleMetricsData`, {
sql: "SELECT *, device_status - COALESCE(LAG(device_status) OVER (PARTITION BY id ORDER BY timemark ASC), 0) as rolling_status FROM ab_api_test.sample_metrics ORDER BY `sample_metrics`.`timemark` DESC",
measures: {
rollingStatusTotal: {
sql: `rolling_status`,
type: `sum`,
rollingWindow: {
trailing: `unbounded`,
},
},
},
dimensions: {
id: {
sql: `id`,
type: `number`,
primaryKey: true
},
timemark: {
sql: `timemark`,
type: `time`
},
}
});
On this cube you can see online device chart with this query
{"measures":["SampleMetricsData.rollingStatusTotal"],"timeDimensions":[{"dimension":"SampleMetricsData.timemark","granularity":"hour","dateRange":"This month"}],"order":{},"dimensions":[],"filters":[]}
Possibly you should see this tutorial, It looks like something similar for your task. And one more related question is here
Note
You can also write a query like this to create a cube from your data. But this is not best practices
select * from (
SELECT DISTINCT ON (dev_id) dev_id,
timestamp,
status
FROM
sample_metrics_data
ORDER BY
dev_id,
timestamp DESC
) as sample_metrics

Related

Psql error: column "paraglider.idMaker" does not exist but it exist

Hello i got a query that throw the this error : error: column "paraglider.idMaker" does not exist
const result = await client.query('SELECT *, "maker.is" AS "maker_id", "maker.name" AS "maker_name" FROM "paraglider" JOIN "maker" ON "paraglider.idMaker"="maker.id"');
There no error of typo
This work perfect in the CLI PSQL:
SELECT paraglider.*,
maker.id AS maker_id,
maker.name AS maker_name
FROM paraglider
JOIN maker ON "idMaker"=maker.id;
I got this result:
id | name | type | release_year | created_at | idMaker | maker_id | maker_name
----+--------+----------+--------------+---------------+---------+----------+------------
1 |Tonic 2 | all | 2019 | time_stamp_ex | 1 | 1 | Skywalk
2 |Arcus RS| all | 2017 | time_stamp_ex | 2 | 2 | Swing
I don't understand why my query work in the CLI of PSQL but not in my code.
I try to change:
'ON "paraglider.idMaker"="maker.id"'
in
'ON "idMaker"="maker.id"'
and the error change in err: error: column "maker.id" does not exist.
Ok i find a query who work :
SELECT paraglider.*, maker.id AS maker_id, maker.name AS maker_name FROM paraglider JOIN maker ON "idMaker"=maker.id
But don't understand where the problem come from...

MySQL select all values which share this field's value

I am new to MySQL so describing problems in words is difficult and searching for solutions is extremely challenging.
This problem is best explained visually:
I want to select (as an array) exchange_pair_id's that share the same pair_id.
So in the above data, my MySQL query would return an object:
{ pair_id: 1, exchange_pair_id: [183, 1] }
I am aware this is a butchered question, but I do not know the words to search to solve this problem.
Updated for clarity/brevity:
+------------------+-------------+---------+
| exchange_pair_id | exchange_id | pair_id |
+------------------+-------------+---------+
| 1 | 3 | 1 |
+------------------+-------------+---------+
| 183 | 1 | 1 |
+------------------+-------------+---------+
| 69 | 2 | 2 |
+------------------+-------------+---------+
| 12 | 4 | 2 |
+------------------+-------------+---------+
| 2 | 3 | 2 |
+------------------+-------------+---------+
| 3 | 3 | 3 |
+------------------+-------------+---------+
Desired output from a Javascript MySQL select query:
[
{ pair_id: 1, exchange_pair_id: [1, 183] },
{ pair_id: 2, exchange_pair_id: [69, 12, 2] },
{ pair_id: 3, exchange_pair_id: [3] }
]
I am thinking a query like this , but I'm waiting your answer at comments.
Basically, you use GROUP BY to obtain in two different columns the values for each pair_id:
SELECT pair_id, MIN(exhange_pair_id) AS id1, MAX(exchange_pair_id) AS id2
FROM yourtable
GROUP BY pair_id;
Update version: Can you try this please on your data?
In this case MYSQL let you concat field using a separator (,)
SELECT pair_id, GROUP_CONCAT(exhange_pair_id) AS exhange_pair_id
FROM yourtable
GROUP BY pair_id
try select exchange_pair_id from yourtable where pair_id=1 then it will return array [1,183]
The SQL you are looking for would be:
SELECT * WHERE pair_id == 1
Without knowing what your specific code looks like, I can only guess as to how you are implementing a call to your database. I would assume you are doing some sort of async call to a PHP controller. So instead of using '1' in the query, you will need to use whatever variable you are pulling from your code to know what you are looking for.

finding the documents containing a value closest to int

Im having the following schema:
var lottary = new Schema({
userid : String,
start: Number,
end: Number,
time: Number,
});
and im writing a query that get the result of the winner.
if the rows are as follows:
| start | end |
| 2 | 4 |
| 5 | 99 |
| 100 | 999 |
and my number are 55, it would return the row with start 5, and end 99, cause 55 is between those numbers.
I know this are done with the following in MYSQL:
SELECT *
FROM lotto_tickets
WHERE 40 BETWEEN start AND end
But, how is this done within mongoose / mongodb?
Possible duplicate, take a look at this: How to find documents having a query-value within the range of two key-values
db.lottary.find({ "start": { "$lt": 55}, "end": { "$gt": 55}});
You can obviously change the number 55 to anything u want.

Node combined multiple rows into one using mysql or lodash

Currently, I'm using Node MySQL library to implement MySQL query in Javascript framework. I have an issue about the left join with multiple rows. When I left join that table, it returns more than one object, this is because the left join will produce duplicate rows but different values of that particular attribute. What I want to achieve right now is, returns one object and insert that multiple values to array of that particular attribute.
Table A
id | name | age
1 abel 22
2 john 22
Table B
id | user_id | equip
1 1 armor
2 2 sword
3 1 knife
4 2 gun
Query
SELECT * FROM Table_A LEFT JOIN TABLE_B ON TABLE_B.user_id = TABLE_A.id;
Current Situation
{
id: 1
name: abel
age: 22
user_id: 1
equip: 'armor'
},
{
id: 1
name: abel
age: 22
user_id: 1
equip: 'knife'
},
{
id: 2
name: john
age: 22
user_id: 2
equip: 'sword'
},
{
id: 2
name: john
age: 22
user_id: 2
equip: 'gun'
}
What I want to achieve
{
id: 1
name: abel
age: 22
user_id: 1
equip: [
'armor','knife'
]
},
{
id: 2
name: john
age: 22
user_id: 2
equip: [
'sword','gun'
]
}
Anyway to achieve using node mysql query or lodash?
What you're trying to do can't be accomplished with a join. Here's what I came up with, though SQL wizards may have better solutions:
SELECT Table_A.name, GROUP_CONCAT(item_name) AS equip
FROM Table_B
JOIN Table_A
ON Table_A.id=Table_B.person_id
WHERE Table_A.id=1;
That will produce
+------+-------------------+
| name | person_items |
+------+-------------------+
| abel | armor,sword,knife |
+------+-------------------+
Then just create an array with split(','). But I see a few problems beyond the one you're trying to solve.
Table_B is doing too much work. Drop the user_id column and create a third table, Table_A_Table_B, with three columns: id primary key, table_a_id foreign key referring to Table_A.id; table_b_id foreign key referring to Table_B.id.
Unless you change the tables this way, you'll need to enter an item name every time you add a row to Table B. Eliminate that redundancy, and use the relational database properly, by creating/inserting the item once in its own table and using a child table to point to it and an entity in Table_A whenever you need to connect them (that's Table_A_Table_B).
Names need to be more descriptive and accurate, ex. Table_A -> people, Table_B -> items, and Table_A_Table_B -> 'person_item,equip->item_name`.
From square one:
CREATE TABLE people (
id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50),
age INT UNSIGNED
);
CREATE TABLE items (
id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
item_name VARCHAR(20)
);
CREATE TABLE person_item (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
person_id INT UNSIGNED NOT NULL,
item_id INT UNSIGNED NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (person_id)
REFERENCES people(id)
ON UPDATE CASCADE ON DELETE CASCADE,
FOREIGN KEY (item_id)
REFERENCES items(id)
ON UPDATE CASCADE ON DELETE CASCADE
);
Mix in a bit of data:
INSERT INTO people (name, age) VALUES ('abel', 20), ('john', 21);
INSERT INTO items (item_name) VALUES ('armor'), ('sword'), ('knife');
INSERT INTO person_item (person_id, item_id) VALUES (1, 1), (1, 2), (1, 3), (2, 1), (2, 2), (2, 3);
MySQL doesn't support arrays, and best practices for what you want to do would, I think, be not to do it at all, but instead to do this:
SELECT p.*, i.*
FROM people p
JOIN person_item pi
ON p.id=pi.person_id
JOIN items i
ON i.id=pi.item_id
WHERE p.id=1;
which would produce:
+------+-----+-----------+
| name | age | item_name |
+------+-----+-----------+
| abel | 22 | armor |
| abel | 22 | sword |
| abel | 22 | knife |
+------+-----+-----------+
which you could then use Node to get the data you want. Or you can do this:
SET sql_mode='';
SELECT people.name, GROUP_CONCAT(item_name) AS person_items
FROM items
JOIN person_item
ON person_item.item_id=items.id
JOIN people
ON people.id=person_item.person_id
WHERE people.id=1;
Producing:
+------+-------------------+
| name | person_items |
+------+-------------------+
| abel | armor,sword,knife |
+------+-------------------+

Creating a nested JSON object from SQL results

I'm trying to figure out how to create a nested JSON object, something like this:
company: "Company 1",
pricing: {
term: "1 year",
price: "$4.95",
term: "2 years",
price: "3.95"
},
I have two tables in MySQL, one called plans which is structured in this fashion
| id | company |
------------------
| 2 | company 1 |
and another table plans_pricing to represent the pricing data
| id | plans_id | term | price |
--------------------------------
| 1 | 2 | 1 year | $4.95 |
| 2 | 2 | 2 years| $3.95 |
I am using Laravel 4 to query the database and create json to send back to my ajax request. Here is the query, which is currently sending a server 500 error.
public function results()
{
$answers = $_POST['answers'];
$data = DB::table('plans')
->join('plans_pricing', 'plans.id', '=', 'plans_pricing.plans_id')
->select('plans.company', 'plans_pricing.price', 'plans_pricing.term')
->whereIn('plans.id', $answers)
->get();
echo json_encode($data);
}
I'm not sure why this query isn't working, but that isn't even why i'm asking this question. I need to know how to get a nested JSON object, when I create the join, I believe that I'll receive a separate object for each, like here:
| company | price | term |
------------------------------------
| company 1 | 4.95 | 1 year |
| company 1 | 3.95 | 2 years|
How can I make this SQL query return a nested JSON object like the one I describe above? I have been stuck on this problem for two days now and could really use some guidance. Thanks
UPDATE:
The server 500 error was fixed by changing echo json_encode to return Response::json($data);
I've never used Laravel but I think this should work:
$output = array();
$currentCompany = "";
foreach ($data as $datum) {
if ($datum->company != $currentCompany) {
$output[] = array();
// get a reference to the newly added array element
end($output);
$currentItem = & $output[key($output)];
$currentCompany = $datum->company;
$currentItem['company'] = $currentCompany;
$currentItem['rates'] = array();
}
$currentItem['rates'][] = array("price" => $datum->price, "term" => $datum->term);
}
json_encoded result:
[{
"company":"company 1",
"rates":[{
"price":4.95,"term":"1 year"
},{
"price":3.95,"term":"2 years"
}]
}]

Categories

Resources