我有一个程序,为员工分配“首选车辆”,最后我确保没有员工拥有相同的车辆。
第一个循环得到一个多次出现的所有首选车辆的列表,嵌套循环遍历每个拥有该车辆的员工。最后,只有一名员工将其设置为他/她的首选车辆
--Loop through to see if a pref_veh occurs more than once
FOR every_duplicate_veh IN
(SELECT PREFERRED_VEH FROM FLEET_USER GROUP BY PREFERRED_VEH HAVING COUNT (PREFERRED_VEH) > 1)
LOOP
max_count:=0;
--Loop through all the employees with the duplicate vehicle
FOR every_employee IN (SELECT EMPLOYEE_ID FROM FLEET_USER WHERE PREFERRED_VEH=every_duplicate_veh.PREFERRED_VEH)
LOOP
--Find which employee is assigned the vehicle the most
SELECT COUNT(ASSIGN_VEH_ID) INTO assigned_veh_count FROM FLEET_TRANSACTION
WHERE ASSIGN_VEH_ID=every_duplicate_veh.PREFERRED_VEH AND DRIVER_EMP_ID=every_employee.EMPLOYEE_ID
AND SYSDATE - 30 <= RESERV_START_DT;
IF assigned_veh_count>max_count THEN
max_count:=assigned_veh_count;
preferred_employee_id:=every_employee.EMPLOYEE_ID;
END IF;
--Reset the employee's preferred vehicle to NULL
UPDATE FLEET_USER SET PREFERRED_VEH = NULL WHERE EMPLOYEE_ID = every_employee.EMPLOYEE_ID;
INSERT INTO FLEET_PREF_VEH_LOG VALUES (SYSDATE, every_employee.EMPLOYEE_ID, NULL);
END LOOP;
--One employee will get the preferred vehicle
UPDATE FLEET_USER SET PREFERRED_VEH = every_duplicate_veh.PREFERRED_VEH WHERE EMPLOYEE_ID = preferred_employee_id;
INSERT INTO FLEET_PREF_VEH_LOG VALUES (SYSDATE, preferred_employee_id, every_duplicate_veh.PREFERRED_VEH);
COMMIT;
END LOOP;
FLEET_USER是一个包含数千行的表...我的目标是消除嵌套循环......我可以这样做吗?我仍然是sql的新手,所以我真的很感激任何建议/指出我错过的任何东西
答案 0 :(得分:3)
首先,插入FLEET_PREF_VEH_LOG
会让我更好地充当触发器,而不是一个程序。通过这种方式卸载它,我认为您可以将其减少为单个SQL语句。
我没有您的对象或数据,所以这是完全未经测试的,但我认为它与您现有的代码非常接近,应该会带来性能提升。
MERGE INTO fleet_user fu_m
USING (SELECT employee_id,
cnt,
MAX (cnt) OVER (PARTITION BY fm_s.preferred_veh)
AS max_cnt
FROM (SELECT fu.employee_id, fu.preferred_veh, COUNT (*) AS cnt
FROM fleet_user fu
JOIN fleet_transaction ft
ON ft.assign_veh_id = fu.preferred_veh
AND ft.driver_emp_id = fu.employee_id
WHERE EXISTS
(SELECT preferred_veh
FROM fleet_user fu2
WHERE fu2.preferred_veh =
fu.preferred_veh
GROUP BY preferred_veh
HAVING COUNT (preferred_veh) > 1)
AND SYSDATE - 30 <= ft.reserv_start_dt
GROUP BY fu.employee_id, fu.preferred_veh)) fm_s
ON (fm_m.employee_id = fm_s.employee_id)
WHEN MATCHED THEN
UPDATE SET preferred_veh = NULL
WHERE max_cnt <> cnt;
此方法的另一个优点是可以防止将不必要的记录写入FLEET_PREF_VEH_LOG
。使用您当前的代码(通过我的阅读),首选用户的首选项将被清除,然后重新设置为其原始值。当没有任何实际改变时,这会产生两个FLEET_PREF_VEH_LOG
记录。
正如评论中指出的那样,这个答案并不涵盖并列计数。问题中的代码确实如此,但它是非确定性的:在最后一次胜利处理的员工中,并列。由于数据库的变幻莫测以及缺少ORDER BY
,每次都不能保证这一点。
我更愿意添加一种确定性手段来打破关系,即使它是任意的:
MERGE INTO fleet_user fu_m
USING (SELECT employee_id,
cnt,
MAX (cnt) OVER (PARTITION BY fm_s.preferred_veh)
AS max_cnt,
ROW_NUMBER () OVER (PARTITION BY cnt ORDER BY employee_id)
AS tie_order
FROM (SELECT fu.employee_id, fu.preferred_veh, COUNT (*) AS cnt
FROM fleet_user fu
JOIN fleet_transaction ft
ON ft.assign_veh_id = fu.preferred_veh
AND ft.driver_emp_id = fu.employee_id
WHERE EXISTS
(SELECT preferred_veh
FROM fleet_user fu2
WHERE fu2.preferred_veh =
fu.preferred_veh
GROUP BY preferred_veh
HAVING COUNT (preferred_veh) > 1)
AND SYSDATE - 30 <= ft.reserv_start_dt
GROUP BY fu.employee_id, fu.preferred_veh)) fm_s
ON (fm_m.employee_id = fm_s.employee_id)
WHEN MATCHED THEN
UPDATE SET preferred_veh = NULL
WHERE max_cnt <> cnt or tie_order <> 1;
ROW_NUMBER () OVER (PARTITION BY cnt order by employee_id)
将为每个员工提供相同的计数,从最低employee_id
到最高{。}}。当没有平局时,所有值都将为1
,因此查询将与以前完全一样。但是当存在平局时,除了具有最低employee_id
的那个领带之外的每个成员都将被设置为空。
显然,您可以将ORDER BY
更改为您选择的任意标准。
我已经更改了上述两个查询,以满足评论中提出的最新问题(我认为)。仅通过车辆编号获得最大count
,我们确保我们分别处理每辆车。