【发布时间】:2019-12-27 20:39:18
【问题描述】:
我有一个跟踪 Oracle 12c 数据库中每小时数据的数据库视图。数据是这样排列的,一天中的每个小时都有列。
SALES_DATE | LOCATION_CODE | HE1_SALES | HE2_SALES | ... | HE24_SALES
_________________________________________________________________________
12/27/2019 | ABCD | 40 | 50 | ... | 60
12/26/2019 | ABCD | 51 | 64 | ... | 68
12/27/2019 | ABCG | 53 | 54 | ... | 50
12/26/2019 | ABCG | 45 | 47 | ... | 52
我有一个存储过程,它查看过去 10 年,并尝试查找每小时模式与用户定义的日期最相似的日期。这是通过获取每个小时的差异并将其添加到总数中来实现的。总差异在 50 以内的任何日期都将在列表中返回(因此结果集通常非常小)。然后它将结果放在一个表格中,以便用户稍后可以回来查看它们,直到他们再次运行该过程(当他们查看数据时,它首先显示最相似的排序,基于该“接近度”)。以下是当前形式的程序:
CREATE OR REPLACE PROCEDURE MYDB.COMPARE_SALES_SP (
p_compare_date IN DATE,
p_location_code IN VARCHAR2,
p_userid IN VARCHAR2,
p_message OUT VARCHAR2)
IS
TYPE SalesArray IS TABLE OF NUMBER
INDEX BY PLS_INTEGER;
v_hours_count INTEGER;
v_difference NUMBER;
v_compare_sales SalesArray;
v_curr_sales SalesArray;
BEGIN
DELETE FROM MYDB.SALES_ANALYSIS
WHERE userid = p_userid;
IF TRUNC (SYSDATE) = TRUNC (p_compare_date)
THEN
v_hours_count := MYDB.F_HOUR_ENDING_NUMBER (SYSDATE);
ELSE
v_hours_count := 24;
END IF;
SELECT HE1_SALES, HE2_SALES, HE3_SALES, HE4_SALES, HE5_SALES, HE6_SALES,
HE7_SALES, HE8_SALES, HE9_SALES, HE10_SALES, HE11_SALES, HE12_SALES,
HE13_SALES, HE14_SALES, HE15_SALES, HE16_SALES, HE17_SALES, HE18_SALES,
HE19_SALES, HE20_SALES, HE21_SALES, HE22_SALES, HE23_SALES, HE24_SALES
INTO v_compare_sales (1), v_compare_sales (2), v_compare_sales (3), v_compare_sales (4),
v_compare_sales (5), v_compare_sales (6), v_compare_sales (7), v_compare_sales (8),
v_compare_sales (9), v_compare_sales (10), v_compare_sales (11), v_compare_sales (12),
v_compare_sales (13), v_compare_sales (14), v_compare_sales (15), v_compare_sales (16),
v_compare_sales (17), v_compare_sales (18), v_compare_sales (19), v_compare_sales (20),
v_compare_sales (21), v_compare_sales (22), v_compare_sales (23), v_compare_sales (24)
FROM MYDB.SALES_BY_DAY
WHERE reading_date = TRUNC (p_compare_date) AND location_code = p_location_code;
FOR i
IN (SELECT *
FROM MYDB.SALES_BY_DAY sd
WHERE sd.READING_DATE > (SYSDATE - 3652)
AND sd.READING_DATE != TRUNC(p_compare_date)
AND location_code = p_location_code)
LOOP
v_difference := 0;
SELECT i.HE1_SALES, i.HE2_SALES, i.HE3_SALES, i.HE4_SALES, i.HE5_SALES, i.HE6_SALES,
i.HE7_SALES, i.HE8_SALES, i.HE9_SALES, i.HE10_SALES, i.HE11_SALES, i.HE12_SALES,
i.HE13_SALES, i.HE14_SALES, i.HE15_SALES, i.HE16_SALES, i.HE17_SALES, i.HE18_SALES,
i.HE19_SALES, i.HE20_SALES, i.HE21_SALES, i.HE22_SALES, i.HE23_SALES, i.HE24_SALES
INTO v_curr_sales (1), v_curr_sales (2), v_curr_sales (3), v_curr_sales (4),
v_curr_sales (5), v_curr_sales (6), v_curr_sales (7), v_curr_sales (8),
v_curr_sales (9), v_curr_sales (10), v_curr_sales (11), v_curr_sales (12),
v_curr_sales (13), v_curr_sales (14), v_curr_sales (15), v_curr_sales (16),
v_curr_sales (17), v_curr_sales (18), v_curr_sales (19), v_curr_sales (20),
v_curr_sales (21), v_curr_sales (22), v_curr_sales (23), v_curr_sales (24)
FROM DUAL;
FOR j IN 1 .. v_hours_count
LOOP
v_difference := v_difference + ABS (v_compare_sales (j) - v_curr_sales (j));
END LOOP;
IF (v_difference < 50)
THEN
INSERT INTO MYDB.SALES_ANALYSIS (READING_DATE, location_code, USERID, PROXIMITY)
VALUES (i.READING_DATE, i.location_code, p_userid, v_difference);
END IF;
END LOOP;
COMMIT;
p_message := 'Sales analysis successful. Please review the results';
EXCEPTION
WHEN OTHERS
THEN
ROLLBACK;
p_message := 'Sales analysis was not successful. Error: ' || SQLERRM;
END;
该过程本身非常快(约 1 秒),但我们使用的 IDE 建议在循环中使用 BULK COLLECT 以保持代码清洁度并确保其继续良好运行。我想这样做,但是我无法解决我应该如何选择比较日期的行,然后在使用该方法时将其与所有其他行进行比较。 BULK COLLECT 是解决这个问题的最佳方法,还是有更好的方法来进行这么多比较?
编辑
如果从表本身中选择数据会更容易,那么此视图中的数据来自这样结构的表。
SALES_DATE | HOUR_ENDING | LOCATION_CODE | VALUE
__________________________________________________________
12/27/2019 1 ABCD 40
12/27/2019 2 ABCD 50
12/27/2019 3 ABCD 51
数据必须每小时比较一次,而不是每天的总数(观察下图)。在此示例中,如果每小时比较,则总差异为 35(由于每小时都有 ABS,因为我不在乎差异是负数还是正数……只是多接近)。但是,如果将总数相加,则会返回 9 的差值。
【问题讨论】: