Sequelize query with NOT IN clause and nested SELECT - orm

I have an SQL query which I would like to express using the sequelize query syntax.
DB Scheme
Users: (id, username, ...)
Groups: (id, name, ...)
User_Groups: (id, #groupId, #userId)
Query Description
Retrieve all users that are not yet part of a particular group.
Raw SQL Query (working)
SELECT User.id, User.email FROM Users as User
WHERE User.id NOT IN (
SELECT User_Group.userId FROM User_Groups as User_Group
WHERE User_Group.groupId = ${groupId}
)
Where I'm blocked
I tried to simulate a join using the sequelize include statement, but my brain is stuck transforming the nested SELECT query as well as the NOT IN operator...

It's not possible to represent sub-queries other than using Sequelize.literal in the where clause like this:
const users = await database.User.findAll({
where: Sequelize.where(Sequelize.literal(`(User.id NOT IN (
SELECT User_Group.userId FROM User_Groups as User_Group
WHERE User_Group.groupId = '${groupId}'
))`)
}
or you can mix up Op.notIn with Sequelize.literal

Related

PostgreSQL subqueries as values

I am trying to use a postgreSQL INSERT query with a subquery as parameter value. This is to find the corresponding user_id from an accompanying auth_token in user_info tabel first and then create a new entry in a different table with the corresponding user_id.
My query looks something like this
INSERT INTO user_movies(user_id, date, time, movie, rating)
VALUES ((SELECT user_id FROM user_info where auth_token = $1),$2,$3,$4,$5)
RETURNING *
I know that a query such as this will work with a single value
INSERT INTO user_movies(user_id)
SELECT user_id FROM user_info where auth_token = $1
RETURNING *
but how do I allow for multiples input values. Is this even possible in postgreSQL.
I am also using nodejs to run this query -> therefore the $ as placeholders.
To expand on my comment (it is probably a solution, IIUC): Easiest in this case would be to make the inner query return all the values. So, assuming columns from the inner query have the right names, you could just
INSERT INTO user_movies(user_id, date, time, movie, rating)
SELECT user_id,$2,$3,$4,$5 FROM user_info where auth_token = $1
RETURNING *
Note this form is also without VALUES, it uses a query instead.
Edited 20220424: a_horse_with_no_name removed the useless brackets around SELECT ... that appeared in my original version; thanks!
YOu could try uising where IN clause
INSERT INTO user_movies(user_id)
SELECT user_id
FROM user_info
WHERE auth_token IN ($1,$2,$3,$4,$5)
RETURNING *

SQL Injection Method

The Injection Procedures are :
SELECT UserId, Name, Password FROM Users WHERE UserId = 105 or 1=1;
But, My Question Is how the injection query is working in the sql?
its when you have your query as string in your code, something like this
Query = "SELECT UserId, Name, Password FROM Users WHERE UserId = '" + sUserID + "'"
So you pass sUserID = "ABC' OR 1=1;"
this will be translated like
SELECT UserId, Name, Password FROM Users WHERE UserId = 'ABC' OR 1=1
Since the condition 1=1 is always true, adding it at the end of a WHERE statement renders it irrelevant, and always true, as if the WHERE statement does not exist at all. Thus, the query is always executed, regardless of any other conditions added to the WHERE statement.
In the example you provided, If you allow your users to write down their own userID, they can write 105 or 1=1 in the input fields or in a website's URL address, and since or 1=1 makes UserId=105 useless, and the query will always select the data, hence the SQL injection.

How do I get rid of inherited SELECTs in Postgres queries?

Postgres 9.4
I guess, queries like this is not the best approach in terms of database performance:
SELECT t.name,
t.description,
t.rating,
t.readme,
t.id AS userid,
t.notifications
FROM ( SELECT "user".name,
"user".description,
"user".rating,
"user".readme,
"user".id,
( SELECT array_to_json(array_agg(row_to_json(notifications.*))) AS array_to_json
FROM ( SELECT notification.id,
notification.action_type,
notification.user_id,
notification.user_name,
notification.resource_id,
notification.resource_name,
notification.resource_type,
notification.rating,
notification.owner
FROM notification
WHERE (notification.owner = "user".id)
ORDER BY notification.created DESC) notifications) AS notifications
FROM "user") t
Column notification contains json object with all the matched rows from notification table.
How should I rebuild this query to receive data in the same manner? I suppose, I should use JOIN commands somehow.
I have request, which utilise more than one inherited SELECT.
Thank you for your time!
The outermost query only aliases id to userid. You can move the alias to the inner query, and omit the outer query entirely.
Then you can create a function to create the notification JSON:
create or replace function get_user_notifications(user_id bigint)
returns json language sql as
$$
select array_to_json(array_agg(row_to_json(n)))
from (
select id
, action_type
, ... other columns from notification ...
from notification
-- Use function name to refer to parameter not column
where user_id = get_user_notifications.user_id
order by
created desc
) n
$$;
Now you can write the query as:
select id as userid
, ... other columns from "user" ...
, get_user_notifications(id) as notifications
from "user" u;
Which looks a lot better, at the cost of having to maintain Postgres functions.

SQL get distinct values but showing all rows with Symfony Doctrine

I have a table like :
a.id; a.username; a.mail; a.optin
Considering theses values :
1; tobby; tobby#google.com; true
2; franck; tobby#google.com; true
3; john; john#google.com; true
I would like (in SQL, but also in Symfony with Doctrine) to get ALL values and ALL rows (id, username, mail, optin) BUT Distinct by email.
With SELECT DISTINCT mail, I only have mails....
Could you help me please ? Thanks !
$qb = $em->getRepository('Your Entity here')->createQueryBuilder('a')->groupBy('a.mail');
$result = $qb->getQuery()->getResult();
Here's more explanation:
The first line builds your query with a select all columns of the entity you fill in and the groupBy will give you distinct rows by email.
In MySql the query generated from this is equivalent to:
SELECT *
FROM 'Your Entity here'
GROUP BY mail

Changing the RoR SQL call to NAME instead of *

Being that it is better to call a SQL database with the specific item, for example:
Bad:
SELECT * FROM tblUsers
Good:
SELECT email FROM tblUsers
How would I do this in RoR to make it explicitly perform the latter example?
Assuming your tblUsers table is used by User model, you should do
User.all(:select => "email")