how to make DISTINCT, ORDER BY, CASE work together? - sql

Required sort by condition - if field is exclusive, then sort by "fees"."exclusive_price" else by "fees"."additional_price".
SQL completely looks like this:
SELECT DISTINCT "numbers".*
FROM "numbers"
INNER JOIN "users_numbers" ON "users_numbers"."number_id" = "numbers"."id"
INNER JOIN "users" ON "users"."id" = "users_numbers"."user_id"
INNER JOIN "fees" ON "fees"."user_id" = "users"."id"
WHERE "numbers"."state" != 'removed'
ORDER BY CASE "numbers"."is_exclusive" WHEN TRUE THEN "fees"."exclusive_price" ELSE "fees"."additional_price" END desc"
But I get an error (in rails):
ActiveRecord::StatementInvalid: PG::InFailedSqlTransaction: ERROR: current transaction is aborted, commands ignored until end of transaction block
Error because of distinct. Without distinct - success.
How to correct SQL?
Added columns from related tables to SELECT - didn't help:
SELECT DISTINCT "fees"."exclusive_price", "fees"."additional_price", "sender_numbers".*
FROM ...

A plain DISTINCT de-duplicates based on the complete SELECT list. The (final) ORDER BY step then only accepts expressions that are part of that SELECT list.
To order by that CASE expression, you'd have to include it in the SELECT list. Like:
SELECT DISTINCT n.*, CASE WHEN n.is_exclusive THEN f.exclusive_price ELSE f.additional_price END AS order_col
FROM numbers n
JOIN users_numbers un ON un.number_id = n.id
JOIN users u ON u.id = un.user_id
JOIN fees f ON f.user_id = u.id
WHERE n.state <> 'removed'
ORDER BY order_col DESC;
You would have to wrap that in an outer query to remove order_col from the SELECT list. But I am not sure, we have the complete picture, yet. And there may be simpler ways ...

Related

PostgreSQL query optimize

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

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

Rows which appeared only once in query results

I want to have rows which appeared in 25th of February and did not appear in 6th of March. I've tried to do such query:
SELECT favfd.day, dv.title, array_to_string(array_agg(distinct "host_name"), ',') AS affected_hosts , htmltoText(dsol.fix),
count(dv.title) FROM fact_asset_vulnerability_finding_date favfd
INNER JOIN dim_vulnerability dv USING(vulnerability_id)
INNER JOIN dim_asset USING(asset_id)
INNER JOIN dim_vulnerability_solution USING(vulnerability_id)
INNER JOIN dim_solution dsol USING(solution_id)
INNER JOIN dim_solution_highest_supercedence dshs USING (solution_id)
WHERE (favfd.day='2018-02-25' OR favfd.day='2018-03-06') AND
dsol.solution_type='PATCH' AND dshs.solution_id=dshs.superceding_solution_id
GROUP BY favfd.day, dv.title, host_name, dsol.fix
ORDER BY favfd.day, dv.title
which gave me following results:
results
I've read that I need to add something like "HAVING COUNT(*)=1" but like you can see in query results my count columns looks quite weird. Here is my results with that line added:
results with having
Can you advice me what I am doing wrong?
One way is to use a HAVING clause to assert your date criteria:
SELECT
dv.title,
array_to_string(array_agg(distinct "host_name"), ',') AS affected_hosts,
htmltoText(dsol.fix),
count(dv.title)
FROM fact_asset_vulnerability_finding_date favfd
INNER JOIN dim_vulnerability dv USING(vulnerability_id)
INNER JOIN dim_asset USING(asset_id)
INNER JOIN dim_vulnerability_solution USING(vulnerability_id)
INNER JOIN dim_solution dsol USING(solution_id)
INNER JOIN dim_solution_highest_supercedence dshs USING (solution_id)
WHERE
dsol.solution_type = 'PATCH' AND
dshs.solution_id = dshs.superceding_solution_id
GROUP BY dv.title, dsol.fix
HAVING
SUM(CASE WHEN favfd.day = '2018-02-25' THEN 1 ELSE 0 END) > 0 AND
SUM(CASE WHEN favfd.day = '2018-03-06' THEN 1 ELSE 0 END) = 0
ORDER BY favfd.day, dv.title
The only major changes I made were to remove host_name from the GROUP BY clause, because you use this column in aggregate, in the select clause. And I added the HAVING clause to check your logic.
The change you should make is to replace your implicit join syntax with explicit joins. Putting join criteria into the WHERE clause is considered bad practice nowadays.

Sub query brain freeze

Its been a while since I've done sub queries and for the life of me I cant see whats wrong with my query.
The error message I get when executing is:
ORA-00904: "SUB"."PRO_REFNO": invalid identifier
This is my query. I'm obviously doing something wrong but I just cant see it.
SELECT
prop.PRO_ADR_1_LINE,
ele.POE_START_DATE,
ele.POE_ELEMENT_DESCR,
ele.POE_VALUE,
ele.POE_ATTRIBUTE,
ele.POE_FURTHER_ATTRIBUTE,
ele.POE_FURTHER_ATTRIBUTE_DESCR,
prop.PRO_SCHEME,
prop.PRO_SCHEME_DESCR,
GEO.GEO_BUS_UNIT,
GEO.GEO_REGION,
GEO.GEO_REGION_DESCR,
prop.PRO_NEIGHBOURHOOD_DESCR
--sub.pro_refno
FROM property prop
--inner join
left join GEO on prop.PRO_GEO_PATCH=GEO.GEO_PATCH
left join PROPERTY_OTHER_ELEMENT ele on ele.POE_PRO_REFNO =prop.PRO_REFNO
inner join(
SELECT
property.PRO_SCHEME,
count(distinct property.PRO_REFNO)
FROM
PROPERTY
WHERE
property.pro_type = 'P'
GROUP BY
property.PRO_SCHEME
)sub
on sub.pro_refno = prop.PRO_REFNO
where
ele.POE_START_DATE BETWEEN '01-APR-2016' AND sysdate
AND
ele.POE_ELEMENT LIKE 'EST%'
AND
ele.POE_ELEMENT_DESCR <> 'Estate Walkabout - Would you live in this neighbourhood ?'
AND
ele.POE_VALUE IN ( '1','2','3','4','5','6','7','8','9','10' )
Both the outer query and sub query run fine separately. Like I said its been a while so I'm guessing its something stupid I've done/not done.
Thanks
Adam
You didn't give a name to the aggregate column:
inner join(
SELECT
property.PRO_SCHEME,
count(distinct property.PRO_REFNO) -- No name!!!
FROM
PROPERTY
WHERE
property.pro_type = 'P'
GROUP BY
property.PRO_SCHEME
)sub
on sub.pro_refno = prop.PRO_REFNO
Change that to:
inner join(
SELECT
property.PRO_SCHEME,
count(distinct property.PRO_REFNO) As PRO_REFNO
FROM
PROPERTY
WHERE
property.pro_type = 'P'
GROUP BY
property.PRO_SCHEME
)sub
on sub.pro_refno = prop.PRO_REFNO
Your subquery doesn't select PRO_REFNO, so the outer query cannot match it in the JOIN predicate. Try this for the subquery:
SELECT
property.PRO_SCHEME,
property.PRO_REFNO,
count(distinct property.PRO_REFNO)
FROM PROPERTY
WHERE property.pro_type = 'P'
GROUP BY
property.PRO_SCHEME,
property.PRO_REFNO
Also, your COUNT(DISTINCT ...) isn't given an alias, which you'll need if it's ever going to be used in the outer select.

Rails: SQL SELECT Statement Not Appearing When Combined With GROUP BY

I have the following code in Ruby:
Comment.select("comments.*, COALESCE(SUM(votes.value), 0) AS rating,
user_votes.value AS user_value").
joins("LEFT JOIN votes ON votes.voteable_type = '"+type+"' AND votes.voteable_id = comments.id").
joins("LEFT JOIN votes AS user_votes ON user_votes.voteable_type = '"+type+"' AND user_votes.voteable_id = comments.id AND user_votes.user_id = #{user_id}").
where(conditions).group("comments.id").order("comments."+method).limit(limit).offset(offset)
When Rails generates this SQL query, it doesn't include the full select statement:
SELECT COUNT(*) AS count_all, comments.id AS comments_id FROM `comments`
LEFT JOIN votes ON votes.voteable_type = 'reply' AND votes.voteable_id = comments.id
LEFT JOIN votes AS user_votes ON user_votes.voteable_type = 'reply' AND
user_votes.voteable_id = comments.id AND user_votes.user_id = 1 WHERE (commentable_id =
1 AND commentable_type = 'Impression')
GROUP BY comments.id ORDER BY comments.rating DESC LIMIT 10 OFFSET 0
If I remove the group statement, however, then it properly includes the full select statement. What's going on?
select comments.* in your select statement conflicts with group by("comments.id").
Every column selected must either be in the GROUP BY clause or be contained in an aggregate function.
SQL Server shows error for SQL:
select c.* from tt_country c group by c.name
Column 'tt_country.country_id' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
ActiveRecord and arel throws out the specified select because it is ambiguous / invalid.