2015-01-26 53 views
1

我有一個程序,爲員工分配「首選車輛」,並最終確保沒有員工擁有相同的車輛。提高SQL Developer中的存儲過程的性能 - 嵌套循環

第一個循環獲取不止一次出現的所有首選車輛的列表,並且嵌套循環遍歷每個擁有該車輛的員工。最後,只有一個員工都會有它設置爲他/她的首選車輛

--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

將數值正確設置爲開始而不重複,而不是稍後嘗試清理會更好嗎?在本節中,您可以在此刻多次更新同一行,我認爲這是一個旁白;你將所有匹配的行都清空,然後把它放回原來的樣子。看起來你希望在過去的30天內將首選車輛設置爲最常用的車輛,並且如果你是該車輛的第二高用戶,那麼最終沒有偏好,對吧? – 2015-01-26 19:39:58

+0

可能想看看這個網站:http://codereview.stackexchange.com/。你可能會有更好的運氣。 – macoms01 2015-01-26 19:46:29

回答

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,我們確保我們正在分別處理每輛車。

+0

這很好,但如果兩個或兩個以上員工之間存在聯繫,則不能適應這種情況。 – memo 2015-04-08 18:15:03

+0

如果有多輛重複車輛會怎麼樣?例如,如果員工A和B擁有車輛ABC,並且ABC的最大數量爲18,但是員工C和D以及車輛DEF,並且該車輛的最大數量爲15 ...此代碼僅顯示最大數量是18.現在C和D都不會有DEF,因爲15!= 18。 – memo 2015-04-09 15:05:14

+0

明白了 - 而不是一個分區,由preferred_veh分區。這給了我正確的數字。車輛ABC爲18,車輛DEF爲15。 – memo 2015-04-09 15:19:00