How to Update with subquery in PostgreSQL - sql

I have a function in MS SQL Server just like this:
UPDATE r
SET
monthly =
(
SELECT SUM(-h.value_ini - h.purchase + h.sold + h.value_fin)
FROM hist_portfolio AS h
WHERE h.comp_id = r.comp_id
AND h.port_id = r.port_id
AND h.exte_id = r.cate_id
AND h.type_id = #type_rel_aux
AND h.hcar_day > #date_month_before
AND h.hcar_day <= #date_base
)
FROM #Month_Table r
WHERE type = 1;
and thats the result (after update):
Seq monthly
2 102471,34
1 -5129,46
3 -29841,23
4 0
But when I execute the same update in a fuction in PostgreSQL, all the rows get the same value:
UPDATE Month_Table
SET variacao_mes_rs = (
SELECT SUM(-h.value_ini - h.purchase + h.sold + h.value_fin)
FROM hist_portfolio AS h
WHERE h.comp_id = r.comp_id
AND h.port_id = r.port_id
AND h.exte_id = r.cate_id
AND h.type_id = v_type_rel_aux
AND h.hcar_day > v_date_month_before
AND h.hcar_day <= v_date_base) FROM Month_Table r WHERE type = 1;
Result (after update), all the same value of Seq 3:]
Seq monthly
1 -29841,23
2 -29841,23
3 -29841,23
4 -29841,23
I don't see the cause of the problem...
Does PostgreSQL have different rules on UPDATE?
Can anyone help me?

Remove the FROM clause from Postgres:
UPDATE Month_Table r
SET variacao_mes_rs = (
SELECT SUM(-h.value_ini - h.purchase + h.sold + h.value_fin)
FROM hist_portfolio AS h
WHERE h.comp_id = r.comp_id
AND h.port_id = r.port_id
AND h.exte_id = r.cate_id
AND h.type_id = v_type_rel_aux
AND h.hcar_day > v_date_month_before
AND h.hcar_day <= v_date_base)
WHERE type = 1;
The FROM clause in an UPDATE behaves differently in the two databases, as you have discovered.

Related

Delete Records from table on the basis of inner join

I have two tables in oracle DB called collection and collection_h. I have to delete all the records from collection_h which has the same below fields in the collection table.
I have to delete all the records from collection_h table that comes out as a result of the below query:
select * from collection inner join collection_h on
collection.pos_protocol_id = collection_h.pos_protocol_id and
collection.terminal_pos_number = collection_h.terminal_pos_number and
collection.cb_file_number = collection_h.cb_file_number and
collection.cb_block_number = collection_h.cb_block_number and
collection.is_stan_batch = collection_h.is_stan_batch and
collection.is_transaction_date = collection_h.is_transaction_date and
collection.is_stan_trans = collection_h.is_stan_trans;
Delete where exists
delete
from collection as c
where exists (
select 1
from collection_h as h
where h.pos_protocol_id = c.pos_protocol_id
and h.terminal_pos_number = c.terminal_pos_number
and h.cb_file_number = c.cb_file_number
and h.cb_block_number = c.cb_block_number
and h.is_stan_batch = c.is_stan_batch
and h.is_transaction_date = c.is_transaction_date
and h.is_stan_trans = c.is_stan_trans
);
Simplified test on db<>fiddle here
But if the columns can have NULL's in the matching rows
delete
from collection as c
where exists (
select 1
from collection_h as h
where decode(h.pos_protocol_id, c.pos_protocol_id, 0, 1) = 0
and decode(h.terminal_pos_number, c.terminal_pos_number, 0, 1) = 0
and decode(h.cb_file_number, c.cb_file_number, 0, 1) = 0
and decode(h.cb_block_number, c.cb_block_number, 0, 1) = 0
and decode(h.is_stan_batch, c.is_stan_batch, 0, 1) = 0
and decode(h.is_transaction_date, c.is_transaction_date, 0, 1) = 0
and decode(h.is_stan_trans, c.is_stan_trans, 0, 1) = 0
);
Delete x from collection X inner join collection_h Y on
X.pos_protocol_id = Y.pos_protocol_id and
X.terminal_pos_number = Y.terminal_pos_number and
X.cb_file_number = Y.cb_file_number and
X.cb_block_number = Y.cb_block_number and
X.is_stan_batch = Y.is_stan_batch and
X.is_transaction_date = Y.is_transaction_date and
X.is_stan_trans = Y.is_stan_trans;

CASE expression in WHERE clause for diferent and

Can I use case expression to build where like this?
select *
from table
where
case
when x=y then z= j and t=v
when x=k then q= p and s=l
end
;
I need change where clause depending on the value of x variable.
Use or:
select *
from table
where (x = y and z = j and t = v) or (x = k and q = p and s = l);
An alternative to using OR is to use nested CASE statements:
SELECT *
FROM table_name
WHERE CASE
WHEN x = y THEN CASE WHEN z = j AND t = v THEN 1 ELSE 0 END
WHEN x = k THEN CASE WHEN q = p AND s = l THEN 1 ELSE 0 END
ELSE 0
END = 1;
or you could simplify it to:
SELECT *
FROM table_name
WHERE CASE
WHEN x = y AND z = j AND t = v THEN 1
WHEN x = k AND q = p AND s = l THEN 1
ELSE 0
END = 1;
However, you should check whether Oracle can use column indexes or if a separate function-based index is required with these solutions.

My while loop is running forever by calculating and inserting the records into a table

I have created a while loop to check if my derived value is less than zero then enter into while loop, increment my Period ID to +1, pull the values from that period and run the logic again.
But my T SQL code is running forever with this logic with the 26000 records which is not a huge data.
Below is my code logic
SELECT #lclcurrentperiod = PERIOD_ID,
#snop_item = FGA_ID,
#lclfullfill_center = fulfillment_center,
#lcldepo_wise_sawtd = DEPOT_WISE_SAWTD,
#lclDemandType = DEMAND_TYPE,
#lclDepo_wise_final = DEPOT_WISE_FINAL,
#lcldepo_wise_forecast = DEPOT_WISE_FORECAST
FROM temp_calc (NOLOCK)
WHERE PERIOd_id IN (SELECT MIN(period_id)FROM temp_calc WHERE depot_wise_forecast > 0)
AND FGA_ID = #fga_id
AND fulfillment_center = #lclFulfillmentCenter
AND DEMAND_TYPE = 'TRANSACTIONAL';
SET #net_offset_forecast = #lclDepo_wise_forecast - #forecast_consumed_by_sales;
INSERT INTO NET_OFFSET_FORECAST (PERIOD_ID,
FGA_ID,
FULFILLMENT_CENTER,
DEMAND_TYPE,
LEGACY_SSC,
DEPOT_WISE_FORECAST,
CUM_FORECAST,
DRAFT_NET_FORECAST,
FORECAST_TO_BE_CONSUMED,
NET_OFFSET_FORECAST)
VALUES (#lclCurrentPeriod, #snop_item, #lclfullfill_center, #lclDemandType, #lclLegacySSC, #lclDepo_wise_forecast, #cum_forecast, #draft_net_forecast, #forecast_consumed_by_sales, #net_offset_forecast);
WHILE (#net_offset_forecast < 0)
BEGIN
SET #lclcurrentperiod = #lclcurrentperiod + 1;
SET #forecast_consumed_by_sales = ABS(#net_offset_forecast);
SELECT #lclcurrentperiod = PERIOD_ID,
#fga_id = FGA_ID,
#lclFulfillmentCenter = FULFILLMENT_CENTER,
#lclDepo_wise_forecast = DEPOT_WISE_FORECAST,
#lclDemandType = DEMAND_TYPE
FROM (SELECT *,
ROW_NUMBER() OVER (ORDER BY PERIOD_ID) AS RN
FROM TEMP_CALC
WHERE DEPOT_WISE_FORECAST > 0
AND FGA_ID = #fga_id
AND fulfillment_center = #lclFulfillmentCenter
AND DEMAND_TYPE = 'TRANSACTIONAL') t
WHERE RN = 1;
SET #net_offset_forecast = #lcldepo_wise_forecast - #forecast_consumed_by_sales;
INSERT INTO NET_OFFSET_FORECAST (PERIOD_ID,
FGA_ID,
FULFILLMENT_CENTER,
DEMAND_TYPE,
LEGACY_SSC,
DEPOT_WISE_FORECAST,
CUM_FORECAST,
DRAFT_NET_FORECAST,
FORECAST_TO_BE_CONSUMED,
NET_OFFSET_FORECAST)
VALUES (#lclCurrentPeriod, #snop_item, #lclfullfill_center, #lclDemandType, #lclLegacySSC, #lclDepo_wise_forecast, #cum_forecast, #draft_net_forecast, #forecast_consumed_by_sales, #net_offset_forecast);
END;
After deriving the net offset forecast it is going into while loop when the net offset forecast is less than zero and performing actions there and not coming up out with the data.
Need to improve the performance of the code to finish the loop and continue processing other data.

Understanding case expression in the "Where" clause

I've got this code here and you can see from my Pseudocode what I'm trying to accomplish
select *
from dbo.BenefitsForms
inner join Dependents on BenefitsForms.UserId = Dependents.BenefitsForm_UserId
inner join CoverageLevels on BenefitsForms.MedicalId = CoverageLevels.Id
where (BenefitsForms.MedicalId > 0 AND BenefitsForms.MedicalId < 13)
AND Dependents.IsSpouse = CASE when CoverageLevels.[Level] = 2 then 1
when CoverageLevels.[Level] = 3 then 0 end
when CoverageLevels.[Level] = 4 then [any, it doesnt matter] <--- my desire but it doesn't work.
What can I do to get the effect I desire in the brackets? If Coverage Level = 4 then I don't care what Dependents.IsSpouse is, I don't even need to sort by it anymore.
Assuming that isSpouse can only be 0 or 1... if CoverageLevels.Level is 4, then compare isSpouse to itself, which will always result in true:
AND Dependents.IsSpouse = CASE
when CoverageLevels.[Level] = 2 then 1
when CoverageLevels.[Level] = 3 then 0
when CoverageLevels.[Level] = 4 then Dependents.IsSpouse
END
Alternately, this can also be expressed without the CASE:
WHERE
BenefitsForms.MedicalId > 0
AND BenefitsForms.MedicalId < 13
AND (
(Dependents.IsSpouse = 1 AND CoverageLevels.[Level] = 2)
OR (Dependents.IsSpouse = 0 AND CoverageLevels.[Level] = 3)
OR CoverageLevels.[Level] = 4
)

SQL Counting Records with Count and Having

I'm having problems with what I thought was a simple query to count records:
SELECT req_ownerid, count(req_status_lender) AS total6
FROM bor_requests
WHERE (req_status_lender = 0 AND req_status_borrower = 0) OR
(req_status_lender = 1 AND req_status_borrower = 1)
GROUP BY req_ownerid
HAVING req_ownerid = 70
I thought this would count all the records where (req_status_lender = 0 AND req_status_borrower = 0) and (req_status_lender = 1 AND req_status_borrower = 1) and then give me the total but it only gives me the total for either (req_status_lender = 0 AND req_status_borrower = 0) or (req_status_lender = 1 AND req_status_borrower = 1).
Any ideas what I'm doing wrong?
You should use the HAVING clause only to limit on something that's been aggregated in your query above - e.g. if you want to select all those rows where a SUM(....) or COUNT(...) is larger than say 5, then you'd use HAVING SUM(...) > 5
What you're doing here is a standard WHERE clause - add it there!
SELECT req_ownerid, count(req_status_lender) AS total6
FROM bor_requests
WHERE req_ownerid = 70
AND ((req_status_lender = 0 AND req_status_borrower = 0) OR
(req_status_lender = 1 AND req_status_borrower = 1))
GROUP BY req_ownerid