Make SELECT subquery COUNT the total subscribers of a subscriber - sql

I trying to create a query that counts the total subscribers of a subscriber. It currently looks like this:
await this.queryInstance.query(
'SELECT all_users_subbed_to.* , (SELECT COUNT(??????)) AS subscribers_sub_count
FROM
(SELECT publisher_id, subscriber_id, u2.username
AS username, u2.user_photo AS user_photo
FROM subscribers s
INNER JOIN users u
ON (u.id = s.subscriber_id)
INNER JOIN users u2 ON (u2.id = s.publisher_id)
WHERE subscriber_id = ($1)
LIMIT 20
OFFSET ($2))
AS all_users_subbed_to;'
,
[currentUserId = 80, offset]
);
The FROM CLAUSE AKA all_users_subbed_to is WORKING correctly and displays a ALL the subscribers the current user has. The data comes back as this:
"subscribedToCurrentUser": [
{
"publisher_id": 84,
"subscriber_id": 80,
"username": "supercoookie",
"user_photo": "profile-pic-for-supercoookie.jpeg"
},
{
"publisher_id": 88,
"subscriber_id": 80,
"username": "GERPAL1",
"user_photo": "profile-pic-for-GERPAL1.jpeg"
}
]
The issue I am having is getting the total subscriber counts for the list of those subscribers. I need to use the subscribers publisher_id ie all_users_subbed_to.publisher_id and get their total subs (using COUNT) from the subscribers table. I would like to create a new column called have subscribers_sub_count that contains that total.
Any ideas?
It should look like this:
"subscribedToCurrentUser": [
{
"publisher_id": 84,
"subscriber_id": 80,
"username": "supercoookie",
"user_photo": "profile-pic-for-supercoookie.jpeg",
"subscribers_sub_count": 3
},
{
"publisher_id": 88,
"subscriber_id": 80,
"username": "GERPAL1",
"user_photo": "profile-pic-for-GERPAL1.jpeg",
"subscribers_sub_count": 70
}
]
The subscribers table looks like this:

await this.queryInstance.query(
'SELECT all_users_subbed_to.*, COUNT(all_users_subbed_to.id) AS subscribers_sub_count
FROM
(SELECT publisher_id, subscriber_id, u2.username
AS username, u2.user_photo AS user_photo
FROM subscribers s
INNER JOIN users u
ON (u.id = s.subscriber_id)
INNER JOIN users u2 ON (u2.id = s.publisher_id)
WHERE subscriber_id = ($1)
LIMIT 20
OFFSET ($2))
AS all_users_subbed_to;'
,
[currentUserId = 80, offset]
);

Fixed it. It just needed a WHERE clause that used data from all_users_subbed_to
await this.queryInstance.query(
'SELECT all_users_subbed_to.* ,
(SELECT COUNT(*) FROM subscribers s2 WHERE s2.publisher_id = all_users_subbed_to.publisher_id) AS subscribers_sub_count AS subscribers_sub_count
FROM
(SELECT publisher_id, subscriber_id, u2.username
AS username, u2.user_photo AS user_photo
FROM subscribers s
INNER JOIN users u
ON (u.id = s.subscriber_id)
INNER JOIN users u2 ON (u2.id = s.publisher_id)
WHERE subscriber_id = ($1)
LIMIT 20
OFFSET ($2))
AS all_users_subbed_to;'
,
[currentUserId = 80, offset]
);

Related

Writing sequelize queries for geometries in POSTGRES

I have the following query and want to write it as a sequelize query
`SELECT "Gigs"."id",
"gigType",
"gigCategory",
"gigTitle",
"gigDescription",
"minOrderAmount",
unit,
"unitPrice",
stock,
sold,
"expireDate",
"Gigs".userid AS "sellerId",
"growerType" AS "sellerType",
"points",
json_build_object('id', "locationId", 'lat', lat, 'lng', lng) AS location
FROM (SELECT DISTINCT ON ("gigid") "gigid", "locationId", lat, lng
FROM (SELECT "gigid",
id as "locationId",
st_x(coordinates::geometry) as lat,
st_y(coordinates::geometry) as lng
FROM "Locations"
WHERE ST_DWithin(coordinates,
ST_MakePoint(${location.lat}, ${location.lng})::geography,
${distance})
ORDER BY coordinates <-> ST_MakePoint(${location.lat}, ${location.lng})::geography
LIMIT ${limit}) AS nearGigIds) AS distinctGigIds
INNER JOIN "Gigs"
ON distinctGigIds."gigid" = "Gigs"."id"
INNER JOIN "Users" U
ON U.id = "Gigs".userid
INNER JOIN "Customers" C on U.id = C.userid
INNER JOIN "Growers" G on C.userid = G.userid
WHERE "expireDate" > ${today}::text::date
ORDER BY points DESC
OFFSET ${offset} LIMIT 10;`
I want to know how to write ORDER BY coordinates <-> ST_MakePoint(${location.lat}, ${location.lng})::geography part in the query. I referred the docs and there is a way to write functions in ORDER BY as follows.
order: [
// Will order by otherfunction(`col1`, 12, 'lalala') DESC
[sequelize.fn('otherfunction', sequelize.col('col1'), 12, 'lalala'), 'DESC'],
],
});
But I am confused about how to write the aforesaid part like this.
db.Table.findAll({
attributes: {
include: [
[
Sequelize.fn(
'ST_Distance',
Sequelize.fn('point', Sequelize.col('longitude'), Sequelize.col('latitude')),
Sequelize.fn('point', longitude, latitude),
),
'distanceAttribute',
],
],
},
order:[['distanceAttribute', 'DESC']]
});

PSQL Join alternative to return all rows

I've got a PSQL function that has 3 joins in it and the data is returned in a json object. I have a 4th table that I need to get data from but it has a one-to-many relationship with the table I wish to join on.
This is my current code:
select json_agg(row_to_json(s)) as results from (
select g.*,row_to_json(o.*) as e_occurence,
row_to_json(d.*) as e_definition,
row_to_json(u.*) as e_e_updates,
cardinality(o.m_ids) as m_count
from schema.e_group g
join schema.e_occurrence o on g.id = o.e_group_id
join schema.e_definition d on g.e_id = d.id
left join schema.e_e_updates u on d.id = u.e_id
) s
This gets me an array of objects that follows this rough structure:
[
{
"id": 11308158,
"e_id": 16,
"created_on": "2020-09-09T12:08:07.556062",
"event_occurence": {
"id": 9081887,
"e_id": 16,
"e_group_id": 11308158
},
"e_definition": {
"id": 16,
"name": "Placeholder name"
},
"e_e_updates": {
"id": 22,
"user_id": "7281057e-2876-1673-js7d-7cqj611b4557",
"e_id": 16
},
"m_count": 0
}
]
My problem is that the table e_e_updates can have multiple records for each corresponding e_definition.id.
Clearly the join will not work as hoped in this instance as I'd like e_e_updates to be an array of all the linked rows.
Is there an alternative means of solving this issue?
Basically, you need another level of aggregation. This should do what you want:
select json_agg(row_to_json(s)) as results
from (
select
g.*,
row_to_json(o.*) as e_occurence,
row_to_json(d.*) as e_definition,
u.u_arr as e_e_updates,
cardinality(o.m_ids) as m_count
from schema.e_group g
join schema.e_occurrence o on g.id = o.e_group_id
join schema.e_definition d on g.e_id = d.id
left join (
select e_id, json_agg(row_to_json(*)) u_arr
from schema.e_e_updates
group by on e_id
) u on d.id = u.e_id
) s
You could also do this with a subquery:
select json_agg(row_to_json(s)) as results
from (
select
g.*,
row_to_json(o.*) as e_occurence,
row_to_json(d.*) as e_definition,
(
select json_agg(row_to_json(u.*))
from schema.e_e_updates u
where u.e_id = d.id
) as e_e_updates,
cardinality(o.m_ids) as m_count
from schema.e_group g
join schema.e_occurrence o on g.id = o.e_group_id
join schema.e_definition d on g.e_id = d.id
) s

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"

Two queries return different data

I am running two queries according to me both should return same result, but i think i am missing something
SELECT "id", "email", "first_name", "last_name", locale
FROM "users" AS "users"
WHERE (
EXISTS (
SELECT cf.field_value, ucf.field_value
FROM custom_fields cf
LEFT JOIN users_custom_fields ucf ON cf."id" = ucf.custom_field_id
WHERE cf."id" = 272 AND
((
cf.field_value = 'true') OR
(
ucf.user_id = users.id AND ucf.field_value = 'true'))
))
it returns record
SELECT users.id, cf.field_value, ucf.field_value, ucf.user_id
FROM users
Join custom_fields cf on cf.user_id = users.id
LEFT join users_custom_fields ucf on ucf.custom_field_id = cf."id"
WHERE cf."id" = 272 AND
((
cf.field_value = 'true') OR
(ucf.user_id = users.id AND ucf.field_value = 'true'))
and it doesn't return anything, is there any difference between these two?
your second query you also join custom_fields table with cf.user_id = users.id. first query does not have this.
With adding cf.user_id = users.id, your first query equal the second.
SELECT "id", "email", "first_name", "last_name", locale FROM "users" AS "users" WHERE (
EXISTS (
SELECT cf.field_value, ucf.field_value
FROM custom_fields cf
LEFT JOIN users_custom_fields ucf ON cf."id" = ucf.custom_field_id
WHERE cf."id" = 272
AND cf.user_id = users.id
AND (( cf.field_value = 'true') OR
( ucf.user_id = users.id AND ucf.field_value = 'true'))
))

How to do left join twice in one query?

How to do left join twice or other way to get data in result list like below in one query ?
I want media_information under media
{
id: 9,
gallery_id: 25,
media_id: 9,
media: {
id: 9,
media_information: {
id: 9,
media_id: 9,
}
},
}
query I tried
var dbQuery = `SELECT
gm.*,
row_to_json(m.*) as media,
row_to_json(mi.*) as media_information
FROM "gallery_media" gm
LEFT JOIN media m ON gm.media_id = m.id
LEFT JOIN media_information mi ON gm.media_id = mi.media_id
WHERE "gallery_id" = $1
ORDER BY gm.create_date DESC OFFSET $2 LIMIT $3`;
result
{
id: 9,
gallery_id: 25,
media_id: 9,
media: {
id: 9,
},
media_information: {
id: 9,
media_id: 9,
}
}
UPDATE
I also tried change ON m.id = mi.media_id but result nothing change
var dbQuery = `SELECT
gm.*,
row_to_json(m.*) as media,
row_to_json(mi.*) as media_information
FROM "gallery_media" gm
LEFT JOIN media m ON gm.media_id = m.id
LEFT JOIN media_information mi ON m.id = mi.media_id
WHERE "gallery_id" = $1
ORDER BY gm.create_date DESC OFFSET $2 LIMIT $3`;
table
CREATE TABLE IF NOT EXISTS "gallery_media"(
"id" SERIAL NOT NULL,
"gallery_id" integer NOT NULL,
"media_id" integer NOT NULL,
PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "media"(
"id" SERIAL NOT NULL,
PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "media_information"(
"id" SERIAL NOT NULL,
"media_id" integer NOT NULL,
PRIMARY KEY ("id") );
http://sqlfiddle.com/#!15/ff805
I think this is what you're after:
var dbQuery = `SELECT
gm.*,
row_to_json(m.*) as media,
row_to_json(mi.*) as media_information
FROM "gallery_media" gm
LEFT JOIN media m ON gm.media_id = m.id
LEFT JOIN media_information mi ON m.id = mi.media_id
WHERE "gallery_id" = $1
ORDER BY gm.create_date DESC OFFSET $2 LIMIT $3`;
ie: doing your second LEFT JOIN on media instead of gallery_media