TypeScript Sequelize : How to join two tables with common - sql

There are three tables.
Tables :
Trip
id | start_destination_id | end_destination_id | arrive_time |
-------------------------------------------------------------------
1 | S | E | 09:00 |
Destination
id | name
---------
S | Start
E | End
Schedule
id | start_destination_id | end_destination_id | should_arrive |
-------------------------------------------------------------------
1 | S | E | 08:00 |
2 | A | E | 10:00 |
Query
SELECT
Trip.*,
Schedule.should_arrive
FROM
Trip
LEFT JOIN
Schedule
ON
Trip.start_destination_id = Schedule.start_destination_id
AND
Trip.end_destination_id = Schedule.end_destination_id
I am trying to include Schedule in Trip.findAll but receive error
Exception: SequelizeEagerLoadingError: Schedule is not associated to Trip!
Is there a way that I can join them together without using foreign keys and raw queries?
Many thanks.

Finally I found a solution (not sure if it is a hack).
Schedule.ts
// add these lines
...
#ForeignKey(() => Trip)
#Column({ type: DataType.VIRTUAL })
private _dummyForTrip: undefined;
...
Then create an association between Schedule and Trip.
Trip.ts
#HasMany(() => Schedule)
public schedules: Schedule[] | null
Then you can include Schedule inside Trip by using include.on
const trips = await Trip.findAll({
include: [{
model: Schedule,
on: {
'$schedules.start$': { [Op.col]: "Trip.start_destination" },
'$schedules.end$': { [Op.col]: "Trip.end_destination" },
}
}],
where: {
id: { [Op.in]: payload.inputTripIdArr }
}
});

Related

How to get what percentage of total users joined what number of meetings in PostgreSQL?

There are two tables, one is called "user_preference" that contains all users:
id | firstname | lastname | email |
And "match" which combines users with meetups they joined:
id | matcher | partner | meetup |
Both matcher and partner are foreign keys that represent user_preference.id, meaning that same user can be both matcher and a partner in the same meetup.
What I need to know is what percentage of total unique users joined what number of meetings.
For example:
17% of users joined 5 meetups
20% of users joined 3 meetups
40% of users joined 1 meetup
23% of users joined 0 meetups
The number of meetups should not be hardcoded but dynamic.
But I want to avoid duplication of users for a single meetup and count them only once. For example this:
id | matcher | partner | meetup |
1 | user1 | user2 | meetup1 |
2 | user1 | user3 | meetup1 |
3 | user5 | user1 | meetup1 |
4 | user6 | user1 | meetup2 |
Should count that user1 visited only 2 meetups.
What I managed to do so far is to display the count of meetups each user visited but that is not what I need:
SELECT distinct up.email users, COUNT(m.user) meetups
FROM user_preference up
LEFT JOIN
(
SELECT matcher AS user FROM match
UNION ALL
SELECT partner AS user FROM match
) m ON m.user = up.id
GROUP BY up.email
ORDER BY meetups desc;
In the end I did this by making simple queries and looping through them in the code, its far from elegant solution but it should work.
If someone posts SQL solution I will accept and upvote it...
export const getDevStats = async () => {
const users = await getRepository(UserPreference).query(
`SELECT * FROM user_preference;`
);
const meetups = await getRepository(Meetup).query(
`SELECT * FROM meetup;`
);
const matches = await getRepository(Match).query(
`SELECT * FROM match;`
);
let userMatches: any = {};
users.forEach((user: any) => {
userMatches[user.id] = []
matches.forEach((match: any) => {
if(user.id == match.matcher || user.id == match.partner) {
if(userMatches[user.id].indexOf(match.meetup) === -1) {
userMatches[user.id].push(match.meetup);
}
}
});
});
let matchStats: any = {};
for (var userId of Object.keys(userMatches)) {
if (typeof matchStats[userMatches[userId].length] === 'undefined') {
matchStats[userMatches[userId].length] = 0;
}
matchStats[userMatches[userId].length]++;
}
return {
users: users,
meetups: meetups,
matches: matches,
userMatches: userMatches,
matchStats: matchStats
};
};

How can I extract a json column into new columns automatically in Snowflake SQL?

This is as example taken from another thread, but essentially I would like to achieve this:
Sample data
ID Name Value
1 TV1 {"URL": "www.url.com", "Icon": "some_icon"}
2 TV2 {"URL": "www.url.com", "Icon": "some_icon", "Facebook": "Facebook_URL"}
3 TV3 {"URL": "www.url.com", "Icon": "some_icon", "Twitter": "Twitter_URL"}
..........
Expected output
ID Name URL Icon Facebook Twitter
1 TV1 www.url.com some_icon NULL NULL
2 TV2 www.url.com some_icon Facebook_URL NULL
3 TV3 www.url.com some_icon NULL Twitter_URL
I'm totally new to Snowflake so I'm shaking my head on how to do this easily (and hopefully automatically, in the case where some rows might have more elements in the json than other rows, which would be tedious to assign manually). Some lines might have sub-categories too.
I found the parse_json function for Snowflake, but it's only giving me the same json column in a new column, still in json format.
TIA!
You can create a view over your table with the following SELECT:
SELECT ID,
Name,
Value:URL::varchar as URL,
Value:Icon::varchar as Icon,
Value:Facebook::varchar as Facebook,
Value:Twitter::varchar as Twitter
FROM tablename;
Additional attributes will be ignored unless you add them to the view. There is no way to "automatically" include them into the view, but you could create a stored procedure that dynamically generates the view based on all the attributes that are in the full variant content of a table.
You can create a SP to automatically build the CREATE VIEW for you based on the JSON data in the VARIANT.
I have some simple example below:
-- prepare the table and data
create or replace table test (
col1 int, col2 string,
data1 variant, data2 variant
);
insert into test select 1,2, parse_json(
'{"URL": "test", "Icon": "test1", "Facebook": "http://www.facebook.com"}'
), parse_json(
'{"k1": "test", "k2": "test1", "k3": "http://www.facebook.com"}'
);
insert into test select 3,4,parse_json(
'{"URL": "test", "Icon": "test1", "Twitter": "http://www.twitter.com"}'
), parse_json(
'{"k4": "v4", "k3": "http://www.ericlin.me"}'
);
-- create the SP, we need to know which table and
-- column has the variant data
create or replace procedure create_view(
table_name varchar
)
returns string
language javascript
as
$$
var final_columns = [];
// first, find out the columns
var query = `SHOW COLUMNS IN TABLE ${TABLE_NAME}`;
var stmt = snowflake.createStatement({sqlText: query});
var result = stmt.execute();
var variant_columns = [];
while (result.next()) {
var col_name = result.getColumnValue(3);
var data_type = JSON.parse(result.getColumnValue(4));
// just use it if it is not a VARIANT type
// if it is variant type, we need to remember this column
// and then run query against it later
if (data_type["type"] != "VARIANT") {
final_columns.push(col_name);
} else {
variant_columns.push(col_name);
}
}
var columns = {};
query = `SELECT ` + variant_columns.join(', ') + ` FROM ${TABLE_NAME}`;
stmt = snowflake.createStatement({sqlText: query});
result = stmt.execute();
while (result.next()) {
for(i=1; i<=variant_columns.length; i++) {
var sub_result = result.getColumnValue(i);
if(!sub_result) {
continue;
}
var keys = Object.keys(sub_result);
for(j=0; j<keys.length; j++) {
columns[variant_columns[i-1] + ":" + keys[j]] = keys[j];
}
}
}
for(path in columns) {
final_columns.push(path + "::STRING AS " + columns[path]);
}
var create_view_sql = "CREATE OR REPLACE VIEW " +
TABLE_NAME + "_VIEW\n" +
"AS SELECT " + "\n" +
" " + final_columns.join(",\n ") + "\n" +
"FROM " + TABLE_NAME + ";";
snowflake.execute({sqlText: create_view_sql});
return create_view_sql + "\n\nVIEW created successfully.";
$$;
Execute the SP will return below string:
call create_view('TEST');
+---------------------------------------+
| CREATE_VIEW |
|---------------------------------------|
| CREATE OR REPLACE VIEW TEST_VIEW |
| AS SELECT |
| COL1, |
| COL2, |
| DATA1:Facebook::STRING AS Facebook, |
| DATA1:Icon::STRING AS Icon, |
| DATA1:URL::STRING AS URL, |
| DATA2:k1::STRING AS k1, |
| DATA2:k2::STRING AS k2, |
| DATA2:k3::STRING AS k3, |
| DATA1:Twitter::STRING AS Twitter, |
| DATA2:k4::STRING AS k4 |
| FROM TEST; |
| |
| VIEW created successfully. |
+---------------------------------------+
Then query the VIEW:
SELECT * FROM TEST_VIEW;
+------+------+-------------------------+-------+------+------+-------+-------------------------+------------------------+------+
| COL1 | COL2 | FACEBOOK | ICON | URL | K1 | K2 | K3 | TWITTER | K4 |
|------+------+-------------------------+-------+------+------+-------+-------------------------+------------------------+------|
| 1 | 2 | http://www.facebook.com | test1 | test | test | test1 | http://www.facebook.com | NULL | NULL |
| 3 | 4 | NULL | test1 | test | NULL | NULL | http://www.ericlin.me | http://www.twitter.com | v4 |
+------+------+-------------------------+-------+------+------+-------+-------------------------+------------------------+------+
Query the source table:
SELECT * FROM TEST;
+------+------+------------------------------------------+-----------------------------------+
| COL1 | COL2 | DATA1 | DATA2 |
|------+------+------------------------------------------+-----------------------------------|
| 1 | 2 | { | { |
| | | "Facebook": "http://www.facebook.com", | "k1": "test", |
| | | "Icon": "test1", | "k2": "test1", |
| | | "URL": "test" | "k3": "http://www.facebook.com" |
| | | } | } |
| 3 | 4 | { | { |
| | | "Icon": "test1", | "k3": "http://www.ericlin.me", |
| | | "Twitter": "http://www.twitter.com", | "k4": "v4" |
| | | "URL": "test" | } |
| | | } | |
+------+------+------------------------------------------+-----------------------------------+
You can refine this SP to detect nested data and have them added to the columns list as well.

Linq-like group by for sql

It seems like sql group by is more of aggregate functions (COUNT, MAX, MIN, SUM, AVG).
select count(Id), Country
from Customer
where Country <> 'CountryX'
group by Country
But do we have a linq-like query where we want to return all results grouped by a certain column, E.g. in linq I would do
id | title | category | email
------------------------------------------
1 | tname-1 | cat1 | test#example.com
2 | tname-2 | cat1 | test1#example.com
3 | tname-3 | cat2 | TEst#example.com
linq group-by:
var groupedBy = list.GroupBy(item => item.Email);
or even throw in some comparison
var groupedBy = list.GroupBy(item => item.Email, StringComparer.OrdinalIgnoreCase);
and a result will be something like:
key | items
----------------------------------------------------------------------------------------------
test#example.com | [{Id :1, Title : "tname-1", category: "cat1", email: "test#example.com" },{Id :3, Title : "tname-3", category: "cat2", email: "TEst#example.com" } ]
test1#example.com| [{Id :2, Title : "tname-2", category: "cat1", email: "test1#example.com" }]
but with sql I would definitely want to return only the subset of the columns, say id, title and email.

Simple relation in YII, can't get it done

This is the first time I use relations in Yii, the question is very simple.
MODULE TABLE
name - PK
status - FK status_id
STATUS TABLE
id PK
name
So, each Module HAS one status.
But I can not seem to get it working.
Module.php (Model)
public function relations()
{
return array(
'status'=>array(self::BELONGS_TO, 'ModuleStatus', 'status'),
);
}
I access them this way:
$modulesAR = Module::model()->with('status')->findAll();
if( $modulesAR )
{
foreach( $modulesAR as $moduleAR )
{
$this->modules[ $moduleAR->name ] = array(
'sessionLimit' => isset($moduleAR->sessionLimit) ? $moduleAR->sessionLimit : 0,
'status' => isset($moduleAR->status) ? $moduleAR->status : 'disabled',
);
}
}
Var_dump(Yii::app()->module->modules;
array(3) {
["digidoc"]=>
array(2) {
["sessionLimit"]=>
int(0)
["status"]=>
string(1) "2" // Should say "Disabled"
}
["docusearch"]=>
array(2) {
["sessionLimit"]=>
int(0)
["status"]=>
string(1) "1" // Should say "Enabled"
}
["printbox"]=>
array(2) {
["sessionLimit"]=>
int(0)
["status"]=>
string(1) "2" // Should say "Disabled"
}
}
I will appreciate any help.
Thanks!
EDIT:
query executed by Yii:
SELECT `t`.`name` AS `t0_c0`, `t`.`status_id` AS `t0_c1`, `t`.`session_limit` AS `t0_c2`, `status`.`id` AS `t1_c0`, `status`.`name` AS `t1_c1` FROM `ss_module` `t` LEFT OUTER JOIN `ss_module_status` `status` ON (`status`.`id`=`t`.`name`)
+------------+-------+-------+-------+-------+
| t0_c0 | t0_c1 | t0_c2 | t1_c0 | t1_c1 |
+------------+-------+-------+-------+-------+
| digidoc | 2 | 0 | NULL | NULL |
| docusearch | 1 | 2 | NULL | NULL |
| printbox | 2 | 0 | NULL | NULL |
+------------+-------+-------+-------+-------+
EDIT 2:
Changing the last
ON (`status`.`id`=`t`.`name`);
to
ON (`status`.`id`=`t`.`status_id`);
works as expected, by I dont know how to fix it in Yii.
When you peek into $moduleAR->status, you get the whole Status model instance, not it's id. So just go on and peek into that instance:
$moduleAR->status->name.
That's what ActiveRecord magic is intended for ;)
And inversely, if you have Status model, you should have a relation there saying something like this:
'modules' => array(self::HAS_MANY, 'Module', 'status_id')
Which could make the following possible:
$status = Status::model()->find(/*somehow*/);
/* now that $status is an Status instance */
foreach ($status->modules as $module) {
// here you are! looping over all modules connected to this status
// each $module is a full-fledged Module instance
}

yii - create nested cgridview

Is it possible to create nested table using cgridview?
I'd like to have the final output as follows
| Transaction | Total |
| T-001 | $100 |
| Item | Price | // here is the nested table
| I-1 | $50 |
| I-2 | $50 |
| T-002 | $90 |
| Item | Price | // here is the nested table
| I-3 | $90 |
I know you can do this using a custom template, but i'd like a neater solution using a widget like CGRidView.
Thanks
If nested table is inside cell then you can, just create function in your model that will render table and return content. You can set third parameter of widget function to true to return content. If you want to enable pagination for nested table then be sure to manually set widget id and allow ajax update.
In model:
function getNestedTable() {
return Yii::app()->controller->widget(..., ..., true);
}
In columns definition use:
'columns' => array(
array(
'name' => 'nestedTable',
'type' => 'raw'
)
)
I think the best way to achieve what you want is to use custom functions in your CActiveRecord model (if you've a CActiveDataprovider for the grid), and put that 'nested table' as normal column:
| Transaction | Item | Price | Total |
------------------------------------------
| T-001 | I-1 | $50 | $100 |
| I-2 | $50 |
------------------------------------------
| T-002 | I-3 | $90 | $90 |
------------------------------------------
In your model you've to define get functions that return data in HTML with line breaks (for example with a br:
class Item extends CActiveRecord {
...
public function getIdItems()
{
$string = '';
foreach($this->items as $item) {
if ($string != '') $string .= '<br/>';
$string .= ' '.$item->textId; // 'I-3', 'I-2'...
}
return $string;
}
public function getPriceItems()
{
$string = '';
foreach($this->items as $item) {
if ($string != '') $string .= '<br/>';
$string .= ' '.$item->price; // $50, $90...
}
return $string;
}
...
}
And to show the new columns in your grid:
$this->widget('zii.widgets.grid.CGridView', array(
'id'=>'anotacionesGrid',
'dataProvider'=>$dataProvider,
'columns'=>array(
'transaction',
'idItems:html:Item',
'priceItems:html:Price',
'total'
)
);