Convert Sequelize Query to BigQuery Query - sql

Hello I need to convert this Sequelize Query to bigQuery sql Query syntax.
Comment.findAll({ where: {artifact_id: id, parent_id: null},
include: [
{ model: db.User, attributes: ['id','first_name', 'last_name'] },
{ model: db.artifact, attributes: ['id','original_artifact_name'] },
{ model: db.status, attributes: ['id','status_name'] },
{ model: Comment, as: 'replies', include: [{ model: db.User, attributes: ['id','first_name', 'last_name'] }] }
]
})
Can any one help with this ?
When I do left join of any table like this
SELECT a.*, b.group_name as parent_grp_name, c.org_name as parent_org_name
FROM ${DB}.groups a LEFT JOIN ${DB}.groups b ON b.id = a.parent_group_id
LEFT JOIN ${DB}.organizations c ON c.id = a.organization_id
ORDER BY a.created_at
LIMIT ${limit}
OFFSET ${offset}`
it gives me record as a single object with both tables data in it. I want to get joined table data as seperate object.

Related

Apply where clause on associated table leads to sequelize bad field error

I have 3 tables sale,company and saleItem with the following relations:
Sale.belongsTo(Company);
Company.hasMany(Sale);
Sale.hasMany(SaleItem, { as: "items" });
SaleItem.belongsTo(Sale);
I want to apply a filter on the company's name I saw that to do that we have to use $ at the start and end but it isnt working. Any ideas where I am going wrong?
When I try to execute the below code I get the error:
SqlError: (conn=201, no: 1054, SQLState: 42S22) Unknown column 'company.name' in 'where clause'
sql: SELECT `sale`.*, `company`.`id` AS `company.id`, `company`.`name` AS `company.name`, `items`.`id` AS `items.id`, `items`.`quantity` AS `items.quantity`, `items`.`price` AS `items.price`, `items`.`description` AS `items.description`, `items`.`margin` AS `items.margin`, `items`.`gst` AS `items.gst`, `items`.`createdAt` AS `items.createdAt`, `items`.`updatedAt` AS `items.updatedAt`, `items`.`saleId` AS `items.saleId`, `items`.`itemId` AS `items.itemId` FROM (SELECT `sale`.`id`, `sale`.`date`, `sale`.`type`, `sale`.`description`, `sale`.`poNumber`, `sale`.`poDate`, `sale`.`paymentType`, `sale`.`gst`, `sale`.`discount`, `sale`.`freight`, `sale`.`status`, `sale`.`saleStatus`, `sale`.`referenceNumber`, `sale`.`ftn`, `sale`.`quotationNumber`, `sale`.`showGST`, `sale`.`invoiceDate`, `sale`.`hasWithholdingTax`, `sale`.`serialNumber`, `sale`.`currency`, `sale`.`createdAt`, `sale`.`updatedAt`, `sale`.`companyId`, `sale`.`customerId` FROM `sale` AS `sale` WHERE `company`.`name` LIKE '%%' AND `sale`.`customerId` = 1 AND `sale`.`status` = 'ACTIVE' ORDER BY `id` DESC LIMIT 0, 15) AS `sale` LEFT OUTER JOIN `company` AS `company` ON `sale`.`companyId` = `company`.`id` LEFT OUTER JOIN `saleItem` AS `items` ON `sale`.`id` = `items`.`saleId` ORDER BY `id` DESC;
It works if I dont include the SaleItem table in query
Here is the code
await Sale.findAndCountAll({
include: [
{
model: Company,
attributes: ["name"],
as: "company",
},
{ model: SaleItem, as: "items" },
],
distinct: true,
where: {
"$company.name$": { [Op.like]: `%${search}%` },
customerId:1,
status: "ACTIVE",
},
})
If you see the generated SQL, company.name WHERE clause is incorrectly added to a subquery, so you can either turn off the subquery or you can add your where option within the include.
Option 1:
await Sale.findAndCountAll({
...,
subQuery: false
})
Option 2:
await Sale.findAndCountAll({
include: [
{
model: Company,
attributes: ["name"],
as: "company",
where: {
name: { [Op.like]: `%${search}%` }
}
},
{ model: SaleItem, as: "items" },
],
distinct: true,
where: {
customerId:1,
status: "ACTIVE",
},
})

Add data from many-to-many to selection data

I have 3 tables:
"tags"
id serial
name text
"questions"
id serial
content jsonb
"questions_tags" (many-to-many)
question_id integer
tag_id integer
I can select all data from questions:
SELECT * FROM questions;
I can select tags of specific question:
SELECT t.*
FROM tags t
JOIN questions_tags qt ON t.id = qt.tag_id
JOIN questions q ON qt.question_id = q.id
WHERE q.id = 157;
I need to select all questions and add to this selection tags for every question.
I'm using nodejs and node-postgres and I need to get something like:
[
{
id: 1,
content: { someProp: 'someValue' },
tags: [
{ id: 58, name: 'sometag58' },
{ id: 216, name: 'sometag216' }
]
},
...
]
How can I do this without ORM, only with the help of vanilla SQL?
I would let PostgreSQL handle it for me:
with tagjson as (
select q.id, q.content, jsonb_agg(to_jsonb(tags)) as tags
from questions q
left join questions_tags qt on qt.question_id = q.id
left join tags on tags.id = qt.tag_id
group by q.id, q.content
)
select jsonb_pretty(jsonb_agg(to_jsonb(tagjson))) as result
from tagjson;
Result:
[
{
"id": 1,
"tags": [
{
"id": 58,
"name": "sometag58"
},
{
"id": 216,
"name": "sometag216"
}
],
"content": "some question 1 content"
}
]
db<>fiddle here
EDIT TO ADD:
Based on a conversation in the comments, the final query should be modified to:
select * from tagjson;
Or the CTE could be promoted to be a stand-alone query.
This allowed the conventional node syntax while preserving the aggregated tags array under the tags key:
const questions: Question[] = result.rows;

SQL query. Join right table as nested object inside left table

After reading through documentation I can't find anything about how to make this simple query:
there is two tables:
first one is "PACKAGES"
{
id: 1,
name: 'package_1',
sender_id: 1,
type: 'shipping'
}
second one is "USERS"
{
id: 1,
name: 'user_1'
}
When I perform Left, Right Join, or simply JOIN, its just merges this two tables into one. And id, name just overwrites with each other
What I need is:
{
id: 1,
name: 'package_1',
sender_id: 1
type: 'shipping',
user: { // second table as nested object
id: 1,
name: 'user_1'
}
}
I tried everything that I can find, for example:
SELECT * FROM packages JOIN users AS user ON packages.sender_id=user.id;"
How I can put right table as an object inside left table?
Use to_json to present the query result as a nested JSON object:
select to_json(res) from
(
select p.*, to_json(u) "user"
from packages p
inner join users u
on p.sender_id = u.id
) res;
Result:
{
"id": 1,
"name": "package_1",
"sender_id": 1,
"type": "shipping",
"user": {
"id": 1,
"name": "user_1"
}
}

How to implement subqueries in cube.js

I'm struggling to see how I would represent the following type of postgres SQL query in a cube.js schema:
SELECT
CASE
WHEN COUNT(tpp.net_total_amount) > 0 THEN
SUM(tpp.net_total_amount) / COUNT(tpp.net_total_amount)
ELSE
NULL
END AS average_spend_per_customer
FROM
(
SELECT
SUM(ts.total_amount) AS net_total_amount
FROM
postgres.transactions AS ts
WHERE
ts.transaction_date >= '2020-11-01' AND
ts.transaction_date < '2020-12-01'
GROUP BY
ts.customer_id,
ts.event_id
) AS tpp
;
I had the feeling that pre-aggregations might be what I'm after, but that doesn't seem to be the case after looking into them. I can get a list of total amount spent per customer per event with the following schema:
cube(`TransactionTotalAmountByCustomerAndEvent`, {
sql: `SELECT * FROM postgres.transactions`,
joins: {
},
measures: {
sum: {
sql: `SUM(total_amount)`,
type: `number`
}
},
dimensions: {
eventId: {
sql: `event_id`,
type: `string`
},
customerId: {
sql: `customer_id`,
type: `string`
},
transactionDate: {
sql: `transaction_date`,
type: `time`
}
},
preAggregations: {
customerAndEvent: {
type: `rollup`,
measureReferences: [sum],
dimensionReferences: [customerId, eventId]
}
}
});
But that is really just giving me the output of the inner SELECT statement grouped by customer and event. How do I query the cube to get the average customer spend per event figure I'm after?
You might find it easier to model the dataset as two different cubes, Customers and Transactions. You'll then need to set up a join between the cubes and then create a special dimension with the subQuery property set to true. I've included an example below to help you understand:
cube('Transactions', {
sql: `SELECT * FROM postgres.transactions`,
measures: {
spend: {
sql: `total_amount`,
type: `number`,
},
},
dimensions: {
eventId: {
sql: `event_id`,
type: `string`
},
customerId: {
sql: `customer_id`,
type: `string`
},
transactionDate: {
sql: `transaction_date`,
type: `time`
},
},
})
cube('Customers', {
sql: `SELECT customer_id FROM postgres.transactions`,
joins: {
Transactions: {
relationship: `hasMany`,
sql: `${Customers}.id = ${Transactions}.customerId`
}
},
measures: {
averageSpend: {
sql: `${spendAmount}`,
type: `avg`,
},
},
dimensions: {
id: {
sql: `customer_id`,
type: `string`
},
spendAmount: {
sql: `${Transactions.spend}`,
type: `number`,
subQuery: true
},
}
})
You can find more information on the Subquery page on the documentation

How to Make a Lookup connection between two Collection

Goal:
This sql and its result should be the same result from mongoDB's query code.
In order words, same result but for mongoDB.
Problem:
How to you make a lookup connection in relation to People and Role in Mongo DB's query code?
Info:
I'm new in mongo DB
SQL code
SELECT
a.*,
'.' AS '.',
b.*,
'.' AS '.',
c.*
FROM
[db1].[dbo].[People_Course_Grade] a
INNER JOIN [db1].[dbo].[People] b on a.PeopleId = b.PeopleId
INNER JOIN [db1].[dbo].[Role] c on b.RoleId = c.RoleId
Json data:
Role:
[{"RoleId":1,"Name":"Student"},{"RoleId":2,"Name":"Teacher"}]
People_Course_Grade:
[{"People_Course_GradeId":1,"PeopleId":1,"CourseId":1},
{"People_Course_GradeId":2,"PeopleId":2,"CourseId":1},
{"People_Course_GradeId":3,"PeopleId":3,"CourseId":2},
{"People_Course_GradeId":4,"PeopleId":1,"CourseId":2}]
Course:
[{"CourseId":1,"Name":"Java"},{"CourseId":2,"Name":"Java II"},
{"CourseId":3,"Name":"Statistik 1"}]
db.People_Course_Grade.aggregate([
{
$lookup:{
from: "People",
localField: "people_id",
foreignField: "_id",
as: "people"
}
},
{ $unwind:"$people" },
{
$project:{
course_id : 1,
people_id : 1,
// grade_id : 1,
Name : "$people.Name",
}
}
]);
You need to start with double $lookup since you have three collections. Then you can use $arrayElemAt to always get single element from lookup's result. To flatten your structure you can use $replaceRoot with $mergeObjects (promotes all the fields from people and course to root level.
db.People_Course_Grade.aggregate([
{
$lookup:{
from: "Role",
localField: "PeopleId",
foreignField: "RoleId",
as: "people"
}
},
{
$lookup:{
from: "Course",
localField: "CourseId",
foreignField: "CourseId",
as: "course"
}
},
{
$replaceRoot: {
newRoot: {
$mergeObjects: [
"$$ROOT",
{ $arrayElemAt: [ "$people", 0 ] },
{ $arrayElemAt: [ "$course", 0 ] },
]
}
}
},
{
$project: {
people: 0,
course: 0
}
}
])
Mongo Playground
$arrayElemAt can always be replaced with $unwind like you tried. You also have a naming conflict on name field so probably you need to run $project to rename one of those fields - otherwise you'll get only one of them in final result.