How to write nested where condition with left join using npm Sequqlize - npm

I am trying to implement left join query by clubbing
select product.* from products
left outer join cart as c on c.prodId = product.prodId
left outer join order as order on c.cid = p.pid
where product.secondaryId = 10 and c.cpid = 210;
How can I write, Sequelize code for this.
EDIT:
Here is the actual MYSQL query, what I am implementing to:
select products.* from products
left outer join Cart as cart on cart.CartId = products.CartId
left outer join Orders as order on order.OrderId = cart.PartId
where cart.CartId = 1 and order.CustId = 12;
Association at Sequelize end
Cart.hasMany(models.Products, {as: 'Products', foreignKey: 'CartId'})
Cart.belongsTo(models.Orders, { as: 'Orders', foreignKey: 'PartId'})
Products.belongsTo(models.Cart, {onDelete: "CASCADE", foreignKey: { name: 'CartId', allowNull: false})
Products -> PrimaryKey: ProduceId
Cart -> PrimaryKey: CartId
Orders -> PrimaryKey: OrderId

First of all left outer join cart as c on c.prodId = product.prodId will work as a usual join because of c.cpid = 210 condition. Either remove it or move it to on clause like this:
left outer join cart as c on c.prodId = product.prodId and c.cpid = 210
As of Sequelize query because you didn't show your model definitions and associations I'll try to guess and write like this (so you can get the whole idea):
const products = await db.Products.findAll({
where: {
secondaryId: 10
},
include: [{
model: db.Cart,
required: false, // this is LEFT OUTER JOIN
where: {
cpid: 210
},
include: [{
model: db.Order,
as: 'Orders',
required: false // this is LEFT OUTER JOIN
}]
}]
})
If you indicate your model definitions and associations then I can correct my answer as well (if necessary).

Related

Postgres query - return flattened JSON

I have the following working query:
const getPromos = async (limit = 10, site: string, branch: string) => {
const query = `SELECT
json_build_object(
'id', p.id,
'description', p.description,
'discounted_price', p.discounted_price,
'items', jsonb_agg((i.id, i.price, i.title))
)
FROM promotions p
INNER JOIN promotion_items pi ON p.id = pi.promotion_id
INNER JOIN items i ON pi.item_code = i.item_code WHERE site_id = ${site} and store_id = ${branch}
GROUP BY p.id LIMIT ${limit}`;
return await db.query(query);
};
The issue is simple - each item (in this example - promotion) is returned with an object that wraps it - named json_build_object. I don't want any object to wrap my promotions - just like this:
[{id:1, .... items: [...items here...]}, {id:2, .... items: [...items here...]}]
Any idea?
You can get the desired result directly from the query when you aggregate the resultset using jsonb_agg (like items further down the query).
SELECT
jsonb_agg(jsonb_build_object(
'id', p.id,
'description', p.description,
'discounted_price', p.discounted_price,
'items', jsonb_agg((i.id, i.price, i.title))
))
--- the rest of your query

Sequelize is automatically adding a sub query within the where clause. Is there a way to make it skip adding the where clause

I have a Sequelize query that uses INNER JOINS. The issue is that sequelize is internally adding another where clause with a sub-query on the child table. That is eating up the query performance. Below are an examples of my code and the raw query output.
Is there a way to make sequelize skip adding this where clause?
Sequelize version: 6.x
PostModel.findAll({
where: {
id: 1,
},
include: [
{
model: CommentsModel,
required: true,
}
]
})
The query builds an SQL query as below.
SELECT "post".*
FROM (SELECT "post"."*"
FROM "posts" AS "post"
WHERE "post"."id" = 2
AND (SELECT "post_id"
FROM "comments" AS "c"
WHERE "comments"."post_id" = "post"."id" AND ("c"."text_search" ## 'who:*')) IS NOT NULL
ORDER BY "post"."id" DESC
LIMIT 50 OFFSET 0) AS "post"
LEFT OUTER JOIN "post_tags" AS "tags" ON "post"."id" = "tags"."post_id"
LEFT OUTER JOIN "tag" AS "tags->tag" ON "tags"."tag_id" = "tags->tag"."id"
INNER JOIN "comments" AS "c" ON "post"."id" = "c"."post_id" AND ("c"."text_search" ## 'who:*')
ORDER BY "post"."id" DESC;
As you can see the WHERE clause has a new added
(SELECT "post_id"
FROM "comments" AS "c"
WHERE "comments"."post_id" = "post"."id" AND ("c"."text_search" ## 'who:*'))
This is basically killing the performance of the query.
After a lot research I figured out the solution.
We need to add subQuery: false within the association.
PostModel.findAll({
where: {
id: 1,
},
include: [
{
subQuery: false,
model: CommentsModel,
required: true,
}
]
})
Query output:
SELECT "post".*
FROM (SELECT "post"."*"
FROM "posts" AS "post"
WHERE "post"."id" = 2
ORDER BY "post"."id" DESC
LIMIT 50 OFFSET 0) AS "post"
LEFT OUTER JOIN "post_tags" AS "tags" ON "post"."id" = "tags"."post_id"
LEFT OUTER JOIN "tag" AS "tags->tag" ON "tags"."tag_id" = "tags->tag"."id"
INNER JOIN "comments" AS "c" ON "post"."id" = "c"."post_id" AND ("c"."text_search" ## 'who:*')
ORDER BY "post"."id" DESC;

SQL set a column to true if some conditions meet

Got 3 tables, said
mail
{
id: number,
content: string
}
player
{
id: number,
name: string
}
is_mail_read
{
mail_id: number,
player_id: number
}
What I want to achieve:
Given a player_id, return the mails with a column is_read which is true (or 1) if there is a record in is_mail_read, false if there isn't.
For example:
[
{
id: 2,
content: "I am a mail",
is_read: true
},
{
id: 3,
content: "I am a mail too",
is_read: false
},...
]
I have tried left join but got no idea what to do next.
The database is MariaDB.
I would use exists:
select m.*,
(exists (select 1
from is_mail_read imr
where imr.mail_id = m.id and
imr.player_id = ? -- your desired player id
)
) as read_flag
from mail m;
You're on the right track with using LEFT JOIN. Now you simply check, if for the record in the mail table there is a record in the player table by checking if the joined record is NULL or not.
I'm assuming that you want to display all mails and return the info if a mail is read for a specific player. Then you have to filter for the player in the JOIN clause, not the WHERE clause. If you do it in the WHERE clause, you implicitly turn the LEFT JOIN into an INNER JOIN. Except when you write the WHERE clause like
WHERE p.name = "John" OR p.name IS NULL
So your query should look like this:
SELECT
m.id,
m.content,
IF(p.id IS NULL, false, true) AS is_read
FROM
mail m
LEFT JOIN is_mail_read imr ON m.id = imr.mail_id
LEFT JOIN player p ON imr.player_id = p.id AND p.name = "John"

Convert a $_GET value in PDO

I wrote a basic function to join 3 tables regarding a similar id.
{
$id = $_GET["id"];
$product_edit_query = $this->DB->query('
SELECT * FROM products
LEFT JOIN products_ingredients
ON products_ingredients.product_id = products.product_id
LEFT JOIN products_languages
ON products_languages.product_id = products.product_id
WHERE products.product_id = 73
');
$product_edit = $product_edit_query->fetch();
$this->smarty->assign('product_edit', $product_edit);
}
In the last line I have of my request, i have WHERE products.product_id = 73. 73 is hardcoded, and should be the value of $_GET["id"], but all I tried didn't work to convert it in PDO, any help ?
Solution given by a friend:
{
$stmt = $this->DB->prepare('
SELECT * FROM products
LEFT JOIN products_ingredients
ON products_ingredients.product_id = products.product_id
LEFT JOIN products_languages
ON products_languages.product_id = products.product_id
WHERE products.product_id = :id
');
$stmt->execute([':id' => $_GET["id"]]);
$get_product_base = $stmt->fetch();
$this->smarty->assign('get_product_base', $get_product_base);
}

Doctrine ORM - Self join without any real relation

I'm trying to find related objects to one object by matching the objects tags. I've constructed a mysql query which will return the objects that match the most by counting the matching tags.
I'm new to doctrine (1.2) so I'm wondering if someone could help me to get on the right track modifying my schema and creating a DQL query? The big problem is that the two tagset doesn't relate to each others in my schema.yml I would guess.
Schema.yml:
Object:
columns:
name:
relations:
Tags: { foreignAlias: Objects, class: Tag, refClass: Tagset}
Tagset:
columns:
object_id: {type: integer, primary: true, notnull: true}
tag_id: { type: integer, primary: true, notnull: true }
relations:
Object: { foreignAlias: Tagsets }
Tag: { foreignAlias: Tagsets }
Tag:
columns:
name: { type: string(255), notnull: true }
Object: { foreignAlias: Tags, class: Object, refClass: Tagset}
Here is the mysql query which works using the schema above:
SELECT object.name, COUNT(*) AS tag_count
FROM tagset T1
INNER JOIN tagset T2
ON T1.tag_id = T2.tag_id AND T1.object_id != T2.object_id
INNER JOIN object
ON T2.object_id = object.id
WHERE T1.object_id = 2
GROUP BY T2.object_id
ORDER BY COUNT(*) DESC
You can use subqueries as well. Something like this:
$object_id = 2;
Doctrine::getTable('Tagset')->createQuery('t')
->select('t.tag_id, o.id, o.name, COUNT(t.tag_id) AS tag_count')
->innerJoin('t.Object o WITH o.id != ?', $object_id)
->where('t.tag_id IN (SELECT t.tag_id FROM Tagset t WHERE t.object_id = ?)', $object_id)
->groupBy('t.object_id')
Solution:
$q = new Doctrine_RawSql();
$this->related_objects = $q->
select('{o.name}')->
from('tagset t1 JOIN tagset t2 ON t1.tag_id = t2.tag_id AND t1.object_id != t2.object_id JOIN object o ON t2.object_id = o.id')->
addComponent('o','Object o')->
where('t1.object_id = ?', $this->object->id)->
groupBy('t2.object_id')->
orderBy('COUNT(*) DESC')->
execute();