PostgreSQL query optimize - sql

Here is my query
SELECT
DISTINCT(org.id),
org.name,
org.partner_id,
pos.partner_id,
pos.id,
org.partner_offer_section_id,
pos.title,
pos.offer_value,
pos.offer_currency,
(SELECT user_info.email FROM user_info WHERE user_info.org_id=org.id ORDER BY created ASC LIMIT 1) as user_email,
(SELECT CONCAT(user_info.first_name,' ',user_info.last_name) FROM user_info WHERE user_info.org_id=org.id ORDER BY created ASC LIMIT 1) as name
FROM org
INNER JOIN partner_offer_section pos ON org.partner_offer_section_id = pos.id
WHERE org.partner_offer_section_id != 0 AND org.partner_id != 0
Here is the same subquery that is executing the twice the same query. I was trying to left join this query but the problem is when I left join I got a null value. I have to get one user name or user email insted of multiple users aginst org.
SELECT org.name,
org.partner_id,
org.partner_offer_section_id,
org.offer_applied_date,
partner_offer_section.title,
partner_offer_section.offer_value,
partner_offer_section.offer_currency,
user_info.email
FROM org
left join (SELECT user_info.id, user_info.email,user_info.created, user_info.org_id FROM user_info WHERE role='Org Admin' LIMIT 1) user_info on org.id = user_info.org_id
left join partner_offer_section on org.partner_offer_section_id = partner_offer_section.id
where org.partner_id = 1
Now I wanna optime this query instead of multiple same subqueries.

You should join the table directly instead of doing a subquery. Bellow is the example, making a JOIN with the first table and the LEFT only with the last one. Also, DISTINCT applies to all columns, it's not a function, as user #a_horse_with_no_name pointed out
SELECT DISTINCT
org.name,
org.partner_id,
org.partner_offer_section_id,
org.offer_applied_date,
partner_offer_section.title,
partner_offer_section.offer_value,
partner_offer_section.offer_currency,
user_info.email
FROM org
join partner_offer_section on org.partner_offer_section_id = partner_offer_section.id
left join user_info on org.id = user_info.org_id
and user_info.role='Org Admin'
where org.partner_id = 1

Related

postgres: LEFT JOIN table and field does not exist

This is my query
SELECT org.id,
org.name,
org.type,
org.company_logo,
(SELECT org_profile.logo_url FROM org_profile WHERE org_profile.org_id = org.id AND org_profile.status = 'active' ORDER BY org_profile.id DESC LIMIT 1) as logo_url,
org.short_description,
org_profile.value_prop,
count(*) OVER () AS total
FROM org
LEFT JOIN user_info ON user_info.id = org.approved_by
INNER JOIN (select distinct org_profile.org_id from org_profile) org_profile ON org_profile.org_id = org.id
WHERE
org.type = 'Fintech'
AND org.status = 'APPROVED'
AND org.draft != TRUE
ORDER BY org.id DESC
I am using LEFT JOIN query with my org_profile table. I used distinct for unique org id but the problem is org_profile.value_prop column does not work. The error is showing column org_profile.value_prop does not exist
I'm trying to solve this issue. But I don't figure out this issue.
basically, the error informs that you try to get the value_prop field from org_profile subquery, which basically doesn't exist.
It's difficult to give a working query by writting just on the paper, but I think that:
it's worth to apply the handy aliasses for each subquery
deduplication, if required, should be done in the subquery. When multiple fields used DISTINCT may be insufficient - RANK function may be required.
you make some operations to get the logo_url by a scalar subquery - it seems a bit strange, especially the same table is used in JOIN - I would suggest to move all logic related to org_profile to the subquery. Scalar expressions would throw an error in case multiple values would be found in output.
SELECT
org.id,
org.name,
org.type,
org.company_logo,
prof.logo_url,
org.short_description,
prof.value_prop,
count(*) OVER () AS total
FROM org
JOIN (
select distinct org_id, logo_url, value_prop -- other deduplication type (RANK) may be required
from org_profile
where status = 'active' -- ?
) prof ON org.id = prof.org_id
LEFT JOIN user_info usr ON usr.id = org.approved_by
WHERE
org.type = 'Fintech'
AND org.status = 'APPROVED'
AND org.draft != TRUE
ORDER BY org.id DESC

Postgresql select related tags (many to many)

I have a VIEW named review, which is related to a cars table, and the cars table have a many to many relationship with table tags (through join table named cars_tags), but what I need is retrieve the reviews from the cars which are related with some tags, AND at same time related to another tags. What I have today is the following SQL code:
SELECT "cars"."review".*
FROM "cars"."review"
LEFT JOIN cars.cars ON (cars.review.car_id = cars.cars.id)
LEFT JOIN cars.makes ON (cars.cars.make_id = cars.makes.id)
LEFT JOIN cars.cars_tags ON (cars.cars.id = cars.cars_tags.car_id)
LEFT JOIN cars.tags ON (cars.cars_tags.tag_id = cars.tags.id)
WHERE (cars.tags.id IN ('91782e95-8c5d-4254-82ab-b11a21306c18'))
AND (cars.tags.id IN ('031cec30-df27-471e-858d-53c3d9657c8a'))
ORDER BY "cars"."review"."score" DESC LIMIT 100
This SQL brings me NO results, but I am sure that there are cars which are related to first id:'91782e95-8c5d-4254-82ab-b11a21306c18'AND '031cec30-df27-471e-858d-53c3d9657c8a' at same time.
What am I doing wrong?
bool_or
select r.col1, r.col2
from
cars.review r
left join
cars.cars on r.car_id = cars.id
inner join
cars.cars_tags on cars.id = cars_tags.car_id
inner join
cars.tags on cars_tags.tag_id = tags.id
group by r.col1, r.col2
having
bool_or (tags.id = '91782e95-8c5d-4254-82ab-b11a21306c18')
and
bool_or (tags.id = '031cec30-df27-471e-858d-53c3d9657c8a')
order by r.score desc
limit 100
exists version:
select col1, col2
from cars.review
where exists (
select 1
from
cars.cars
inner join
cars.cars_tags on cars.id = cars_tags.car_id
inner join
cars.tags on cars_tags.tag_id = tags.id
where review.car_id = cars.id
group by 1
having
bool_or (tags.id = '91782e95-8c5d-4254-82ab-b11a21306c18')
and
bool_or (tags.id = '031cec30-df27-471e-858d-53c3d9657c8a')
)
order by score desc
limit 100
From what I understand you're interested in reviews for a car that has 2 specific tags.
This can be done with the query below. I've removed the reference to cars.make as you weren't retrieving any data from it. Also I've removed the reference to cars.tags as the only information you were using was the tag id which is in the cars_tags table.
SELECT "cars"."review".*
FROM "cars"."review"
WHERE
EXISTS (SELECT * FROM cars.cars_tags
WHERE cars.cars_tags.car_id = cars.review.cars_id
AND cars.cars_tags.tag_id = '91782e95-8c5d-4254-82ab-b11a21306c18')
AND
EXISTS (SELECT * FROM cars.cars_tags
WHERE cars.cars_tags.car_id = cars.review.cars_id
AND cars.cars_tags.tag_id = '9031cec30-df27-471e-858d-53c3d9657c8a')
ORDER BY "cars"."review"."score" DESC LIMIT 100
The query simply finds all reviews where there exists a cars_tags entry for the two tag_id's you're after.

Order BY on column generated by select statement

i'm running this query:
SELECT
claims.rID
,claims.client
,clients.cName
,cur.currency
,carriers.scac
,carriers.cName
,claims.client+claims.counter
,claims.dateOn
,(SELECT top 1 errorCode FROM entries WHERE entries.rID=claims.rID) as errorCode
,(SELECT SUM(refundDue) FROM entries WHERE entries.rID=claims.rID) as amount
,auditors.initials
FROM claims
INNER JOIN clients ON clients.code = claims.client
INNER JOIN currency cur ON claims.currency = cur.currencyID
INNER JOIN entries ON claims.rID = entries.rid
INNER JOIN carriers ON carriers.carrierID = claims.carrierID
INNER JOIN auditors ON claims.auditorID=auditors.auditorID
GROUP BY
claims.rID
,claims.client
,claims.counter
,claims.dateOn
,carriers.scac
,carriers.cName
,clients.cName
,cur.currency
,auditors.initials
,errorCode
ORDER BY errorCode ASC
The focus should be on the orderBy errorCode. For some reason, it's not actually ordering Alphabetically by errorCode. Any ideas as to why?
Most databases don't allow you to use aliases created in the SELECT-list in the GROUP BY, ORDER BY and WHERE clauses. Instead, you have to repeat the expression used in the SELECT-list:
SELECT
claims.rID,
...,
(SELECT top 1 errorCode FROM entries WHERE entries.rID=claims.rID) AS errorCode,
...
FROM
claims INNER JOIN ...
GROUP BY
claims.rID,
...,
(SELECT top 1 errorCode FROM entries WHERE entries.rID=claims.rID)
ORDER BY
(SELECT top 1 errorCode FROM entries WHERE entries.rID=claims.rID) ASC
But I think that is better to use a sub-select as data source in the FROM-clause:
SELECT
claims.rID,
...,
E.errCode, E.amount,
...
FROM claims
INNER JOIN clients ON clients.code = claims.client
INNER JOIN currency cur ON claims.currency = cur.currencyID
INNER JOIN (SELECT rID, MIN(errorCode) AS errCode, SUM(refundDue) AS amount
FROM entries
GROUP BY rID) AS E
ON claims.rID = E.rid
INNER JOIN carriers ON carriers.carrierID = claims.carrierID
INNER JOIN auditors ON claims.auditorID=auditors.auditorID
GROUP BY
claims.rID,
...,
E.errCode
ORDER BY
E.errCode ASC
Note that I included the SUM(refundDue) AS amount as well. If the error codes can be NULL within entries, use MAX(errorCode) instead of MIN(errorCode).

SQL - Select resources accessible by an user directly or via his group

I'm trying to list all the resources a user has access to. He can have a direct access (user permission) or being in a group which has access to the resource (group permission).
My tables would be:
group: id, name
user: id, name
resource: id, name
rel_resource_user: resource, user
rel_group_resource: group, resource
rel_group_user: group, user
I tried the following query:
SELECT DISTINCT resource.id
FROM resource
LEFT OUTER JOIN rel_group_resource ON resource.id = rel_group_resource.resource
INNER JOIN `group` ON rel_group_resource.`group` = `group`.id
INNER JOIN rel_group_user ON rel_group_user.`group` = `group`.id
LEFT OUTER JOIN rel_resource_user ON rel_resource_user.resource = resource.id
WHERE rel_resource_user.user = 1 OR rel_group_user.user = 1
But I receive only the resources access via the group and not by the user directly. If I cut the query in two query, one for the resources accessed by groups and one by users directly, it works. But I'm not able to get all the resources in one query.
Thank you for you help !!!
You could try to use two where resource.id in like this:
select resource.id
from resource
where resource.id in (select rel_resource_user.resource
from rel_resource_user
where rel_resource_user.user = 1) or
resource.id in (select rel_group_resource.resource
from rel_group_resource
inner join rel_group_user
on rel_group_resource.group = rel_group_user.group
where rel_group_user.user = 1)
If they have the same number of output fields and the same type you can use UNION or UNION ALL, the only difference is UNION gets rid of duplicates.
SELECT
ResourceID
FROM
FIRSTTABLE
UNION ALL
SELECT ResourcedID
FROM
SecondTable
SELECT r.id
FROM resource AS r
WHERE EXISTS
( SELECT *
FROM rel_resource_user AS ru
WHERE ru.resource = r.id
AND ru.user = 1
)
OR EXISTS
( SELECT *
FROM rel_group_user AS gu
JOIN `group` AS g
ON g.id = gu.`group`
JOIN rel_group_resource AS gr
ON gr.`group` = g.id
WHERE gr.resource = r.id
AND gu.user = 1
)
try this query:
select Resource_id from
(
select rel_resource_user.resource as Resource_id,user.id as user_id FROM user inner join rel_resource_user on rel_resource_user.user=user.id
UNION
SELECT rel_group_resource..resource as Resource_id,rel_group_user.user as user_id FROM group inner join rel_group_resource on group.id= rel_group_resource.group INNER JOIN rel_group_user.group= group.id
)

Joining top records in T-SQL

SELECT MD.*, Contact.FirstName
FROM MerchantData MD
JOIN Merchant M ON M.MerchID = MD.MerchID
JOIN (SELECT TOP 1 * FROM Location WHERE Location.BusID = MD.BusID) L ON L.BusID=MD.BusID
AND L.Deleted = 0
JOIN Contact ON Contact.ContactID = L.PrincipalID
I am using SQLSERVER 2008 and trying to write this SQL statement. There is some times multiple locations for a busid and I want to join in only the first found. I am getting an error on the part "Location.BusID = MD.BusID" as MD.BusID cannot be bound. Is it possible to use the MD table in the nested select statment in this join or is there another way of accomplishing this?
I am contiplating putting the data using nested querys in the column list to grab the contact data driectly there.
It would be simpler I think to have a subquery of the full result set:
SELECT MD.*, Contact.FirstName
FROM MerchantData MD
JOIN Merchant M ON M.MerchID = MD.MerchID
JOIN (SELECT BusID, MAX(PrincipalID)
FROM Location
WHERE Deleted = 0
GROUP BY BusID) L ON L.BusID=MD.BusID
JOIN Contact ON Contact.ContactID = L.PrincipalID
You still get one record per BusID in the JOIN but it's not correlated.
SELECT MD.*, Contact.FirstName
FROM MerchantData MD
JOIN Merchant M ON M.MerchID = MD.MerchID
CROSS APPLY (SELECT TOP 1 * FROM Location WHERE BusID = MD.BusID AND DELETED = 0) L
JOIN Contact ON Contact.ContactID = L.PrincipalID
This is a case of the "top n per group" problem. This question will guide you:
SQL Server query select 1 from each sub-group
You'll want to be doing something like this:
SELECT MD.* ,
Contact.FirstName
FROM MerchantData MD
JOIN Merchant M ON M.MerchID = MD.MerchID
JOIN ( select * ,
seq = rank() over( partition by BusID order by BusID , ... )
from Location
where L.Deleted = 0
) L on L.BusID = MD.BusID
and seq = 1
JOIN Contact ON Contact.ContactID = L.PrincipalID
The virtual table expression should return at most 1 Location per BusID (0 if the BusID has no non-deleted Locations).
To try and isolate out the error I would try. See if it can match Location.BusID = MD.BusID.
SELECT MD.*, Contact.FirstName
FROM MerchantData MD
JOIN Merchant M ON M.MerchID = MD.MerchID
JOIN Location On Location.BusID = MD.BusID
You do not use the * so use
SELECT TOP 1 Location.BusID FROM Location WHERE Location.BusID = MD.BusID
Once you get the syntax working.
You do know that once you get this working it will only match if the "first" row and then check if it is deleted. The problem is that without an order by the "first" row is arbitrary. Even with a clustered index on a table there is no guaranteed sort without an order by clause. To get a repeatable answer you need a sort. But if you are sorting and only want the top row then a MAX or MIN and a group be more straight forward.
If you want just business that have one or more deleted locations then the following should work but you need to break out the columns for the group by. If two deleted locations have differenct contact name then it will report each. So, this may not be what you are looking for.
SELECT MD.col1, MD.col2, Contact.FirstName
FROM MerchantData MD
JOIN Merchant M ON M.MerchID = MD.MerchID
JOIN Location L
ON L.BusID = MD.BusID
AND L.Deleted = 0
JOIN Contact ON Contact.ContactID = L.PrincipalID
GROUP BY MD.col1, MD.col2, Contact.FirstName