Node combined multiple rows into one using mysql or lodash - javascript

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 |
+------+-------------------+

Related

Implementing DISTINCT ON in CubeJS

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

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.

Mysql select row containing string + specific int column comparison

I have found numerous questions related to mine but still can't solve this issue.
In my table there are 3 columns filled with integer values and 3 columns with string values. I have several rows.
Table structure example:
INT_1 | INT_2 | INT_3 | VALUE1 | VALUE2 | VALUE3
33 | 25 | 10 | "nice"| "hello"| "goodbye"
---------------------------------------------------
10 | 15 | 28 | "dice"| "hay" | "bird"
I have a string that I use to select the rows based on the VALUE columns. The way I want to select it is with inclusion which means if the string is "llo" I should get the row where at least one of the values (VALUE,VALUE2,VALUE3) contains "llo" (would select the row with "hello" in VALUE2, for example).
However if two different rows have VALUE columns that contain the string (like in the example if the string is "ice") I want to retrieve the row where the INT column associated to that VALUE is higher. In the example since the string was compared to VALUE1 I should compare INT_1 of the upper column with INT_1 of the lower column and retrieve the row where INT_1 is higher. (INT_1 -> VALUE1, INT_2 -> VALUE2, INT_3 -> VALUE3).
Well not much but I could figure this myself:
SELECT * FROM my_table WHERE VALUE1 = "+string+" OR VALUE2= "+string+" OR VALUE3= "+string+"";
I am not sure how should I include "LIKE" to check for containing string when I have the values like "+string+".
I don't know how to compare the specific INT column with the specific VALUE column when I have more than one row where VALUE contains the string.
First normalize your table using UNION ALL. That means every row have to be splitted into three. One for each group (INT_1 VALUE1, INT_2 VALUE2, INT_3 VALUE3). Since you don't have an explicit primary key, you need to include all columns to identify the source row.
select t.*, 1 as position, INT_1 as i, VALUE1 as v from my_table t
union all
select t.*, 2 as position, INT_2 as i, VALUE2 as v from my_table t
union all
select t.*, 3 as position, INT_3 as i, VALUE3 as v from my_table t
Result:
| INT_1 | INT_2 | INT_3 | VALUE1 | VALUE2 | VALUE3 | position | i | v |
|-------|-------|-------|--------|--------|---------|----------|----|---------|
| 33 | 25 | 10 | nice | hello | goodbye | 1 | 33 | nice |
| 10 | 15 | 28 | dice | hay | bird | 1 | 10 | dice |
| 33 | 25 | 10 | nice | hello | goodbye | 2 | 25 | hello |
| 10 | 15 | 28 | dice | hay | bird | 2 | 15 | hay |
| 33 | 25 | 10 | nice | hello | goodbye | 3 | 10 | goodbye |
| 10 | 15 | 28 | dice | hay | bird | 3 | 28 | bird |
http://sqlfiddle.com/#!9/9086d5/1
Now put it in a subquery and search for your string in the v column using WHERE v LIKE '%ice%'.
select *
from (
select t.*, 1 as position, INT_1 as i, VALUE1 as v from my_table t
union all
select t.*, 2 as position, INT_2 as i, VALUE2 as v from my_table t
union all
select t.*, 3 as position, INT_3 as i, VALUE3 as v from my_table t
) n
where v like '%ice%'
Result:
| INT_1 | INT_2 | INT_3 | VALUE1 | VALUE2 | VALUE3 | position | i | v |
|-------|-------|-------|--------|--------|---------|----------|----|------|
| 33 | 25 | 10 | nice | hello | goodbye | 1 | 33 | nice |
| 10 | 15 | 28 | dice | hay | bird | 1 | 10 | dice |
http://sqlfiddle.com/#!9/9086d5/4
Last step - Pick the row with the highest value in i using ORDER BY i DESC LIMIT 1:
select `INT_1`, `INT_2`, `INT_3`, `VALUE1`, `VALUE2`, `VALUE3`
from (
select t.*, 1 as position, INT_1 as i, VALUE1 as v from my_table t
union all
select t.*, 2 as position, INT_2 as i, VALUE2 as v from my_table t
union all
select t.*, 3 as position, INT_3 as i, VALUE3 as v from my_table t
) n
where v like '%ice%'
order by i desc
limit 1
Result:
| INT_1 | INT_2 | INT_3 | VALUE1 | VALUE2 | VALUE3 |
|-------|-------|-------|--------|--------|---------|
| 33 | 25 | 10 | nice | hello | goodbye |
http://sqlfiddle.com/#!9/9086d5/5
The query can be shorter if you use a HAVING clause instead of WHERE, so you don't need to use a subquery. But then you get two columns (i and v), that you might not need. On the other hand, they might be the only columns you need.
select t.*, INT_1 as i, VALUE1 as v from my_table t union all
select t.*, INT_2 as i, VALUE2 as v from my_table t union all
select t.*, INT_3 as i, VALUE3 as v from my_table t
having v like '%ice%'
order by i desc
limit 1
And one more modification which might improve the performance a little bit:
select t.*, INT_1 as i from my_table t where VALUE1 like '%ice%' union all
select t.*, INT_2 as i from my_table t where VALUE2 like '%ice%' union all
select t.*, INT_3 as i from my_table t where VALUE3 like '%ice%'
order by i desc
limit 1
This is a horrible data structure. But here is one way to do this?
SELECT t.*
FROM my_table t
WHERE VALUE1 LIKE '%string%' OR VALUE2 LIKE '%string%' OR VALUE3 LIKE '%string%'
ORDER BY greatest( (case when VALUE1 LIKE '%string%' then int_1 else -1 end),
(case when VALUE1 LIKE '%string%' then int_2 else -1 end),
(case when VALUE1 LIKE '%string%' then int_3 else -1 end) ) desc
LIMIT 1;

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