SQL UPDATE statement with LEFT JOIN, GROUP BY and HAVING? - sql

I need to update some rows in a table. I've created a Select statement to make sure I've got the rows I wanted to select.
I want to update task_status_id in the table task, and I've tried in various ways but always end up with a syntax error and have honestly no idea how to do so even though I've tried to follow others examples by using INNER JOIN and putting the select statement in parenthesis. Any help would be appreciated.
UPDATE statement to merge with the SELECT statement.
UPDATE task
SET task_status_id = (SELECT task_status_id
FROM task_status
WHERE task_type_id = 1
AND name = 'Completed');
WHERE
SELECT
t.task_id
FROM task t
LEFT JOIN user u
ON t.user_id = u.user_id
LEFT JOIN contract co
ON u.user_id = co.user_id
LEFT JOIN task_status ts
ON t.task_status_id = ts.task_status_id
WHERE co.status = 'Closed' AND
t.task_type_id = 1 AND
t.task_status_id != (SELECT task_status_id
FROM task_status
WHERE task_type_id = 1
AND name = 'Completed')
GROUP BY t.task_id
HAVING count(t.contract_id) <= 2;

First of all, it doesn't make sense to use LEFT JOIN contract co and then filter results using co.status = 'Closed', because if you're going to filter by a column from a joined table then you should use INNER JOIN (unless you're comparing to null in the filter).
Secondly, syntax here is incorrect - you should use not in instead of !=
AND t.task_status_id != (SELECT task_status_id
FROM task_status
WHERE task_type_id = 1
AND name = 'Completed')
However, since you already joined the task_status table you can replace the above block of code with the following (assuming that task_status_id is a unique column):
AND ts.name != 'Completed'
Either way, you should post sample data and expected result.

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

Delete with subquery that produces two columns (in Postgresql)

I have to delete a few records that match two columns calculated with a subquery.
I can properly see them with this query:
select * from user_assignments as ua,
(
select user_assignments.user_id as uid,
job_selection as jid
from user_assignments
join job_selections on job_id = jobs.id
join data on job_selections.data_id = data.id
where data.my_column IS NULL
) as sq
where sq.uid = ua.user_id AND ua.job_selection_id = sq.jid;
This works, and I see the 7 assignments I want to delete.
However, deleting is not as easy as changing the SELECT by DELETE...
If I do:
delete from user_assignments as ua,
(
...
) as sq
where sq.uid = ua.user_id AND sq.jid = ua.job_selection_id;
I get:
ERROR: syntax error at or near ","
I've tried quite an assortment of combinations, yet I can't get it to work. I imagine it must be quite simple, but I'm quite a newbie in SQL.
Basically, I have a subquery that properly produces two columns that I can use for a SELECT FROM user_assignments and now I want to DELETE FROM user_assignments the records that I know I can SELECT.
Any hints would be very appreciated. Thank you in advance.
Use in or exists:
delete from user_assignments ua
where exists (select 1
from user_assignments ua2 join
job_selections js
on ua2.job_id = js.id join
data d
on js.data_id = d.id
where d.my_column IS NULL and
ua.user_id = sq.uid and ua.job_selection_id = sq.jid
);
Oh, I got it (I think).
Kuddos to this tutorial this tutorial and particularly, the section SQL delete records using subqueries with alias.
If someone else is interested, what I did was:
DELETE FROM user_assignments ua
WHERE EXISTS(
SELECT user_assignments.user_id as uid,
user_assignments.job_selection as jid
FROM user_assignments
join job_selections on job_id = jobs.id
join data on job_selections.data_id = data.id
WHERE data.my_column IS NULL
AND ua.user_id = uid
AND ua.job_selection = jid
)
This query also works fine with SELECT * FROM user_assignments

Optimizing sql condition to apply condition to all dependent rows

I have the following query, split up into a view for readability:
CREATE TEMPORARY VIEW task_depcount AS
SELECT
t.*,
COUNT(p.id) AS unfinished_dep_count
FROM
task t
LEFT JOIN taskdependency d on t.id = d.task_id
LEFT JOIN task p on d.parent_task_id = p.id and p.status != 'SUCCESS'
GROUP BY t.id;
SELECT t.id, t.task_type, t.status
FROM task_depcount t
WHERE t.status = 'READY' AND t.unfinished_dep_count = 0;
Now If we're looking at the EXPLAIN ANALYZE output, this is obviously very inefficient, as we cannot really do index scans over a COUNT() result. Rewriting into a single query with HAVING would also not improve it.
So here's the question: Is there a way to write this query so that the database isn't forced to do sequence scans all over? Database is PostgreSQL 9.2, with no option to upgrade to newer versions.
Or, to state the intended result in plain english: I need all the tasks where either all it's dependencies are of status "success", or there are no dependencies at all.
You can use not exists:
SELECT t.*
FROM task t
WHERE NOT EXISTS (SELECT 1
FROM taskdependency d JOIN
task p
ON d.parent_task_id = p.id
WHERE t.id = d.task_id AND p.status <> 'SUCCESS'
);
With the right indexes, this should be much faster.
The use of an aggregation function such as COUNT() -- whether in a view, subquery, or CTE -- requires processing all the data. With NOT EXISTS, the processing can stop for each at the first unsuccessful one (if any) and not have to do any aggregation.
create temporary view task_depcount as
select t.*
from
task t
left join
taskdependency d on t.id = d.task_id
left join
task p on d.parent_task_id = p.id
group by t.id
having not bool_or(p.status != success) or not bool_or(d.task_id is not null)
;
select t.id, t.task_type, t.status
from task_depcount t
where t.status = 'READY'

Oracle left outer join, only want the null values

I'm working on a problem with two tables. Charge and ChargeHistory. I want to display a selection of columns from both tables where either the matching row in ChargeHistory has a different value and/or date from Charge or if there is no matching entry in ChargeHistory at all.
I'm using a left outer join declared using the ansi standard and while it does show the rows correctly where there is a difference, it isn't showing the null entries.
I've read that there can sometimes be issues if you are using the WHERE clause as well as the ON clause. However when I try and put all the conditons in the ON clause the query takes too long > 15 minutes (so long I have just cancelled the runs).
To make things worse both tables use a three part compound key.
Does anyone have any ideas as to why the null values are being left out?
SELECT values...
FROM bcharge charge
LEFT OUTER JOIN chgHist history
ON charge.key1 = history.key1 AND charge.key2 = history.key2 AND charge.key3 = history.key3 AND charge.chargeType = history.chargeType
WHERE charge.chargeType = '2'
AND (charge.value <> history.value OR charge.date <> history.date)
ORDER BY key1, key2, key
You probably want to explicitly select the null values:
SELECT values...
FROM bcharge charge
LEFT OUTER JOIN chgHist history
ON charge.key1 = history.key1 AND charge.key2 = history.key2 AND charge.key3 = history.key3 AND charge.chargeType = history.chargeType
WHERE charge.chargeType = '2'
AND ((charge.value <> history.value or history.value is null) OR (charge.date <> history.date or history.date is null))
ORDER BY key1, key2, key
You can explicitly look for a match in the where. I would recommend looking at one of the keys used for the join:
SELECT . . .
FROM bcharge charge LEFT OUTER JOIN
chgHist history
ON charge.key1 = history.key1 AND charge.key2 = history.key2 AND
charge.key3 = history.key3 AND charge.chargeType = history.chargeType
WHERE charge.chargeType = '2' AND
(charge.value <> history.value OR charge.date <> history.date OR history.key1 is null)
ORDER BY key1, key2, key;
The expressions charge.value <> history.value change the left outer join to an inner join because NULL results will be filtered out.
A WHERE clause filters the data returned by a join. Therefore when your inner table has null data for a particular column, the corresponding rows get filtered out based on your specified condition. That is why you should move that logic to the ON clause instead.
For the performance issues, you could consider adding indexes on the columns used for joining and filtering.
Have a look at this site, it will be very helpful for you, visual illustration of all the join statements with code samples
blog.codinghorror.com
Quoted of the relevant info in the above link:
SELECT * FROM TableA
LEFT OUTER JOIN TableB
ON TableA.name = TableB.name
Sample output:
id name id name
-- ---- -- ----
1 Pirate 2 Pirate
2 Monkey null null
3 Ninja 4 Ninja
4 Spaghetti null null
Left outer join
produces a complete set of records from Table A, with the matching records (where available) in Table B. If there is no match, the right side will contain null
For any field from an outer joined table used in the where clause you must also permit an IS NULL option for that same field, otherwise you negate the effect of the outer join and the result is the same as if you had used an inner join.
SELECT
*
FROM bcharge charge
LEFT OUTER JOIN chgHist history
ON charge.key1 = history.key1
AND charge.key2 = history.key2
AND charge.key3 = history.key3
AND charge.chargeType = history.chargeType
WHERE charge.chargeType = '2'
AND (
(charge.value <> history.value OR history.value IS NULL)
OR
(charge.date <> history.date OR history.date IS NULL)
)
ORDER BY
key1, key2, key3
Edit: Appears that this is the same query structure used by Rene above, so treat this one as in support of that please.