Related
I just dump data from production server to my local server for testing purpose. Our app uses a stored procedure with about 300 lines of sql. This procedure work fine in the server database, but it got stuck (running forever) every times i run it on my local database. Does anyone have any ideas about why it got stuck.
This is my command to dump data.
pg_dump --host localhost --port 5432 --username postgres --dbname test-new >D:\test5.sql
psql --host localhost --port 5432 --username postgres --dbname test-qa < file.sql
here is the Store Procedure
CREATE OR REPLACE FUNCTION public.insert_hm_ticket_statistic(v_sprint_id bigint)
RETURNS integer
LANGUAGE plpgsql
AS $$
DECLARE
v_ticket INT;
v_startsprint_date DATE;
v_end_date DATE;
v_current_date DATE;
v_first_activity_date DATE;
v_started DATE;
v_sprint_setting VARCHAR(255);
v_time_spent_second FLOAT;
v_remaining_estimate FLOAT;
v_time_logged_tmp FLOAT;
v_has_log INT;
v_time_logged_tk FLOAT;
v_oe_tk INT;
v_has_past_sprint INT;
v_oe_sprint INT;
v_complete_of_sprint FLOAT;
v_oe_tk_burnt FLOAT;
v_oe_sprint_burnt FLOAT;
v_project_complete FLOAT;
v_complete_tk FLOAT;
rl_cursor CURSOR FOR SELECT
tk.id,
tk.has_past_sprint,
cast(first_activity_date AS DATE)
FROM hm_ticket tk
WHERE sprint = v_sprint_id AND deleted = 0;
BEGIN
-- find start date and end date of sprint
SELECT
cast(start_date AS DATE),
cast(end_date AS DATE)
INTO v_startsprint_date, v_end_date
FROM hm_sprint
WHERE id = v_sprint_id AND status <> 'future';
-- find sprint setting
IF NOT exists(SELECT d.burn_down_statuses
FROM hm_sprint x, hm_setting c, hm_burn_down_status d
WHERE c.id = d.setting AND x.setting = c.id AND x.id = v_sprint_id)
THEN
v_sprint_setting :='xxx';
ELSE
SELECT string_agg(d.burn_down_statuses, ',')
INTO v_sprint_setting
FROM hm_sprint x, hm_setting c, hm_burn_down_status d
WHERE c.id = d.setting AND x.setting = c.id AND x.id = v_sprint_id;
END IF;
raise notice 'v_sprint_setting %', v_sprint_setting;
OPEN rl_cursor;
LOOP
FETCH rl_cursor INTO v_ticket, v_has_past_sprint, v_first_activity_date;
raise notice 'v_ticket: %', v_ticket;
EXIT WHEN NOT FOUND;
/*select cast(min(started) as date) into v_started
from hm_worklog
where ticket=v_ticket and cast(started as date) between v_startsprint_date and v_end_date;
if v_started is null then v_current_date:=v_startsprint_date;
else
v_current_date:=v_started;
end if;*/
v_current_date:=v_startsprint_date;
--- calculate remaining estimate, time logged of ticket
IF v_has_past_sprint = 0
THEN
SELECT
coalesce(remaining_estimate, 0),
coalesce(time_logged, 0)
INTO v_remaining_estimate, v_time_logged_tk
FROM hm_ticket
WHERE id = v_ticket;
ELSE
SELECT coalesce(time_logged, 0)
INTO v_time_logged_tmp
FROM hm_ticket
WHERE id = v_ticket;
--------------------------------------
SELECT
coalesce(remaining_estimate, 0),
v_time_logged_tmp - coalesce(time_logged, 0)
INTO v_remaining_estimate, v_time_logged_tk
FROM hm_ticket_past_sprint
WHERE ticket = v_ticket;
END IF;
raise notice '----------v_has_past_sprint: %', v_has_past_sprint;
raise notice '----------v_time_logged_tmp: %', v_time_logged_tmp;
raise notice '----------v_time_logged_tk: %', v_time_logged_tk;
raise notice '----------v_remaining_estimate: %', v_remaining_estimate;
-- calculate oe of ticket
IF v_has_past_sprint = 0
THEN
SELECT cast(CASE WHEN coalesce(original_estimate, 0) = 0
THEN coalesce(time_logged, 0)
ELSE original_estimate
END
AS FLOAT)
INTO v_oe_tk
FROM hm_ticket
WHERE id = v_ticket;
ELSE
SELECT coalesce(time_logged, 0)
INTO v_time_logged_tmp
FROM hm_ticket
WHERE id = v_ticket;
--------------------------------------
SELECT cast(CASE WHEN coalesce(original_estimate, 0) = 0
THEN v_time_logged_tmp - coalesce(time_logged, 0)
ELSE original_estimate
END
AS FLOAT)
INTO v_oe_tk
FROM hm_ticket_past_sprint
WHERE ticket = v_ticket;
END IF;
raise notice 'v_oe_tk: %',v_oe_tk;
/*########################################################################## START TO LOOP CURRENT DATE #############################################################################*/
WHILE v_current_date <= v_end_date LOOP
--- calculate time_spent_seconds from start sprint
IF NOT exists(SELECT id
FROM hm_worklog
WHERE ticket = v_ticket AND cast(started AS DATE) BETWEEN v_startsprint_date AND v_current_date)
THEN
v_time_spent_second:=0;
ELSE
SELECT cast(sum(time_spent_seconds) AS FLOAT)
INTO v_time_spent_second
FROM hm_worklog
WHERE ticket = v_ticket AND cast(started AS DATE) BETWEEN v_startsprint_date AND v_current_date
GROUP BY ticket;
END IF;
raise notice 'v_time_spent_second: %', v_time_spent_second;
--calculate % complete of ticket/day
IF NOT v_current_date = v_end_date THEN
IF NOT exists(SELECT date FROM hm_ticket_history WHERE ticket = v_ticket AND cast(date AS DATE) = v_current_date)
then
-- FIND STATUS OF TICKET IN SETTING. 0: NO STATUS EXIST -> FORMULAR SHOULD BE APPLIED , <>0 : EXIST--> SET COMPLETE TK = 0
SELECT CASE WHEN (position(upper(trim(status)) IN upper(trim(v_sprint_setting))) = 0)
THEN CASE WHEN v_remaining_estimate = 0 AND v_time_logged_tk <> 0 THEN 80
WHEN v_remaining_estimate = 0 AND v_time_logged_tk = 0 THEN 0
WHEN (v_remaining_estimate + v_time_logged_tk) = 0 THEN 0
ELSE round(cast(v_time_spent_second * 100 / (v_time_logged_tk + v_remaining_estimate) AS NUMERIC), 2)
END
ELSE 100
END AS complete
INTO v_complete_tk
FROM hm_ticket
WHERE id = v_ticket;
ELSE
SELECT CASE WHEN (position(upper(trim(status)) IN upper(trim(v_sprint_setting))) = 0)
THEN CASE WHEN v_remaining_estimate = 0 AND v_time_logged_tk <> 0 THEN 80
WHEN v_remaining_estimate = 0 AND v_time_logged_tk = 0 THEN 0
WHEN (v_remaining_estimate + v_time_logged_tk) = 0 THEN 0
ELSE round(cast(v_time_spent_second * 100 / (v_time_logged_tk + v_remaining_estimate) AS NUMERIC), 2)
END
ELSE 100
END AS complete
INTO v_complete_tk
FROM hm_ticket_history
WHERE ticket = v_ticket AND cast("date" AS DATE)= v_current_date;
END IF;
ELSE
--NOTE: IF V_CURRENT_DATE == END-DATE-OF-SPRINT, COMPLETE % WILL BE CALCULATED BASE ON LATEST TICKET STATUS, NOT LAST-DAY-OF-SPRINT TICKET STATUS
IF NOT exists(SELECT date FROM hm_ticket_history WHERE ticket = v_ticket AND cast(date AS DATE) = v_current_date)
then
SELECT CASE WHEN (position(upper(trim(status)) IN upper(trim(v_sprint_setting))) = 0)
THEN CASE WHEN v_remaining_estimate = 0 AND v_time_logged_tk <> 0 THEN 80
WHEN v_remaining_estimate = 0 AND v_time_logged_tk = 0 THEN 0
WHEN (v_remaining_estimate + v_time_logged_tk) = 0 THEN 0
ELSE round(cast(v_time_spent_second * 100 / (v_time_logged_tk + v_remaining_estimate) AS NUMERIC), 2)
END
ELSE 100
END AS complete
INTO v_complete_tk
FROM hm_ticket
WHERE id = v_ticket;
else
SELECT CASE WHEN (position(upper(trim(status)) IN upper(trim(v_sprint_setting))) = 0)
THEN CASE WHEN v_remaining_estimate = 0 AND v_time_logged_tk <> 0 THEN 80
WHEN v_remaining_estimate = 0 AND v_time_logged_tk = 0 THEN 0
WHEN (v_remaining_estimate + v_time_logged_tk) = 0 THEN 0
ELSE round(cast(v_time_spent_second * 100 / (v_time_logged_tk + v_remaining_estimate) AS NUMERIC), 2)
END
ELSE 100
END AS complete
INTO v_complete_tk
FROM hm_ticket_history
WHERE ticket = v_ticket order by date desc limit 1;
END IF;
END IF;
if v_complete_tk > 100 then v_complete_tk := 100;
end if;
raise notice 'v_sprint_setting: %', v_sprint_setting;
raise notice 'v_complete_tk: %', v_complete_tk;
raise notice '---------------------------------';
-- check has log
IF exists(SELECT id
FROM hm_worklog
WHERE cast(started AS DATE) = cast(v_current_date AS DATE) AND ticket = v_ticket)
THEN
v_has_log := 1;
ELSE
v_has_log := 0;
END IF;
--raise notice 'v_has_log: %', v_has_log;
-- calculate oe of sprint
SELECT sum(x.oe)
INTO v_oe_sprint
FROM
(
SELECT CASE WHEN coalesce(original_estimate, 0) = 0
THEN coalesce(time_logged, 0)
ELSE coalesce(original_estimate, 0)
END oe
FROM hm_ticket
WHERE sprint = v_sprint_id AND cast(first_activity_date AS DATE) <= v_current_date
AND has_past_sprint = 0
UNION ALL
SELECT CASE WHEN coalesce(b.original_estimate, 0) = 0
THEN coalesce(a.time_logged - b.time_logged)
ELSE coalesce(b.original_estimate, 0)
END oe
FROM hm_ticket a, hm_ticket_past_sprint b
WHERE a.id = b.ticket AND a.sprint = v_sprint_id AND cast(a.first_activity_date AS DATE) <= v_current_date
AND a.has_past_sprint = 1
) x;
-- raise notice 'v_oe_sprint: %', v_oe_sprint;
-- calculate v_oe_tk_burnt
SELECT CASE WHEN v_time_spent_second = 0 OR v_oe_tk = 0
THEN 0
ELSE round(cast((v_time_spent_second * 100 / v_oe_tk) AS NUMERIC), 2)
END
INTO v_oe_tk_burnt;
--raise notice 'v_oe_tk_burnt: %', v_oe_tk_burnt;
-- calculate v_oe_sprint_burnt
IF v_oe_sprint = 0
THEN
v_oe_sprint_burnt:=100;
ELSE
SELECT round(cast((v_time_spent_second * 100 / v_oe_sprint) AS NUMERIC), 2)
INTO v_oe_sprint_burnt;
END IF;
--raise notice 'v_oe_sprint_burnt: %', v_oe_sprint_burnt;
-- calculate v_project_complete
IF v_oe_sprint = 0
THEN
v_project_complete:=100;
ELSE
SELECT round(cast((v_complete_tk * v_oe_tk / v_oe_sprint) AS NUMERIC), 2)
INTO v_project_complete;
END IF;
-- raise notice 'v_project_complete: %', v_project_complete;
IF v_current_date >= v_first_activity_date
THEN
INSERT INTO hm_ticket_statistic (complete, date, has_log, last_modified, original_estimate_burnt, original_estimate_project_burnt, project_complete, status, ticket, sprint)
VALUES
(v_complete_tk, cast(v_current_date AS DATE), v_has_log, current_timestamp, v_oe_tk_burnt, v_oe_sprint_burnt,
v_project_complete, 1, v_ticket, v_sprint_id)
ON CONFLICT (date, ticket, sprint)
DO UPDATE
SET complete = v_complete_tk,
has_log = v_has_log,
original_estimate_burnt = v_oe_tk_burnt,
original_estimate_project_burnt = v_oe_sprint_burnt,
project_complete = v_project_complete;
END IF;
v_current_date:= v_current_date + INTERVAL '24 hours';
EXIT WHEN NOT found;
END LOOP;
END LOOP;
CLOSE rl_cursor;
RETURN 0;
END;
Also, if you see anything that can be improved in the code, please suggest me.
Try running analyze on the local database, after restoring the dump. This updates statistics.
Finally found the bug. It is a deadlock bug, i used Synchronized on function way too much, they start to get stuck. The production server still get the same problem, i just don't see it. So i try to use Synchronized more selectively, reduce the size of functions that Synchronized on. Then it is fixed.
I have this query :
-- There are a select and joins in the query
--Then I have this Where part
WHERE
(
dbo.Table.Furnished IS NULL
OR dbo.Table.Furnished = 0
)
AND (
dbo.Table.Type2 = N'House'
)
AND (
dbo.Table.Type3 = N'ForSale'
)
AND (
dbo.Table.FixedArea/ dbo.Table.ToiletNumber< 250
)
AND (
dbo.Table.FixedArea> 25
)
AND (
CASE
WHEN dbo.Table.Segment1 = 1 THEN
'FirstGroup'
WHEN dbo.Table.Segment1 = 2 THEN
'SecondGroup'
WHEN dbo.Table.Segment1 = 3 THEN
'ThirdGroup'
ELSE
'Undefined'
END <> 'Undefined'
)
AND (
dbo.Table.Segment2 <> N'Undefined'
)
OR
(
dbo.Table.Furnished IS NULL
OR dbo.Table.Furnished = 0
)
AND (
dbo.Table.Type2 = N'Commercial'
)
AND (
dbo.Table.Type3 = N'ForSale'
)
AND (
dbo.Table.FixedArea/ dbo.Table.ToiletNumber< 250
)
AND (
dbo.Table.FixedArea> 25
)
AND (
CASE
WHEN dbo.Table.Segment1 = 1 THEN
'FirstGroup'
WHEN dbo.Table.Segment1 = 2 THEN
'SecondGroup'
WHEN dbo.Table.Segment1 = 3 THEN
'ThirdGroup'
ELSE
'Undefined'
END <> 'Undefined'
)
AND (
dbo.Table.Segment2 <> N'Undefined'
)
When I add this part below at the bottom of my original query (which doesn't have any division or something like that), I get Divide by zero error encountered.
OR
(
dbo.Table.Geo IS NOT NULL
)
AND (
dbo.Table.FixedArea > 0
)
AND (
dbo.Table.Price > 0
)
AND (
dbo.Table.RoomNumber LIKE N'%+%'
)
AND (
dbo.Table.Furnished IS NULL
OR dbo.Table.Furnished = 0
)
AND(
dbo.Table.Type3 = N'ForRent'
)
Why am I getting this divide by zero error? I'm not adding any division. I'm just adding some new filters. Please help me with this error. Thanks.
You actually do here dbo.Table.FixedArea/ dbo.Table.ToiletNumber< 250...and probably ToiletNumber is zero
EDIT: Try re-running your query by changing the above code to
dbo.Table.FixedArea / NULLIF(dbo.Table.ToiletNumber, 0) < 250
You div/0 is here:
(
dbo.Table.FixedArea/ dbo.Table.ToiletNumber < 250
)
This:
AND (
dbo.Table.FixedArea> 25
)
Probably removed any div/0
Now this:
AND (
dbo.Table.FixedArea > 0
)
Means that rows with a FixedArea between 0 and 25 probably has a 0 for ToiletNumber, and has to be evaluated
To fix:
case when ToiletNumber = 0 then 1
else dbo.Table.FixedArea/ dbo.Table.ToiletNumber
end <250
I have a collection of objects (5000+) with 7 different properties. Two properties are tertiary the rest are binary. Each object have all 7 properties specified. It is possible that in some scenarios binary property may become unary.
Once in awhile I need to select top N random objects from this collection weighted by the frequency of a label in each category against the objects' total.
Currently, I have all data in sql server table as object, propertyMask pairs; however, I can reorganize that any other way necessary.
Examples:
black blue yellow (1,2,4)
circle square triangle (8,16,32)
solid color/meshed color (64)
dashed contour/no contour (128)
etc. (256)
The data is :
object1|9 <- 1001 black circle only (all other properties are 0)
object2|81 <- 101 0001 black square with solid color (all other properties are 0)
object3|148 <- 1001 0100 yellow square with dashed contour
etc.
Say, I end up with 1k objects with 600 black, 300 yellow and 100 blue objects. And I need to select top 10 objects. If I just consider one property, I'll just take any 6 black, 3 yellow and 1 blue objects. But I have 6 other properties to consider and ensure I have right amount of circles, squares and triangles. Etc. At this point I don't even know how to approach this problem.
Any suggestions would be appreciated.
*EDIT:
I repopulated data in the following format
name | att1 | att2 | ...
obj1 | 1 | 8 | ...
obj2 | 2 | 16 | ...
obj3 | 1 | 32 | ...
Is there a way to select TOP N objects weighted by the frequency of each attribute? I have 7 attributes for each objects; no Null values.
Thanks!
It's messy, it does not always fetch the exact number of rows required or an perfect distribution but it comes pretty darn close.
So how does it work:
ValuesPivotted: pivot all the distinct values and give each row a random rownumber
TargetDistribution: for each distinct value determine how many you need
SelectRows: go through each row in ValuesPivotted on a per row basis see if the row is to be skipped because it would otherwise breach an target for an distinct value. Otherwise increment the Sum for each value applicable for that row.
DECLARE #TargetRowNum INT = 100;
WITH ValuesPivotted AS(
SELECT O.id
, RowNum = ROW_NUMBER() OVER (ORDER BY NEWID())
, [0] = CASE WHEN O.atr1 = 0 THEN 1 ELSE 0 END
, [1] = CASE WHEN O.atr1 = 1 THEN 1 ELSE 0 END
, [2] = CASE WHEN O.atr1 = 2 THEN 1 ELSE 0 END
, [4] = CASE WHEN O.atr2 = 4 THEN 1 ELSE 0 END
, [8] = CASE WHEN O.atr2 = 8 THEN 1 ELSE 0 END
, [16] = CASE WHEN O.atr3 = 16 THEN 1 ELSE 0 END
, [32] = CASE WHEN O.atr3 = 32 THEN 1 ELSE 0 END
, [64] = CASE WHEN O.atr4 = 64 THEN 1 ELSE 0 END
, [128] = CASE WHEN O.atr4 = 128 THEN 1 ELSE 0 END
FROM dbo.objects AS O
),
TargetDistribution AS (
SELECT Target0 = ROUND(CAST(SUM([0] ) AS FLOAT) / COUNT(*) * #TargetRowNum, 0)
, Target1 = ROUND(CAST(SUM([1] ) AS FLOAT) / COUNT(*) * #TargetRowNum, 0)
, Target2 = ROUND(CAST(SUM([2] ) AS FLOAT) / COUNT(*) * #TargetRowNum, 0)
, Target4 = ROUND(CAST(SUM([4] ) AS FLOAT) / COUNT(*) * #TargetRowNum, 0)
, Target8 = ROUND(CAST(SUM([8] ) AS FLOAT) / COUNT(*) * #TargetRowNum, 0)
, Target16 = ROUND(CAST(SUM([16] ) AS FLOAT) / COUNT(*) * #TargetRowNum, 0)
, Target32 = ROUND(CAST(SUM([32] ) AS FLOAT) / COUNT(*) * #TargetRowNum, 0)
, Target64 = ROUND(CAST(SUM([64] ) AS FLOAT) / COUNT(*) * #TargetRowNum, 0)
, Target128 = ROUND(CAST(SUM([128]) AS FLOAT) / COUNT(*) * #TargetRowNum, 0)
FROM ValuesPivotted
),
SelectRows AS(
SELECT VP.id
, RowNum
, KeepRow = 1
, Target0 , Sum0 = [0]
, Target1 , Sum1 = [1]
, Target2 , Sum2 = [2]
, Target4 , Sum4 = [4]
, Target8 , Sum8 = [8]
, Target16 , Sum16 = [16]
, Target32 , Sum32 = [32]
, Target64 , Sum64 = [64]
, Target128 , Sum128 = [128]
FROM ValuesPivotted AS VP
CROSS JOIN TargetDistribution AS TD
WHERE VP.RowNum = 1
UNION ALL
SELECT
VP.id
, VP.RowNum
, KeepRow = ISNULL(SkipRow.Value, 1)
, Target0 , Sum0 = Sum0 + ISNULL(SkipRow.Value, [0] )
, Target1 , Sum1 = Sum1 + ISNULL(SkipRow.Value, [1] )
, Target2 , Sum2 = Sum2 + ISNULL(SkipRow.Value, [2] )
, Target4 , Sum4 = Sum4 + ISNULL(SkipRow.Value, [4] )
, Target8 , Sum8 = Sum8 + ISNULL(SkipRow.Value, [8] )
, Target16 , Sum16 = Sum16 + ISNULL(SkipRow.Value, [16] )
, Target32 , Sum32 = Sum32 + ISNULL(SkipRow.Value, [32] )
, Target64 , Sum64 = Sum64 + ISNULL(SkipRow.Value, [64] )
, Target128 , Sum128 = Sum128 + ISNULL(SkipRow.Value, [128])
FROM SelectRows AS SR
INNER JOIN ValuesPivotted AS VP
ON VP.RowNum = SR.RowNum + 1
CROSS APPLY(
SELECT Value =
CASE WHEN Sum0 + [0] <= Target0
AND Sum1 + [1] <= Target1
AND Sum2 + [2] <= Target2
AND Sum4 + [4] <= Target4
AND Sum8 + [8] <= Target8
AND Sum16 + [16] <= Target16
AND Sum32 + [32] <= Target32
AND Sum64 + [64] <= Target64
AND Sum128 + [128] <= Target128
THEN NULL ELSE 0 END
) AS SkipRow
WHERE Sum0 < Target0
OR Sum1 < Target1
OR Sum2 < Target2
OR Sum4 < Target4
OR Sum8 < Target8
OR Sum16 < Target16
OR Sum32 < Target32
OR Sum64 < Target64
OR Sum128 < Target128
)
SELECT O.*
FROM SelectRows AS SR
INNER JOIN dbo.objects AS O
ON SR.id = O.id
WHERE SR.KeepRow = 1
OPTION(MAXRECURSION 0)
EDIT: The WHERE clause in SelectRows did not do what it was supposed to, stop the recursion when all targets were met, now it does.
I am trying to rework a query that is based on cursors.
The query calculates certain stats based on multiple values. In the snippet below the first, second and third CASE works out the happiness of Unit1. Any of these fields are 0 (they can never be NULL) I will get a Divide by Zero error. I could just add 1 to each field (Unit2 + 1) / (Unit1 + 1) and that will stop the error. However, it seems like a bodge and it will potentially give the incorrect result. ie. Unit1 needs the same amount of Unit2 to keep them happy. If I have one Unit1 and no Unit2 this bodge will give 100% happy for that check. So my first problem is how do I prevent the divide by zero but not distort the results. Each CASE gives me a % happy
Select
CASE WHEN ((Unit2 / Unit1) * 100) > 100 Then 100 Else ((Unit2 / Unit1) * 100) END Unit1Happy1,
CASE WHEN ((Stock3 / (Unit1 * 2)) * 100) > 100 Then 100 Else ((Stock3 / (Unit1 * 2)) * 100) END Unit1Happy2,
CASE WHEN (((Drug3 + (Drug1 / 2)) / Unit1) * 100) > 100 Then 100 Else (((Drug3 + (Drug1 / 2)) / Unit1) * 100) END Unit1Happy3,
CASE WHEN (((Weapon6 + Weapon7 + Weapon8 + Weapon9) / Unit2) * 100) > 100 Then 100 ELSE (((Weapon6 + Weapon7 + Weapon8 + Weapon9) / Unit2) * 100) END Unit2Happ1,
CASE WHEN (((Stock2 + (Stock1 / 2)) / Unit2) * 100) > 100 Then 100 Else (((Stock2 + (Stock1 / 2)) / Unit2) * 100) END Unit2Happ2
FROM tblUserFiles
My next problem is that I need to take to lowest value for each UnitHappiness and store that value in the table. So in tblUserFiles are 5 fields Unit1Happ, Unit2Happ .... Unit5Happ. Looking at the above query if Unit1Happy1 is the lowest figure I store that figure into Unit1Happ, If Unit2Happy is the lowest I store that etc.
My record Identifier is UserId and I need to run this for a given UserId or for the whole table.
What I am basically asking is:
What is the best method to identify the lowest value of each Unit
calculation?
What is the best way to prevent the divide by zero
error?
Can I approach this problem in a better way?
Update
I am working through the suggestions posted in the answers below. As this is just a training exercise it may take a while. I do have a working query that gives the results I am looking for I am just not sure if the suggested answers would be more efficient.
Update tblUserFiles Set Unit1Happ = happyvals.Unit1Happiness, Unit2Happ = happyvals.Unit2Happiness, Unit3Happ = happyvals.Unit3Happiness, Unit4Happ = happyvals.Unit4Happiness, Unit5Happ = happyvals.Unit5Happiness FROM
(SELECT ch.UserId,
Case When ch.Unit1Happy1 < ch.Unit1Happy2 And ch.Unit1Happy1 < ch.Unit1Happy3 Then ch.Unit1Happy1
When ch.Unit1Happy2 < ch.Unit1Happy1 And ch.Unit1Happy2 < ch.Unit1Happy3 Then ch.Unit1Happy2
Else ch.Unit1Happy3
End As Unit1Happiness,
CASE WHEN ch.Unit2Happy1 > ch.Unit2Happy2 THEN ch.Unit2Happy1
ELSE ch.Unit2Happy2
END AS Unit2Happiness,
ch.Unit3Happy1 AS Unit3Happiness,
ch.Unit4Happy1 AS Unit4Happiness,
ch.Unit5Happy1 AS Unit5Happiness
FROM
(
Select
UserId,
CASE WHEN Unit2 = 0 OR Unit1 = 0 THEN 0
WHEN ((Unit2 / Unit1) * 100) > 100 Then 100
ELSE ((Unit2 / Unit1) * 100)
END Unit1Happy1,
CASE WHEN Stock3 = 0 OR Unit1 = 0 THEN 0
WHEN ((Stock3 / (Unit1 * 2)) * 100) > 100 THEN 100
ELSE ((Stock3 / (Unit1 * 2)) * 100)
END Unit1Happy2,
CASE WHEN Unit1 = 0 THEN 0
WHEN Drug3 = 0 AND Drug1 = 0 THEN 0
WHEN Drug1 = 0 THEN
CASE WHEN (Drug3 / Unit1) * 100 > 100 THEN 100
ELSE (Drug3 / Unit1) * 100
END
WHEN Drug3 = 0 THEN
CASE WHEN (Drug1 / 2) / Unit1 > 100 THEN 100
ELSE (Drug1 / 2) / Unit1
END
ELSE
CASE WHEN (((Drug3 + (Drug1 / 2)) / Unit1) * 100) > 100 THEN 100
ELSE (((Drug3 + (Drug1 / 2)) / Unit1) * 100)
END
END Unit1Happy3,
CASE WHEN Unit2 = 0 THEN 0
WHEN (Weapon6 + Weapon7 + Weapon8 + Weapon9) = 0 THEN 0
WHEN (((Weapon6 + Weapon7 + Weapon8 + Weapon9) / Unit2) * 100) > 100 THEN 100
ELSE (((Weapon6 + Weapon7 + Weapon8 + Weapon9) / Unit2) * 100)
END Unit2Happy1,
CASE WHEN Unit2 = 0 THEN 0
WHEN Stock1 = 0 AND Stock2 = 0 THEN 0
WHEN Stock1 = 0 THEN
CASE WHEN ((Stock2 / Unit2) * 100) > 100 THEN 100
ELSE ((Stock2 / Unit2) * 100)
END
WHEN Stock2 = 0 THEN
CASE WHEN (((Stock1 / 2) / Unit2) * 100) > 100 THEN 100
ELSE (((Stock1 / 2) / Unit2) * 100)
END
WHEN (((Stock2 + (Stock1 / 2)) / Unit2) * 100) > 100 THEN 100
ELSE (((Stock2 + (Stock1 / 2)) / Unit2) * 100)
END Unit2Happy2,
CASE WHEN Unit2 = 0 OR Unit3 = 0 THEN 0
WHEN ((Unit2 / Unit3) * 100) > 100 THEN 100
ELSE ((Unit2 / Unit3) * 100)
END Unit3Happy1,
CASE WHEN Unit2 = 0 OR Unit4 = 0 THEN 0
WHEN ((Unit2 / Unit4) * 100) > 100 THEN 100
ELSE ((Unit2 / Unit4) * 100)
END Unit4Happy1,
CASE WHEN Unit2 = 0 OR Unit5 = 0 THEN 0
WHEN ((Unit2 / Unit5) * 100) > 100 THEN 100
ELSE ((Unit2 / Unit5) * 100)
END Unit5Happy1
FROM tblUserFiles) ch) happyvals
Join tblUserFiles ON tblUserFiles.UserID = happyvals.UserID
put a nullif(Unit1, 0) around every divide by group ?
Something like this.
The idea is to wrap your division in a UDF (scalar user defined function)....and use the "Max(v)" trick.
The code below may not be perfect, I've supplying the idea.
Cursors are horrible performers, 99.9% of the time. Try to solve this without cursors.
/* or Create */
ALTER FUNCTION dbo.udfSafeDivision (#num float , #denom float)
RETURNS float
AS
BEGIN
declare #returnValue float
select #returnValue = 0
if(isnull(#denom,0) != 0)
BEGIN
select #returnValue = convert(float, convert(float, #num)/convert(float, #denom))
END
return #returnValue
END
GO
IF OBJECT_ID('tempdb..#TableOne') IS NOT NULL
begin
drop table #TableOne
end
CREATE TABLE #TableOne
(
SurrogateKey int IDENTITY(1001, 1),
Unit1 int ,
Unit2 int ,
Stock1 int ,
Stock2 int ,
Stock3 int ,
Drug1 int ,
Drug3 int ,
Weapon6 int , Weapon7 int , Weapon8 int , Weapon9 int
)
Insert into #TableOne (Unit1, Unit2, Stock1, Stock2 , Stock3 , Drug1, Drug3 , Weapon6 , Weapon7 , Weapon8 , Weapon9)
select 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
UNION ALL select 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
UNION ALL select 1 , 2 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
UNION ALL select 1 , 2 , 3 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
UNION ALL select 1 , 2 , 3 , 4 , 0 , 0 , 0 , 0 , 0 , 0 , 0
UNION ALL select 1 , 2 , 3 , 4 , 5 , 0 , 0 , 0 , 0 , 0 , 0
UNION ALL select 1 , 2 , 3 , 4 , 5 , 6 , 0 , 0 , 0 , 0 , 0
UNION ALL select 1 , 2 , 3 , 4 , 5 , 6 , 7 , 0 , 0 , 0 , 0
UNION ALL select 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 0 , 0 , 0
UNION ALL select 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 0 , 0
UNION ALL select 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 0
UNION ALL select 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11
UNION ALL select 5 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
UNION ALL select 5 , 0 , 0 , 0 , 5 , 0 , 0 , 0 , 0 , 0 , 0
UNION ALL select 10 , 0 , 0 , 0 , 0 , 5 , 10 , 0 , 0 , 0 , 0
UNION ALL select 0 , 1 , 0 , 0 , 0 , 5 , 10 , 10 , 20 , 30 , 40
UNION ALL select 0 , 50 , 20 , 10 , 0 , 0 , 0 , 0 , 0 , 0 , 0
SELECT
(SELECT Max(v)
FROM (VALUES (Unit1Happy1), (Unit1Happy2), (Unit1Happy3), (Unit2Happ1), (Unit2Happ2)) AS value(v)) as [MaxValue]
, '--------' as Sep1
, derived1.*
FROM
(
Select
CASE WHEN ((dbo.udfSafeDivision(Unit2 , Unit1)) * 100) > 100 Then 100 Else (dbo.udfSafeDivision(Unit2 , Unit1) * 100) END Unit1Happy1,
CASE WHEN (dbo.udfSafeDivision(Stock3 , (Unit1 * 2)) * 100) > 100 Then 100 Else (dbo.udfSafeDivision(Stock3 , (Unit1 * 2)) * 100) END Unit1Happy2,
CASE WHEN (dbo.udfSafeDivision((Drug3 + (Drug1 / 2)) , Unit1) * 100) > 100 Then 100 Else (dbo.udfSafeDivision((Drug3 + (Drug1 / 2)) , Unit1) * 100) END Unit1Happy3,
CASE WHEN (dbo.udfSafeDivision((Weapon6 + Weapon7 + Weapon8 + Weapon9) , Unit2) * 100) > 100 Then 100 ELSE (dbo.udfSafeDivision((Weapon6 + Weapon7 + Weapon8 + Weapon9) , Unit2) * 100) END Unit2Happ1,
CASE WHEN (dbo.udfSafeDivision((Stock2 + (Stock1 / 2)) , Unit2) * 100) > 100 Then 100 Else (dbo.udfSafeDivision((Stock2 + (Stock1 / 2)) , Unit2) * 100) END Unit2Happ2
/* the below is to debug */
, '--' as Sep1
, Unit1, Unit2, Stock1, Stock2 , Stock3 , Drug1, Drug3 , Weapon6 , Weapon7 , Weapon8 , Weapon9
, dbo.udfSafeDivision(Unit2 , Unit1) as Div1
, dbo.udfSafeDivision(Stock3 , (Unit1 * 2)) as Div3
FROM #TableOne
) as derived1
IF OBJECT_ID('tempdb..#TableOne') IS NOT NULL
begin
drop table #TableOne
end
I have this massive query that I can typically run in under 2 minutes. However, when I run it a second time about a minute after, it goes on infinitely... so I kill the process and my SSMS session. I don't have any other jobs running in the background.
Is something else being retained on the server? I think I'm missing something as far as how SQL Server works.
Thanks.
EDIT: Here's the SQL (had to do a little obfuscation)
SELECT pl.OrangeLocationID ,
e.EventID ,
cr.Title AS [Event Type] ,
su.LastName + ', ' + su.FirstName AS FMR ,
CONVERT(VARCHAR(20), pl.Report_Date, 101) AS [Report Entry Date] ,
l.Name ,
l.Number ,
ll.SodaPopLocationID AS [SodaPop Location ID] ,
l.Zip ,
c.Channel ,
pl.DT AS [ReportedDate] ,
RIGHT(pl.DT_start, 8) AS [ReportedStartTime] ,
RIGHT(pl.DT_end, 8) AS [ReportedEndTime] ,
[CMS].dbo.dDateDiff(pl.DT_start, pl.DT_end) AS [ReportedDuration] ,
pl.scheduled_date AS [ScheduledDate] ,
RIGHT(pl.scheduled_start, 8) AS [ScheduledStartTime] ,
RIGHT(pl.scheduled_end, 8) AS [ScheduledEndTime] ,
[CMS].dbo.dDateDiff(pl.scheduled_start, pl.DT_end) AS [ScheduledDuration] ,
e.HoursPaid AS [Rep Hours Worked] ,
ISNULL(PP.[RepCount], 0) AS [RepCount] ,
CASE WHEN [CMS].dbo.dDateDiff(pl.DT_start, pl.DT_end) = ( e.HoursPaid / ISNULL(PP.[RepCount], 1) )
THEN [CMS].dbo.oa_HourDateDiff(pl.DT_start, pl.DT_end)
WHEN [CMS].dbo.dDateDiff(pl.scheduled_start, pl.DT_end) = ( e.HoursPaid / ISNULL(PP.[RepCount], 1) )
THEN [CMS].dbo.oa_HourDateDiff(pl.scheduled_start, pl.DT_end)
ELSE ( e.HoursPaid / ISNULL(PP.[RepCount], 1) )
END AS [FinalDuration] ,
g.[Description] AS [OA Market] ,
g.SodaPop_Region AS [SodaPop Region] ,
g.SodaPop_Area AS [SodaPop Area] ,
coup4 ,
coupo ,
coupo_e ,
card_num ,
promo ,
promo_no ,
promo_no_o ,
highlight1 ,
highlight2 ,
highlight3 ,
mgmt_reaction ,
mgmt_reaction_e ,
comm_p ,
comm_n ,
r.comments ,
s_fname ,
s_lname ,
v_title ,
ll.KeyAccountCorp AS [Key Account Corp.] ,
interact_new + interact_rep AS [interact_total] ,
samp_new + samp_rep AS [samp_total] ,
purch_new + purch_rep AS [purch_total] ,
23 / ( NULLIF(( interact_new + interact_rep ), 0) * 1.0 ) AS [Int_Crate] ,
CASE WHEN sampletype = 11 THEN ( purch_new + purch_rep ) / ( NULLIF(( samp_new + samp_rep ), 0) * 1.0 )
ELSE NULL
END AS [Samp_Crate] ,
coup1 + coup2 AS [coup_total] ,
CASE WHEN coup1 + coupo > 0 THEN 1
ELSE 0
END AS [CoupDist] ,
DATEPART(month, pl.DT) AS [Visit_Month] ,
DATEPART(quarter, pl.DT) AS [Quarter] ,
DATEPART(weekday, pl.DT) AS [Weekday] ,
CASE DATEPART(weekday, pl.DT)
WHEN 6 THEN 'Fri'
WHEN 7 THEN 'Sat'
WHEN 1 THEN 'Sun'
ELSE 'Mon-Thurs'
END AS [Weekday_Grouped] ,
CASE WHEN dbo.Exception(pl.OrangeLocationID, 12) = 1
OR dbo.Exception(pl.OrangeLocationID, 13) = 1
OR dbo.Exception(pl.OrangeLocationID, 14) = 1 THEN 1
ELSE 0
END AS [EVolume] ,
CASE WHEN dbo.DoesHaveException(pl.OrangeLocationID, 18) = 1 THEN 1
ELSE 0
END AS [CVolume] ,
CASE WHEN dbo.eException(pl.OrangeLocationID, 9) = 1
OR dbo.eException(pl.OrangeLocationID, 22) = 1 THEN 1
ELSE 0
END AS [Volumes] ,
CASE WHEN dbo.eException(pl.OrangeLocationID, 8) = 1
OR dbo.eException(pl.OrangeLocationID, 21) = 1 THEN 1
ELSE 0
END AS [Sales Price] ,
CASE WHEN dbo.eException(pl.OrangeLocationID, 11) = 1 THEN 1
ELSE 0
END AS [Sample Volume] ,
ISNULL(i.[NormalizedSold], 0) AS [EQBottlesSold] ,
CASE WHEN ISNULL(purch_new, 0) = 0 THEN 0
ELSE ISNULL(i.[NormalizedSold], 0) / ( purch_new + purch_rep )
END AS [EQBottlesSoldPerPurch] ,
ac.AvgSales ,
ac.STDEVSales ,
( ISNULL(i.[NormalizedSold], 0) - ac.AvgSales ) / ac.STDEVSales AS [sl] ,
ac.AvgPurchasers ,
ac.STDEVPurchasers ,
( ISNULL(r.purch_new, 0) - ac.AvgPurchasers ) / ac.STrchasers AS [ZScore_Purchasers] ,
ac.AvgConversions ,
ac.STDEVConversions ,
( ISNULL(( purch_new + purch_rep ) / ( NULLIF(( interact_new ), 0) ), 0) - ac.AvgConversions )
/ ac.STDEVConversions AS [ZScore_Conversions] ,
ac.[AvgSalesPerPurchaser] ,
ac.[STDEVSalesPerPurchaser] ,
( ISNULL(( CASE WHEN ISNULL(purch_new, 0) = 0 THEN 0
ELSE ISNULL(i.[NormalizedSold], 0) / ( purch_new + purch_rep )
END ), 0) - ac.[AvgSalesPerPurchaser] ) / ac.[STDEVSalesPerPurchaser] AS [SalesPerPurchaser] ,
( ( ( ISNULL(i.[NormalizedSold], 0) - ac.AvgSales ) / ac.STDEVSales )
+ ( (ISNULL(( CASE WHEN ISNULL(purch_new + purch_rep, 0) = 0 THEN 0
ELSE ISNULL(i.[NormalizedSold], 0) / ( purch_new + purch_rep )
END ), 0) - ac.[AvgSalesPerPurchaser]) ) ) / 4 AS [core] ,
( ( (( ISNULL(i.[NormalizedSold], 0) - ac.AvgSales ) / ac.STDEVSales) ) / 4 ) + 3 AS [core] ,
su.aaUserID ,
l.LsocationID
FROM [CMS_SodaPop].dbo.Schedule pl WITH ( NOLOCK )
INNER JOIN [CMS_SodaPop].dbo.Report r WITH ( NOLOCK ) ON r.OrangeLocationID = pl.OrangeLocationID
INNER JOIN [CMS].dbo.Users su WITH ( NOLOCK ) ON su.UserID = pl.Rep_FMR
INNER JOIN [CMS].dbo.Locations l WITH ( NOLOCK ) ON l.LocationID = pl.LocationID
INNER JOIN [CMS].dbo.OrangeReports cr WITH ( NOLOCK ) ON cr.RedID = pl.RedID
INNER JOIN [CMS_SodaPop].dbo.Events e WITH ( NOLOCK ) ON e.OrangeLocationID = pl.OrangeLocationID
INNER JOIN [CMS_SodaPop].dbo.MarketList g WITH ( NOLOCK ) ON g.GroupID = pl.GroupID
INNER JOIN [CMS_SodaPop].dbo.Locations ll WITH ( NOLOCK ) ON ll.LocationID = pl.LocationID
LEFT JOIN [CMS_SodaPop].dbo.Channels c WITH ( NOLOCK ) ON ll.ChannelID = c.ChannelID
LEFT JOIN ( SELECT PLocationID ,
COUNT(DISTINCT UserID) AS [RepCount]
FROM [CMS_roll].dbo.rollItems WITH ( NOLOCK )
WHERE RedID = 154
GROUP BY OrangeLocationID
) PP ON PP.OrangeLocationID = pl.OrangeLocationID
LEFT JOIN ( SELECT OrangeLocationID ,
SUM(NormalizedSold) AS [NormalizedSold]
FROM [Analysis].dbo.[vSodaPop_Retail_Inventory] WITH ( NOLOCK )
GROUP BY OrangeLocationID
) i ON i.OrangeLocationID = pl.OrangeLocationID
LEFT JOIN [Analysis].dbo.[vSodaPop_Calculations] ac WITH ( NOLOCK ) ON ac.[Quarter] = CASE WHEN DATEPART(MM,
[DT]) IN ( 10,
11, 12 ) THEN 4
END
AND ac.[Year] = DATEPART(YY, pl.DT)
WHERE pl.Activity = 1
AND pl.RedID = 154
AND pl.GroupID <> 444
AND pl.[DT] < GETDATE()
AND DATEPART(YY, [DT]) >= 2010
AND ISNULL(i.NormalizedSold, 0) >= 0
AND DATEPART(year, GETDATE()) = DATEPART(year, r.Insert_Date)
Would have to see the query to really dig in however..
you could try adding OPTION (RECOMPILE) to the end of the query to force it to create a new execution plan.
Are you using Temp Tables?
Cursors not deallocated & closed?
You can look at Profiler to see if anything looks different between the 2 executions.
Are you sure it isn't being blocked by another process the second time?
What happens if you execute
CHECKPOINT;
GO;
DBCC DROPCLEANBUFFERS;
GO;
DBCC FREEPROCCACHE;
GO;
between the queries? This is not really a solution but will help diagnosis.