Trying to create a stored procedure in SQL 2008R2.
I have two Parameter inputs, that depending on their input, will ideally set another parameter's value to one of four values. Trying to determine if this can done, and if so, where is the best place to put it in the SP.
Here is a slimmed down version of my code.
Declare #FWIdle as bYN, #NewComp as bYN
set #FW = 'Y'
set #NEW = 'Y'
select Case(
when w.WearRate <0.400 then round((- 50*w.WearRate+112.5),2
when w.WearRate <0.450 then round((-100*w.WearRate+132.5),2)
when w.WearRate <0.550 then round((- 50*w.WearRate+110.5),2)
else 52.5 END as Score
from EMWear w
Hoping to replace all w.WearRate with #Wear and setting #Wear to one of four possible fields in the view, based on the combination of the two parameters.
So something like:
if #FW = 'Y' and #NEW = 'N' then #Wear = w.FWRate else
if #FW = 'N' and #NEW = 'Y' then #Wear = w.NewRate else
if #FW = 'Y' and #NEW = 'Y' then #Wear = w.FWNewRate else
#Wear = w.WearRate
Since I have 150+ lines of code that deals with this rate, I would prefer not to have to write this code 4 times to get the desired Value. Any help on how I can write this and keep it clean/simple would be greatly appreciated.
You can reduce all of your code to something like this.
SELECT w.EMCo, w.Component,w.CompType, CASE
WHEN #FW = 'Y' AND #NEW = 'Y' THEN w.FwIdleWearRate
WHEN #FW = 'Y' AND #NEW = 'N' THEN w.FwWearRate
WHEN #FW = 'N' AND #NEW = 'Y' THEN w.IdleWearRate
ELSE w.StdWearRate
END AS WearRate
FROM bcvEMCompWearAdj w
The solution to my problem ended up being to create a temp table and insert the WearRate that I wanted to use.
If OBJECT_ID('tempdb.dbo.#TempWear','U') IS NOT NULL
Drop Table #TempWear;
Create Table #TempWear
EMCo tinyint Null,
Component varchar(10) NULL,
CompType varchar(10) NULL,
WearRate decimal(38,5) NULL
);
if #FW = 'Y' and #New = 'Y'
Begin
Insert into #TempWear
(EMCo, Component, CompType, WearRate)
select w.EMCo, w.Component,w.CompType, w.FwIdleWearRate
from bcvEMCompWearAdj w
END
else
if #FW = 'Y' and #New = 'N'
Begin
Insert into #TempWear
(EMCo, Component, CompType, WearRate)
select w.EMCo, w.Component,w.CompType, w.FwWearRate
from bcvEMCompWearAdj w
END
else
if #FW = 'N' and #New = 'Y'
Begin
Insert into #TempWear
(EMCo, Component, CompType, WearRate)
sselect w.EMCo, w.Component,w.CompType, w.IdleWearRate
from bcvEMCompWearAdj w
END
else
--if #FW = 'N' and #New = 'N'
Begin
Insert into #TempWear
(EMCo, Component, CompType, WearRate)
select w.EMCo, w.Component,w.CompType, w.StdWearRate
from bcvEMCompWearAdj w
END
Related
I am working on a Stored Procedure that has two input parameters that I need to use COLLATE SQL_Latin1_General_CP1_CI_AS so that I can query non-case sensitive results; however, I would also like to allow for a Show All Option with the inputs. I have not found a way for the Collate to work in a case statement. Note: As a work around I will code with 4 different If statements for the different scenarios of choosing a value and/or show all, but would like something cleaner if possible. Current Code below
Declare
#INCo as bCompany,
#MatBeg as bMatl,
#MatEnd as bMatl,
#Catg as bGroup,
#Model as VarChar(20),
#PatternM as VarChar(20),
#SellOn as VarChar(20),
#PatternS as VarChar(20),
#ShowZero as bYN
set #INCo = '1'
set #MatBeg = '0'
set #MatEnd = 'ZZZZZZZZZZ'
set #Catg = '0'
set #Model = '637'
set #SellOn = 'EBAY'
set #ShowZero = 'Y'
begin
set #PatternM = '%' + #Model + '%';
set #PatternS = '%' + #SellOn + '%';
select i.INCo, i.Material, h.Description, h.Category, c.Description as CatgDesc, i.Booked as Quantity, i.PhyLoc, p.Storage,
i.udModelFit as FitsModel, i.udSellPriceNew as LikeNewSellPrice, i.udSellPriceUsed as UsedSellPrice, i.udSellingOn as SellingOn
from INMT i
left outer join HQMT h on i.MatlGroup=h.MatlGroup and i.Material=h.Material
left outer join HQMC c on h.MatlGroup=c.MatlGroup and h.Category=c.Category
left outer join udPhysicalloc p on i.PhyLoc=p.Physicalloc
where i.INCo = (CASE when #INCo <> 0 then #INCo else i.INCo END)
and i.Material >= #MatBeg and i.Material <= #MatEnd
and c.Category = (CASE when #Catg <> 0 then #Catg else c.Category END)
and i.udModelFit COLLATE SQL_Latin1_General_CP1_CI_AS like #PatternM
and i.udSellingOn COLLATE SQL_Latin1_General_CP1_CI_AS like #PatternS
and i.Booked >= (CASE when #ShowZero = 'N' then 1 else 0 END)
END
This code works works great if I only care about having non-case sensitive results for #Model and #SellOn. However, like the other parameters I have, I would like to include something that allows to show all results for that parameter. Something like:
i.udModelFit = (CASE when #Model <> 'ALL' then COLLATE SQL_Latin1_General_CP1_CI_AS like #PatternM else i.udModelFit END)
So as an example I would like to input #Model = 'ALL' and #SellOn = 'eBay' and results would be any Model with Sell On = EBAY (non-case sensitive)
Update: I was able to modify Where to the following and I am getting the desired results now.
where i.INCo = (CASE when #INCo <> 0 then #INCo else i.INCo END)
and i.Material >= #MatBeg and i.Material <= #MatEnd
and c.Category = (CASE when #Catg <> 0 then #Catg else c.Category END)
and (
(#Model IS NULL or i.udModelFit COLLATE SQL_Latin1_General_CP1_CI_AS like #PatternM)
or ((i.udModelFit like '%' or i.udModelFit IS NULL) and #Model = 'ALL')
)
and (
(#SellOn IS NULL or i.udSellingOn COLLATE SQL_Latin1_General_CP1_CI_AS like #PatternS)
or ((i.udSellingOn like '%' or i.udSellingOn IS NULL) and #SellOn = 'ALL')
)
and i.Booked >= (CASE when #ShowZero = 'N' then 1 else 0 END)
You can use and( (...) or (...) ) e.g.
and (#Model = 'ALL' or i.udModelFit like #PatternM SQL_Latin1_General_CP1_CI_AS)
If you want to toggle different search options based on input, you may want to consider writing your procedure using dynamic sql, and/or adding option (recompile).
Catch-all query reference:
Parameter Sniffing, Embedding, and the RECOMPILE Options - Paul White
Dynamic Search Conditions - Erland Sommarskog
Catch-all queries - Gail Shaw
An Updated "Kitchen Sink" Example - Aaron Bertand
I am using function to update to one column , like
DetailedStatus = dbo.fn_GetProcessStageWiseStatus(PR.ProcessID, PR.ProcessRunID, getdate())
Here 500,000 records are continuously UPDATED in this line. Its like like a loop
So using this function for few records its executing fast but when its 500,000 records executing it becomes very slow...
What can I do to make this execute faster using many records?
Any measures to be taken or any split to be used?
Function:
CREATE FUNCTION [dbo].[fn_GetProcessStageWiseStatus]
(
#ProcessID INT
,#ProcessRunID INT
,#SearchDate SMALLDATETIME
)
RETURNS VARCHAR(100)
AS
BEGIN
DECLARE
#iLoopCount SMALLINT
,#iRowCount SMALLINT
,#StepProgress VARCHAR(100)
,#StepCount SMALLINT
IF EXISTS(
SELECT TOP 1 1
FROM dbo.Step S WITH(NOLOCK)
JOIN dbo.vw_FileGroup FG
ON S.FileConfigGroupID = FG.FileConfigGroupID
WHERE S.ProcessID = #ProcessID
AND S.Active = 1
AND FG.FileConfigGroupActive = 1
AND FG.Direction = 'Inbound'
)
BEGIN
SET #StepProgress = 'Not Received'
END
ELSE
BEGIN
SET #StepProgress = 'Not Started'
END
DECLARE #StepRunDetailsTable TABLE
(
KeyNo INT IDENTITY(1,1)
,StepID INT
,StepStartTime SMALLDATETIME
,StepEndTime SMALLDATETIME
,SourceEnv VARCHAR(100)
,DestEnv VARCHAR(100)
)
INSERT INTO #StepRunDetailsTable
SELECT
S.StepID
,MAX(isnull(SR.StepStartTime, '06/06/2079'))
,MAX(isnull(SR.StepEndTime, '06/06/2079'))
,isnull(SENV.EnvironmentName, '')
,isnull(DENV.EnvironmentName, '')
FROM dbo.ProcessRun PR WITH(NOLOCK)
JOIN dbo.StepRun SR WITH(NOLOCK)
ON SR.ProcessRunID = PR.ProcessRunID
JOIN dbo.vw_StepHierarchy SH
ON SR.StepID = SH.StepID
AND SH.Active = 1
JOIN dbo.Step S WITH(NOLOCK)
ON SH.StepID = S.StepID
JOIN dbo.WorkFlow WF WITH(NOLOCK)
ON S.WorkFlowID = WF.WorkFlowID
AND WF.Active = 1
JOIN dbo.Environment SENV WITH(NOLOCK)
ON SENV.EnvironmentID = WF.SourceEnvironmentID
AND SENV.Active = 1
JOIN dbo.Environment DENV WITH(NOLOCK)
ON DENV.EnvironmentID = WF.DestinationEnvironmentID
AND DENV.Active = 1
WHERE PR.ProcessRunID = #ProcessRunID
GROUP BY S.StepID, SENV.EnvironmentName, DENV.EnvironmentName, SH.StepOrder
ORDER BY SH.StepOrder ASC
SELECT #StepCount = COUNT(*)
FROM dbo.ProcessRun PR WITH(NOLOCK)
JOIN dbo.Step S WITH(NOLOCK)
ON PR.ProcessID = S.ProcessID
AND PR.ProcessRunID = #ProcessRunID
AND S.Active = 1
SELECT #iRowCount = COUNT(DISTINCT StepID) FROM #StepRunDetailsTable
SET #iLoopCount = 0
WHILE (#iRowCount > #iLoopCount)
BEGIN
SET #iLoopCount = #iLoopCount + 1
SELECT
#StepProgress =
CASE
--WHEN #SearchDate BETWEEN StepStartTime AND StepEndTime
WHEN #SearchDate >= StepStartTime AND #SearchDate <= StepEndTime
THEN DestEnv + ' Load in Progress'
WHEN #SearchDate > StepEndTime AND #iLoopCount < #StepCount
THEN 'Waiting on next step - Loaded to ' + DestEnv
WHEN #SearchDate > StepEndTime AND #iLoopCount = #StepCount
THEN 'Completed'
WHEN #SearchDate < StepStartTime AND #iLoopCount = 1
THEN 'Load Not Started'
ELSE #StepProgress
END
FROM #StepRunDetailsTable
WHERE KeyNo = #iLoopCount
END
RETURN #StepProgress
END
Thanks in advance.
Seems like you have a change in execution plan when you try to update 500k rows.
You can try and set forceseek hint on the from clause to force using seeks instead of scans.
Also, WHILE (#iRowCount > #iLoopCount) should be replaced with if exists, because you basically check for certain conditions on the results table and you need to return as early as possible.
I see that you use nolock hint everywhere to allow dirty reads, you can set isolation level read uncommitted in the calling stored procedure and remove all of those; or consider to change the database to set read_committed_snapshot on to avoid locks.
By the way, scalar function calls in SQL Server are very expensive, so if you have some massive updates/selects happening in a loop where you call a function you have to avoid using functions as much as possible.
I want to update multiple columns using case statement, I achieved this but it is not a best way i have to do same task three times, how can i achieve in one statement here is my test sql script:
Delimiter //
create procedure test_f()
begin
update test set
#### total####
test.total = case when test.currencyid='INR' then test.total/85.09
Else test.total End,
test.total = case when test.currencyid='SEK' then test.total/8.97
Else test.total End,
### Commission ####
test.commission = case when test.currencyid='INR' then test.commission/85.09
Else test.commission End,
test.commission = case when test.currencyid='SEK' then test.commission/8.97
Else test.commission End,
test.currencyid = case when test.currencyid in ('SEK','INR') then 'EUR'
Else test.currencyid End
WHERE test.currencyid in ('SEK','INR') ;
END //
Now i want to update all three columns altogether based on currencyid.
You could try this for the first case:
instead of:
update test set
#### total####
test.total = case when test.currencyid='INR' then test.total/85.09
Else test.total End,
test.total = case when test.currencyid='SEK' then test.total/8.97
Else test.total End,
change to:
update test set
#### total####
test.total =
case when test.currencyid='INR' then test.total/85.09
when test.currencyid='SEK' then test.total/8.97
Else test.total
End,
do the same for ### Commission ####
I would be inclined to write the query as:
update test t
set t.total = t.total / (case when t.currencyid = 'INR' then 85.09 else 8.97 end),
t.commission = t.commission / (case when t.currencyid = 'INR' then 85.09 else 8.97 end),
t.currency = 'EUR'
where t.currencyid in ('INR', 'SEK') ;
The where clause limits the currencies to the two mentioned, so the case only has to test for those two conditions. Simplifying the case statement also makes it clearer that the same logic is being used for both calculations.
I have an after insert trigger on a table that is to update fields on another table based on the newly inserted row. (Its deducting stock qty in one location and adding to another location. Each column is a distict stock keeping location)
The exact field to be updated is determined from a select statment. Essentially
UPDATE stock SET FIELD1=FIELD1+NEW.stquantity, FIELD2=FIELD2-NEW.stquantity
WHERE FIELD1 and FIELD2 are determined by the case statement
The trigger I have looks like this now:
CREATE TRIGGER `changestockqty` AFTER INSERT ON `stocktransfer_items` FOR EACH ROW
BEGIN
DECLARE fromstocklocationvar INT DEFAULT -1;
DECLARE tostocklocationvar INT DEFAULT -1;
SET fromstocklocationvar = (SELECT stsource_location FROM stocktransfers WHERE stocktransfer.idstocktransfers=NEW.idstocktransfers);
SET tostocklocationvar = (SELECT stdest_location FROM stocktransfers WHERE stocktransfer.idstocktransfers=NEW.idstocktransfers);
UPDATE stock SET
CASE
WHEN fromstocklocationvar=1 THEN sl1
WHEN fromstocklocationvar=2 THEN sl2
WHEN fromstocklocationvar=3 THEN sl3
WHEN fromstocklocationvar=4 THEN sl4
WHEN fromstocklocationvar=5 THEN sl5
END
=
CASE
WHEN fromstocklocationvar=1 THEN sl1
WHEN fromstocklocationvar=2 THEN sl2
WHEN fromstocklocationvar=3 THEN sl3
WHEN fromstocklocationvar=4 THEN sl4
WHEN fromstocklocationvar=5 THEN sl5
END
-NEW.stquantity,
CASE
WHEN tostocklocationvar=1 THEN sl1
WHEN tostocklocationvar=2 THEN sl2
WHEN tostocklocationvar=3 THEN sl3
WHEN tostocklocationvar=4 THEN sl4
WHEN tostocklocationvar=5 THEN sl5
END
=
CASE
WHEN tostocklocationvar=1 THEN sl1
WHEN tostocklocationvar=2 THEN sl2
WHEN tostocklocationvar=3 THEN sl3
WHEN tostocklocationvar=4 THEN sl4
WHEN tostocklocationvar=5 THEN sl5
END
+NEW.stquantity,
WHERE stock.idstockpcode=NEW.stpcode;
END
It shows an error after the first case. What am I doing wrong?
Probably there is a syntax error (you try to use CASE statements to create another statement)..
Try this SQL (does the same thing):
CREATE TRIGGER `changestockqty` AFTER INSERT ON `stocktransfer_items` FOR EACH ROW
BEGIN
DECLARE fromstocklocationvar INT DEFAULT -1;
DECLARE tostocklocationvar INT DEFAULT -1;
SET fromstocklocationvar = (SELECT stsource_location FROM stocktransfers WHERE stocktransfer.idstocktransfers=NEW.idstocktransfers);
SET tostocklocationvar = (SELECT stdest_location FROM stocktransfers WHERE stocktransfer.idstocktransfers=NEW.idstocktransfers);
UPDATE stock SET
sl1 = CASE WHEN fromstocklocationvar=1 THEN sl1-NEW.stquantity ELSE sl1 END,
sl2 = CASE WHEN fromstocklocationvar=2 THEN sl2-NEW.stquantity ELSE sl2 END,
sl3 = CASE WHEN fromstocklocationvar=3 THEN sl3-NEW.stquantity ELSE sl3 END,
sl4 = CASE WHEN fromstocklocationvar=4 THEN sl4-NEW.stquantity ELSE sl4 END,
sl5 = CASE WHEN fromstocklocationvar=5 THEN sl5-NEW.stquantity ELSE sl5 END
,
sl1 = CASE WHEN tostocklocationvar=1 THEN sl1+NEW.stquantity ELSE sl1 END,
sl2 = CASE WHEN tostocklocationvar=2 THEN sl2+NEW.stquantity ELSE sl2 END,
sl3 = CASE WHEN tostocklocationvar=3 THEN sl3+NEW.stquantity ELSE sl3 END,
sl4 = CASE WHEN tostocklocationvar=4 THEN sl4+NEW.stquantity ELSE sl4 END,
sl5 = CASE WHEN tostocklocationvar=5 THEN sl5+NEW.stquantity ELSE sl5 END
WHERE stock.idstockpcode=NEW.stpcode;
END
You can't do it that way. The left side of Update have to be fixed.
Here is an example:
UPDATE stock SET
sl1=sl1 - CASE WHEN fromstocklocationvar=1 THEN NEW.stquantity ELSE 0 END
+ CASE WHEN tostocklocationvar=1 THEN NEW.stquantity ELSE 0 END,
sl2=sl2 - CASE WHEN fromstocklocationvar=2 THEN NEW.stquantity ELSE 0 END
+ CASE WHEN tostocklocationvar=2 THEN NEW.stquantity ELSE 0 END,
sl3=sl3 - CASE WHEN fromstocklocationvar=3 THEN NEW.stquantity ELSE 0 END
+ CASE WHEN tostocklocationvar=3 THEN NEW.stquantity ELSE 0 END,
sl4=sl4 - CASE WHEN fromstocklocationvar=4 THEN NEW.stquantity ELSE 0 END
+ CASE WHEN tostocklocationvar=4 THEN NEW.stquantity ELSE 0 END,
sl5=sl5 - CASE WHEN fromstocklocationvar=5 THEN NEW.stquantity ELSE 0 END
+ CASE WHEN tostocklocationvar=5 THEN NEW.stquantity ELSE 0 END
WHERE stock.idstockpcode=NEW.stpcode;
Is it possible to change these 2 queries into 1?
Query #1:
UPDATE `pvdownloader`.`posts`
SET `exists` = 'y'
WHERE source = ANY (SELECT source FROM dbdownloader.posts)
AND `mangacount` = 0
AND NOT `exists` = 'm'
Query #2:
UPDATE `pvdownloader`.`posts`
SET `exists` = 'n'
WHERE `exists` = 'u'
AND `mangacount` = 0
I'm assuming it would be possible to make something like this with IF/ELSE but I can't figure out how to use it at all :<
Maybe something like this would work:
update
pvdownloader.posts
set
exists =
case
when source = any(select source from dbdownloader.posts) and mangacount = 0 and exists != 'm' then 'y'
when exists = 'u' and mangacount = 0 then 'n'
end
where
(source = any(select source from dbdownloader.posts) and mangacount = 0 and exists != 'm')
or
(exists = 'u' and mangacount = 0)
;
As #MostyMostacho suggested, I took the liberty of changing your logic to not have a double-negative.