sql Update specific rows from select query - sql

Hello I need some awful stored procedure and I cannot think of such
I've got these tables
Criterias
CriteriaId
Title
.......
Table StepCriterias
StepID
CriteriaId
OrderNum
Table Steps
StepID
ProcedureID
Title
...
So I select all the StepCriterias of a step with particular name and particular number
SELECT *
FROM StepCriterias sc
INNER JOIN Steps s ON sc.StepId = s.StepId
WHERE
s.Title = 'MYteststep'
ANd s.ProcedureId = 2
So suppose this query returns me five rows; now I should UPDATE all the resulting rows and their OrderNum should be incremented by 1 (I should increase the OrderNum of each row). That's my first problem and I cannot think of a stored procedure that an make that.

You could replace the join logic with an exsits condition in an update statement:
UPDATE StepCriterias sc
SET OrderNum = OrderNum + 1
WHERE EXISTS (SELECT *
FROM steps s
WHERE sc.StepId = s.StepId AND
s.Title = 'MYteststep' AND
s.ProcedureId = 2)

Probably this way .
UPDATE sc
SET OrderNum = OrderNum + 1
FROM StepCriterias sc
INNER JOIN steps s ON sc.StepId = s.StepId
WHERE s.Title = 'MYteststep'
AND s.ProcedureId = 2

In the event that you want an increment for each match, and a stepcriteria could match more than once, then:
UPDATE StepCriterias sc
SET OrderNum = (OrderNum +
(SELECT COUNT(*)
FROM steps s
WHERE sc.StepId = s.StepId AND
s.Title = 'MYteststep' AND
s.ProcedureId = 2
)
WHERE EXISTS (SELECT 1
FROM steps s
WHERE sc.StepId = s.StepId AND
s.Title = 'MYteststep' AND
s.ProcedureId = 2
);
There are simpler ways to express this in most databases, but the above is general syntax that should work in any database.

You can use UPDATE with a subquery, something like:
UPDATE StepCriterias
SET OrderNum = sc.OrderNum + 1
WHERE StepId IN (SELECT StepId
FROM StepCriterias sc
INNER JOIN steps s ON sc.StepId = s.StepId
WHERE s.Title = 'MYteststep' AND s.ProcedureId = 2);

Related

Trying to update a field conditionally in SQL Stored Procedure

I have a procedure that populates two sets of application information into the same fields. First the fields are filled out with applicable accounts from group "A" and then the same process happens for group "B" accounts.
Most of the group B fields are filled in by a insert/select statement. However, the query to select "account number" is a little more complex and that is in an UPDATE statement. I will paste the code below but I cannot get it to properly update the rows (for group B) with account numbers, despite the fact the query works on its own outside the procedure (essentially, the account numbers do exist).
Any idea why? I tried adding a case statement to single out group B rows (the where clause is hardcoded for group B... e.g. clfcode = 3) but that didn't work. Let me know if you need more information. I haven't much experience with update statements in stored procedures.
update src
set account_key = case when src.clfcode = 3 and src.branch_key = 12 then a.account_key else src.account_key end
from #src_table src
inner join SDFDW_Landing.cu.FICS_ms_Investor_Loan l
on l.loan_id = src.application_number
left join dm.dim_product p
on p.product_key = src.product_key
left join (
Select Distinct t.PARENTACCOUNT, t.USERCHAR1 as loan_id
from SDFDW_Landing.dbo.tracking t
where t.TYPE = 1
and t.ProcessDate = #v_max_last_processed_date
and t.USERCHAR1 is not null
) t on t.loan_id = l.loan_id
left join dm.dim_account a
on t.PARENTACCOUNT = a.account_nkey
WHERE p.bdw_report_category = 'Mortgage'
and l.processdate = #v_max_last_processed_date
The join on a subquery might cause the issue. You could try to replace it with an apply and see if that helps.
update
src
set
account_key =
case
when
src.clfcode = 3
and src.branch_key = 12
then
a.account_key
else
src.account_key
end
from
#src_table src
inner join
SDFDW_Landing.cu.FICS_ms_Investor_Loan l
on l.loan_id = src.application_number
left join
dm.dim_product p
on p.product_key = src.product_key
outer apply (
Select
acc.*
from
dm.dim_account acc
inner join
SDFDW_Landing.dbo.tracking t
on acc.account_nkey = t.parentaccount
where
t.TYPE = 1
and t.ProcessDate = #v_max_last_processed_date
and t.USERCHAR1 is not null
and t.loan_id = l.loan_id
) a
WHERE
p.bdw_report_category = 'Mortgage'
and l.processdate = #v_max_last_processed_date
alternatively since you are already within a stored procedure, I'd populate a temp table with the data from your subquery and simply join on that temp table from your update statement.

Adding value of COUNT to an already existing value

I have a table for managing inventory movements and a table to manage stock.
I want to add the count of movements that the item exists in between given dates to the stock table.
My update statement looks like this:
UPDATE inventory
SET quantity = quantity + 1
WHERE ItemID IN
( SELECT ItemID FROM movements
WHERE group = '3' AND store = '500'
AND DateMove BETWEEN 20201219 AND 20201223 )
AND StoreNumber = '500'
How can I change this query to add the amount of times that the ItemID appears in movements to the quantity in the inventory table?
I've been thinking that I can add a count(itemID) and group by and add an alias in the subquery and use the alias after the + but it doesn't seem to work.
Thanks for any help
Using an UPDATE, you can use a correlated subquery:
UPDATE inventory i
SET quantity = (i.quantity +
(SELECT COUNT(*)
FROM movements m
WHERE m.ItemId = i.ItemId AND
m.group = 3 AND m.store = i.store AND
m.DateMove BETWEEN 20201219 AND 20201223
)
)
WHERE i.store = 500 AND
EXISTS (SELECT 1
FROM movements m
WHERE m.ItemId = i.ItemId AND
m.group = 3 AND m.store = 500 AND
m.DateMove BETWEEN 20201219 AND 20201223
);
Note that I removed the single quotes around 500 and 3. These values look like numbers. Only use the single quotes if they are strings.
Oracle also allows you to update using a subquery under some circumstances, so this should work as well:
update (select i.*,
(SELECT COUNT(*)
FROM movements m
WHERE m.ItemId = i.ItemId AND
m.group = 3 AND m.store = i.store AND
m.DateMove BETWEEN 20201219 AND 20201223
) as inc_quantity
from inventory i
where store = 500
)
set quantity = quantity + inc_quantity
where quantity > 0;
You appear to want a MERGE statement and to COUNT the movements:
MERGE INTO inventory dst
USING (
SELECT ItemID,
store,
COUNT(*) AS num_movements
FROM movements
WHERE group = 3
AND store = 500
AND DateMove BETWEEN DATE '2020-12-19' AND DATE '2020-12-23'
GROUP BY
ItemId,
store
) src
ON ( src.ItemId = dst.ItemId AND dst.StoreNumber = src.store )
WHEN MATCHED THEN
UPDATE
SET quantity = dst.quantity + src.num_movements;
(Also, if values are numbers then use number literals and not string literals and, for dates, use date literals and not numbers.)
You need a correlated subquery. For brevity, I've omitted all other where conditions.
UPDATE inventory AS inv
SET quantity = quantity + (SELECT COUNT(*) FROM movements AS mov WHERE mov.itemID = inv.itemID);
I think it's better to create a view such as
CREATE OR REPLACE VIEW v_inventory AS
SELECT i.*,
SUM(CASE
WHEN i.StoreNumber = 500 AND m."group" = 3 AND m.store = 500 AND
m.DateMove BETWEEN date'2020-12-19' AND date'2020-12-23'
THEN
1
ELSE
0
END) OVER (PARTITION BY m.ItemID) AS mov_quantity
FROM inventory i
LEFT JOIN movements m
ON m.ItemID = i.ItemID
rather than applying a DML for the sake of good db design, since the desired count already can be calculated, and may yield for later confusions

Multiple Joins less cost way

Below query has 3 tables where I have to do 2 joins to get a column information, It is very slow, is there any effective way to run this query?
SELECT DISTINCT
st.status_c1
FROM
schemaname.tablea st
INNER JOIN (
SELECT
lic.SpecId AS applicationid,
lic.comData AS combusappid,
lic.ageId,
lic.licId,
lic.licid,
lic.appid,
com.nybe_bustbl_id AS busid
FROM
schemaname.tableb lic
INNER JOIN tablec com ON lic.comData = com.comData
WHERE
lic.ageId = '12'
) rt ON
st.ageId = rt.ageId
AND
st.licId = rt.licId
AND
st.licid = rt.licid
AND
st.appid = rt.appid
WHERE
status_id = 3;
Your current query will create extra rows when the JOIN condition is met for multiple entries in either table and then DISTINCT will filter these duplicates out. You could try to cut down the amount of work filtering duplicates by using EXISTS:
SELECT DISTINCT
st.status_c1
FROM schemaname.tablea st
WHERE status_id = 3
AND EXISTS (
SELECT 1
FROM schemaname.tableb lic
WHERE lic.ageId = '12'
AND st.ageId = lic.ageId
AND st.licId = lic.licId
AND st.appid = lic.appid
AND EXISTS(
SELECT 1 FROM tablec com WHERE lic.comData = com.comData
)
);
There is a bunch of redundancy in the query (licid is in the SELECT and ON twice) and you don't need to use subqueries for this. I think this will work:
SELECT DISTINCT st.status_c1
FROM tablea st
INNER JOIN tableb lic ON st.ageId = lic.ageId
AND st.licId = lic.licId
AND st.appid = lic.appid
INNER JOIN tablec com ON lic.comData = com.comData
WHERE status_id = 3
and lic.ageId = '12'
How frequently are you going to run this query, how much time is it taking now and what is the explectation. Are statistcs run on all tha tables.
There are many things which we can think of, but to start with if possible could you plese give ue the like the table structure and explain plan of the query.
Also may be an index on status_c1 table tablea help. As pointed out try removing the join condition which is twice AND st.licid = rt.licid
SELECT DISTINCT st.status_c1
FROM schemaname.tablea st
INNER JOIN (
SELECT
lic.SpecId AS applicationid, lic.comData AS combusappid, lic.ageId, lic.licId, lic.licid,
lic.appid, com.nybe_bustbl_id AS busid
FROM schemaname.tableb lic
INNER JOIN tablec com ON lic.comData = com.comData
WHERE lic.ageId = '12'
) rt ON st.ageId = rt.ageId AND st.licId = rt.licId AND st.licid = rt.licid AND st.appid = rt.appid
WHERE status_id = 3;

Slowness in update query using inner join

I am using the below query to update one column based on the conditions it is specified. I am using "inner join" but it is taking more than 15 seconds to run the query even if it has to update no records(0 records).
UPDATE CONFIGURATION_LIST
SET DUPLICATE_SERIAL_NUM = 0
FROM CONFIGURATION_LIST
INNER JOIN (SELECT DISTINCT APPLIED_MAT_CODE, APPLIED_SERIAL_NUMBER, COUNT(*) AS NB
FROM CONFIGURATION_LIST
WHERE
PLANT = '0067'
AND APPLIED_SERIAL_NUMBER IS NOT NULL
AND APPLIED_SERIAL_NUMBER !=''
AND DUPLICATE_SERIAL_NUM = 1
GROUP BY
APPLIED_MAT_CODE, APPLIED_SERIAL_NUMBER
HAVING
COUNT(*) = 1) T2 ON T2.APPLIED_SERIAL_NUMBER = CONFIGURATION_LIST.APPLIED_SERIAL_NUMBER
AND T2.APPLIED_MAT_CODE = CONFIGURATION_LIST.APPLIED_MAT_CODE
WHERE
CONFIGURATION_LIST.PLANT = '0067'
AND DUPLICATE_SERIAL_NUM = 1
The index is there with APPLIED_SERIAL_NUMBER and APPLIED_MAT_CODE and fragmentation is also fine.
Could you please help me on the above query performance.
First, you don't need the DISTINCT when using GROUP BY. SQL Server probably ignores it, but it is a bad idea anyway:
UPDATE CONFIGURATION_LIST
SET DUPLICATE_SERIAL_NUM = 0
FROM CONFIGURATION_LIST INNER JOIN
(SELECT APPLIED_MAT_CODE, APPLIED_SERIAL_NUMBER, COUNT(*) AS NB
FROM CONFIGURATION_LIST cl
WHERE cl.PLANT = '0067' AND
cl.APPLIED_SERIAL_NUMBER IS NOT NULL AND
cl.APPLIED_SERIAL_NUMBER <> ''
cl.DUPLICATE_SERIAL_NUM = 1
GROUP BY cl.APPLIED_MAT_CODE, cl.APPLIED_SERIAL_NUMBER
HAVING COUNT(*) = 1
) T2
ON T2.APPLIED_SERIAL_NUMBER = CONFIGURATION_LIST.APPLIED_SERIAL_NUMBER AND
T2.APPLIED_MAT_CODE = CONFIGURATION_LIST.APPLIED_MAT_CODE
WHERE CONFIGURATION_LIST.PLANT = '0067' AND
DUPLICATE_SERIAL_NUM = 1;
For this query, you want the following index: CONFIGURATION_LIST(PLANT, DUPLICATE_SERIAL_NUM, APPLIED_SERIAL_NUMBER, APPLIED_MAT_CODE, APPLIED_SERIAL_NUMBER).
The HAVING COUNT(*) = 1 suggests that you might really want NOT EXISTS (which would normally be faster). But you don't really explain what the query is supposed to be doing, you only say that this code is slow.
Looks like you're checking the table for rows that exist in the same table with the same values, and if not, update the duplicate column to zero. If your table has a unique key (identity field or composite key), you could do something like this:
UPDATE C
SET C.DUPLICATE_SERIAL_NUM = 0
FROM
CONFIGURATION_LIST C
where
not exists (
select
1
FROM
CONFIGURATION_LIST C2
where
C2.APPLIED_SERIAL_NUMBER = C.APPLIED_SERIAL_NUMBER and
C2.APPLIED_MAT_CODE = C.APPLIED_MAT_CODE and
C2.UNIQUE_KEY_HERE != C.UNIQUE_KEY_HERE
) and
C.PLANT = '0067' and
C.DUPLICATE_SERIAL_NUM = 1
I will try with a select first:
select APPLIED_MAT_CODE, APPLIED_SERIAL_NUMBER, count(*) as n
from CONFIGURATION_LIST cl
where
cl.PLANT='0067' and
cl.APPLIED_SERIAL_NUMBER IS NOT NULL and
cl.APPLIED_SERIAL_NUMBER <> ''
group by APPLIED_MAT_CODE, APPLIED_SERIAL_NUMBER;
How many rows do you get with this and how long does it take?
If you remove your DUPLICATE_SERIAL_NUM column from your table it might be very simple. The DUPLICATE_SERIAL_NUM suggests that you are searching for duplicates. As you count your rows you could introduce a simple table that contains the counts:
create table CLCOUNT ( N int unsigned, C int /* or what APPLIED_MAT_CODE is */, S int /* or what APPLIED_SERIAL_NUMBER is */, PLANT char(20) /* or what PLANT is */, index unique (C,S,PLANT), index(PLANT,N));
insert into CLCOUNT select count(*), cl.APPLIED_MAT_CODE, cl.APPLIED_SERIAL_NUMBER, cl.PLANT
from CONFIGURATION_LIST cl
where
cl.PLANT='0067' and
cl.APPLIED_SERIAL_NUMBER IS NOT NULL and
cl.APPLIED_SERIAL_NUMBER <> ''
group by APPLIED_MAT_CODE, APPLIED_SERIAL_NUMBER;
How long does this take?
Now you can simply select * from CLCOUNT where PLANT='0067' and N=1;
This is all far from being perfect. But you should be able to analyze (EXPLAIN SELECT ...) your queries and find why it takes so long.

How to rewrite a SQL query that contains repeated SubQuery?

Could anyone help me to rewrite a SQL query as below in short ? There is a subquery to be repeated.
update policy
set totalvehicles = (
select count(*) from riskunit
where riskunit.policyId = policy.id
and riskunit.subtype = 7)
where policy.verified = '1'
and policy.Totalvehicles <(
select count(*)
from riskunit
where riskunit.policyId = policy.id
and riskunit.subtype = 7
);
Thanks !!
I prefer this because it's easy to insert a select above the from and see what would be changed.
UPDATE p
SET totalvehicles = cnt.[Count]
FROM policy p
INNER JOIN (
SELECT
policyId,COUNT(*) [Count]
FROM riskunit
WHERE ru.subtype = 7
GROUP BY policyId
) cnt on cnt.policyId=p.policyId
WHERE p.verified = '1'
AND p.Totalvehicles < cnt.[Count]
This should work (assuming MySQL, also working for Oracle):
update policy p
inner join (
select policyId, count(*) as n from riskunit
where riskunit.subtype = 7
group by policyId
) ru on ru.policyId = p.id
set p.totalvehicles = ru.n
where p.verified = '1'
and p.Totalvehicles < ru.n;