提高SQL开发人员嵌套循环中存储过程的性能

提高SQL开发人员嵌套循环中存储过程的性能,sql,oracle,performance,Sql,Oracle,Performance,我有一个计划,为员工分配一辆首选车辆,最后我确保没有员工拥有相同的车辆 第一个循环获取所有首选车辆的列表,这些车辆多次出现,嵌套循环遍历拥有该车辆的每个员工。最终,只有一名员工将其设置为其首选车辆 --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 HAV

我有一个计划,为员工分配一辆首选车辆,最后我确保没有员工拥有相同的车辆

第一个循环获取所有首选车辆的列表,这些车辆多次出现,嵌套循环遍历拥有该车辆的每个员工。最终,只有一名员工将其设置为其首选车辆

--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还是相当陌生,因此我非常感谢您提供任何建议/指出我遗漏的任何内容

首先,插入到FLEET\u PREF\u VEH\u日志中让我觉得它更适合作为触发器,而不是过程的一部分。通过这种方式卸载,我认为您可以将其简化为一条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;
按cnt order BY employee_id分区上的ROW_编号将为每个计数相同的员工提供一个序列号,从最低的员工id到最高的员工id。当并没有关联时,所有的值都将是1,所以查询将和以前一样工作。但是,当存在平局时,平局的每个成员(员工id最低的成员除外)都将设置为null

显然,您可以根据您选择的任意条件更改顺序


我已经改变了上述两个问题,以满足我认为在评论中提出的最新问题。通过仅按车辆编号获取最大计数,我们可以确保单独处理每辆车。

是否最好先正确设置值,而不重复,而不是稍后尝试清理?我认为,在这个部分中,您可以同时多次更新同一行,作为旁白;您将所有匹配的行都清空,然后将其中一行恢复到最初的状态。看起来您希望将首选车辆设置为过去30天内使用次数最多的车辆,如果您是该车辆的第二大用户,您最终没有首选车辆,对吗?可能希望查看此网站:。你可能会有更好的运气。这很好,但是如果两个或两个以上的员工之间有关系,这就不适合这种情况。如果有多个重复的车辆怎么办?例如,如果员工A和B拥有车辆ABC,ABC的最大计数为18,但员工C和D拥有车辆DEF,此车辆的最大计数为15。。。此代码仅显示最大计数为18。现在C和D都没有DEF了,因为15=18.明白了-不是一分一,而是一分一。这给了我正确的数字。18用于车辆ABC,15用于车辆DEF。
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;