How to aggregate multiple calues in this postgresql query? - postgresql

This is my query:
SELECT "vehicle"."id",
"vehicle"."description",
"tag"."id" AS "tag_id",
"tag"."name" AS "tag_name"
FROM "vehicle"
INNER JOIN "vehicle_tag_pivot" ON "vehicle"."id" = "vehicle_tag_pivot"."vehicle_id"
INNER JOIN "tag" ON "vehicle_tag_pivot"."tag_id" = "tag"."id"
WHERE "tag"."name" IN ('car', 'busses')
AND "vehicle"."category_id" = '1E4FD2C5-C32E-4E3F-91B3-45478BCF0185'
I only have one vehicle in my database. It has two tags -> car and busses (this is test data).
So when I run the query, it returns The exact same vehicle showing the 2 tags it has.
How do I get it to return the vehicle once? I do not really want to return the tag_name. I only want to filter and return all the vehicles that has the both tags car and busses. If one vehicle has both those tags, then it should return that vehicle only. But instead it is returning the same vehicle twice showing its tags.

This should work for you.
SELECT i.*
FROM "interest" as "i"
where i.id in (
select it.interest_id
from interest_tag_pivot it
join tag t on it.tag_id = t.id
where t.name in ('car', 'busses')
group by it.interest_id
having count (*) = 2
)
and i.category_id = '1E4FD2C5-C32E-4E3F-91B3-45478BCF0185'

Do not JOIN - joins leads to duplications. Put all tags logic to WHERE EXISTS(...) or similar.
Here two scalar subqueries comparison in WHERE, try this (important! - it is assumed that tags for each vehicle can't duplicate, so we can compare its counts):
WITH required_tags(val) AS (
VALUES ('car'),
('busses')
)
SELECT "vehicle"."id",
"vehicle"."description",
"tag"."id" AS "tag_id",
"tag"."name" AS "tag_name"
FROM "vehicle"
WHERE "vehicle"."category_id" = '1E4FD2C5-C32E-4E3F-91B3-45478BCF0185'
AND (
-- count matching tags...
SELECT count(1)
FROM "vehicle_tag_pivot"
INNER JOIN "tag" ON "vehicle_tag_pivot"."tag_id" = "tag"."id"
WHERE "vehicle"."id" = "vehicle_tag_pivot"."vehicle_id"
AND "tag"."name" IN (SELECT val FROM required_tags)
) = (
-- ...equals to count required tags
SELECT count(1)
FROM required_tags
)

Related

Using two COUNT in SELECT returns the same values

SELECT user_posts.id,
COUNT(user_post_comments.post_id) as number_of_comments,
COUNT(user_post_reactions.post_id) as number_of_reactions
FROM user_posts
LEFT JOIN user_post_comments
ON (user_posts.id = user_post_comments.post_id)
LEFT JOIN user_post_reactions
ON (user_posts.id = user_post_reactions.post_id)
WHERE user_posts.user_id = '850e6511-2f30-472d-95a1-59a02308b46a'
group by user_posts.id
I have this query for getting the number of comments and reactions from another table by post_id
current output screenshot
To caluclate number of comments and reactions just use subqueries. No need to join and group by.
SELECT user_posts.id,
( select COUNT(*) from user_post_comments
where user_posts.id = user_post_comments.post_id
) as number_of_comments,
( select COUNT(*) from user_post_reactions
where user_posts.id = user_post_reactions.post_id
) as number_of_reactions
FROM user_posts
WHERE user_posts.user_id = '850e6511-2f30-472d-95a1-59a02308b46a'
If both joins return non-null rows, what you get for each count is the product of the number of rows from each for a given user_posts.id. One way you could fix that is by counting distinct identifiers for each table, e.g.
COUNT(DISTINCT user_post_comments.id) as number_of_comments
(Assuming "id" exists as a primary key on that table). This may not be spectacularly efficient, but is relatively simple.

How to avoid duplicates in the STRING_AGG function

My query is below:
select
u.Id,
STRING_AGG(sf.Naziv, ', ') as 'Ustrojstvena jedinica',
ISNULL(CONVERT(varchar(200), (STRING_AGG(TRIM(p.Naziv), ', ')), 121), '')
as 'Partner',
from Ugovor as u
left join VezaUgovorPartner as vup
on vup.UgovorId = u.Id AND vup.IsDeleted = 'false'
left join [TEST_MaticniPodaci2].dbo.Partner as p
on p.PartnerID = vup.PartnerId
left join [dbo].[VezaUgovorUstrojstvenaJedinica] as vuu
on vuu.UgovorId = u.Id
left join [TEST_MaticniPodaci2].hcphs.SifZavod as sf
on sf.Id = vuu.UstrojstvenaJedinicaId
left join [dbo].[SifVrstaUgovora] as vu
on u.VrstaUgovoraId = vu.Id
group by u.Id, sf.Naziv
My problem is that I can have more sf.Naziv and also only one sf.Naziv so I have to check if there is one and then show only one result and if there is two or more to show more results. But for now the problem is when I have only one sf.Naziv, query returns two sf.Naziv with the same name because in first STRING_AGG i have more records about p.Naziv.
I have no idea how to implement DISTINCT into STRING_AGG function
Any other solutions are welcome, but I think it should work with DISTINCT function.
It looks like distinct won't work, so what you should do is put your whole query in a subquery, remove the duplicates there, then do STRING_AGG on the data that has no duplicates.
SELECT STRING_AGG(data)
FROM (
SELECT DISTINCT FROM ...
)
I like this format for distinct values:
(d is required but you can use any variable name there)
SELECT STRING_AGG(LoadNumber, ',') as LoadNumbers FROM (SELECT DISTINCT LoadNumber FROM [ASN]) d
A sample query to remove duplicates while using STRING_AGG().
WITH cte AS (
SELECT DISTINCT product
FROM activities
)
SELECT STRING_AGG(product, ',') products
FROM cte;
Or you can use the following query. The result is same -
SELECT STRING_AGG(product, ',') as products
from (
SELECT product
FROM Activities
GROUP BY product
) as _ ;

MariaDB - order by with more selects

I have this SQL:
select * from `posts`
where `posts`.`deleted_at` is null
and `expire_at` >= '2017-03-26 21:23:42.000000'
and (
select count(distinct tags.id) from `tags`
inner join `post_tag` on `tags`.`id` = `post_tag`.`tag_id`
where `post_tag`.`post_id` = `posts`.`id`
and (`tags`.`tag` like 'PHP' or `tags`.`tag` like 'pop' or `tags`.`tag` like 'UI')
) >= 1
Is it possible order the results by number of tags in posts?
Maybe add there alias?
Any information can help me.
Convert your correlated subquery into a join:
select p.*
from posts p
join (
select pt.post_id,
count(distinct t.id) as tag_count
from tags t
inner join post_tag pt on t.id = pt.tag_id
where t.tag in ('PHP', 'pop', 'UI')
group by pt.post_id
) pt on p.id = pt.post_id
where p.deleted_at is null
and p.expire_at >= '2017-03-26 21:23:42.000000'
order by pt.tag_count desc;
Also, note that I changed the bunch of like and or to single IN because you are not matching any pattern i.e. there is no % in the string. So, better using single IN instead.
Also, if you have defined your table names, column names etc keeping keywords etc in mind, you shouldn't have the need to use the backticks. They make reading a query difficult.

Postgres get rows which hasnt match in other table

I need your help. I need an advanced Query to my database. Im showing part of my database following:
Place (id, name, address)
Local (id, place_id, name)
PlaceReservation(id, local_id, date)
Media_Place (id, place_id, type)
Now I need a query, which gets all places with logo, which have AT LEAST ONE local which hasn't been reserved on a specific day e.g: 2015-07-01.
Help me please, because I haven't an idea how to do it. I thought about an outer join but I don't know how use it.
I was trying by:
$query = 'SELECT DISTINC *,
(SELECT sum(po.rating)/count(po.id)
FROM "Place_Opinion" po
WHERE po.place_id = p.id AND po.deleted = false) AS rating,
mp.path as logo_path
FROM "Place" p
INNER JOIN "Media_Place" mp ON mp.place_id = p.id
JOIN Local ON Local.place_id = Place.id
LEFT JOIN (
SELECT id AS rr, local_id
FROM PlaceReservation
WHERE date_start = \'2015-07-01\') Reserved ON Reserved.local_id = Local.id
WHERE mp.type = ' . Model_Row_MediaPlace::LOGO_TYPE . '
AND mp.deleted = false
AND p.deleted = false
AND rr IS NULL';
Looking for things that do not exist in a database is usually very inefficient. But you can change the logic around by finding places that do have a booking for the specified date, then LEFT JOIN that to all places with a logo and filter out the records with a reservation:
SELECT DISTINCT p.*, po.rating, mp.path as logo_path
FROM "Place" p
JOIN "Media_Place" mp ON mp.place_id = p.id AND mp.deleted = false AND mp.type = ?
JOIN Local ON Local.place_id = p.id
LEFT JOIN (
SELECT id AS rr, local_id
FROM PlaceReservation
WHERE date_start = '2015-07-01') reserved ON reserved.local_id = Local.id
LEFT JOIN (
SELECT place_id, avg(rating) AS rating
FROM "Place_Opinion"
WHERE deleted = false
GROUP BY place_id) po ON po.place_id = p.id
WHERE p.deleted = false
AND reserved.rr IS NULL;
The average rating per places is calculated in a separate sub-query. The error you had was because you referenced the "Place" table (p.id) before it was defined. For simple columns you can do that, but for sub-queries you can't.

TSQL, join to multiple fields of which one could be NULL

I have a simple query:
SELECT * FROM Products p
LEFT JOIN SomeTable st ON st.SomeId = p.SomeId AND st.SomeOtherId = p.SomeOtherId
So far so good.
But the first join to SomeId can be NULL, In that case the check should be IS NULL, and that's where the join fails. I tried to use a CASE, but can't get that to work also.
Am I missing something simple here?
From Undocumented Query Plans: Equality Comparisons.
SELECT *
FROM Products p
LEFT JOIN SomeTable st
ON st.SomeOtherId = p.SomeOtherId
AND EXISTS (SELECT st.SomeId INTERSECT SELECT p.SomeId)