2013-03-13 153 views
4

目前我們使用三個嵌套的foreach循環來獲取運行批處理的信息。不過,我相當肯定我們可以通過帶有聯接和子查詢的單個MySQL語句獲取信息。批處理作業跟蹤通知

我們有大約30個類別,2000用戶。我們的目標是100個用戶,大約100個類別,但顯然這些foreach循環並不理想(即使現在它們只需要一分鐘左右的時間)。

境遇: 用戶希望是否有可用的人,他們在某方面做貿易工作的通知

目標: 批量處理(每天,每週等)將通知把發件箱中

技術: PHP,MySQL的

我迄今爲止:

數據庫:

"table.notification_options" : [id][user_id][category] 
"table.user" : [id][user_id][method_of_contact][contact_frequency][center_of_work_area_long][center_of_work_area_lat][distance_from_center] 
"table.work" : [id][post_date][longitude][latitude][category] 

代碼:

foreach user{ 
    foreach category tracked{ 
     foreach job in category posted <> $current_date-$batch_frequency{ 
      if job inside workspace{ 
       notify_user(job); 
      } 
     } 
    } 
} 

期望的結果是job_ids的陣列與USER_ID數組作爲鍵 [user_id] => {jobs}

例如

{ 
     [user1]{ 
       job1, 
       job4, 
       job28 
       }, 
     [user34]{ 
       job3, 
       job4, 
       job34, 
       job78 
       } 
    { 

編輯:

我找到了更有效的一點,我可以選擇的所有作業的一個用戶。但它仍然需要一個foreach用戶。

$category_id = get_category_from_notification_options($userid); 
    $user_distance = get_user_work_distance($userid); 
    "SELECT DISTINCT work.ID as workID, (6371 * acos(cos(radians(-46.409939)) * cos(radians(jobs.lat)) * cos(radians(jobs.lng) - radians(168.366180)) + sin(radians(-46.409939)) * sin(radians(jobs.lat)))) 
     AS distance 
     FROM work,user 
     WHERE work.categoryID == $category_id 
     HAVING distance < $user_distance 
     ORDER BY distance"; 
+0

目標:將批處理(每日,每週等)通知放入發件箱 - ??? 發件箱? – Karma 2013-03-18 04:48:08

+0

對不起,我不明白你的問題。澄清:用戶跟蹤他們指定的區域(他們所在位置的半徑)的工作是否可用。該腳本的工作是以特定的時間間隔運行,並收集自上次運行併發出通知後所有發佈在其區域中的作業信息。發件箱是消息發送給用戶的發送隊列 - 就像所有的電子郵件/ txt等作品一樣。 – 2013-03-18 11:11:19

+0

我建議的SQL將在幾乎一個SQL語句中執行此操作,只需在php – Kickstart 2013-03-25 10:41:25

回答

1

我認爲你應該以相反的方式來做到這一點,以提高效率。下面我將向您展示我用來創建查詢的過程。所以只有最後的查詢是你需要的。但我解釋了這些步驟,所以或許它會在未來幫助你。

首先我要選擇所有的工作。如果您的目標是100,000個用戶,那麼最有可能的就是那些用戶較少的工作。

select JOB.id, JOB.category 
FROM table.work JOB 

現在我們有所有的工作,讓我們看看哪些用戶希望被通知它。

select JOB.id, JOB.category, NOTIFY.user_id 
FROM table.work JOB 
LEFT JOIN table.notification_options NOTIFY 
ON JOB.category=NOTIFY.category 
WHERE NOTIFY.user_id IS NOT NULL 

這會爲每個作業創建一個列表,列出所有要通知它的用戶標識。我添加了WHERE子句來刪除沒有人想看到的列表中的所有作業。 現在我們可以用JOIN這個用戶表來獲取用戶的詳細信息了。

select JOB.id 
    , JOB.post_date 
    , JOB.longitude 
    , JOB.latitude 
    , USR.user_id 
    , USR.method_of_contact 
    , USR.contact_frequency 
    , USR.center_of_work_area_long 
    , USR.center_of_work_area_lat 
    , USR.distance_from_center 
    , ((ACOS(SIN(USR.center_of_work_area_lat * PI()/180) * SIN(JOB.latitude * PI()/180) + COS(USR.center_of_work_area_lat * PI()/180) * COS(JOB.latitude * PI()/180) * COS((USR.center_of_work_area_long – JOB.longitude) * PI()/180)) * 180/PI()) * 60 * 1.1515) AS `distance` 
FROM table.work JOB 
LEFT JOIN table.notification_options NOTIFY 
ON JOB.category=NOTIFY.category 
LEFT JOIN table.user USR 
ON NOTIFY.user_id=USR.user_id 
WHERE NOTIFY.user_id IS NOT NULL 
HAVING `distance`<=USR.distance_from_center 
ORDER BY USR.user_id ASC, distance ASC 

我在查詢中包含了距離。請注意,我使用HAVING來檢查距離是否比用戶提供的距離小。如果您將其添加到WHERE子句中,則會出現錯誤,說明distance是未知列。 我還添加了ORDER BY類,首先在用戶ID和距離上進行排序。這將使您更容易在PHP中創建所需的數組。

現在有很多方法來實現每日/每週的時間間隔。其中之一是爲每個間隔創建單獨的腳本,並只選擇設置它的用戶。 例如,你可以創建一個腳本「daily.php」您運行的每一天,有以下查詢

select JOB.id 
    , JOB.post_date 
    , JOB.longitude 
    , JOB.latitude 
    , USR.user_id 
    , USR.method_of_contact 
    , USR.contact_frequency 
    , USR.center_of_work_area_long 
    , USR.center_of_work_area_lat 
    , USR.distance_from_center 
    , ((ACOS(SIN(USR.center_of_work_area_lat * PI()/180) * SIN(JOB.latitude * PI()/180) + COS(USR.center_of_work_area_lat * PI()/180) * COS(JOB.latitude * PI()/180) * COS((USR.center_of_work_area_long – JOB.longitude) * PI()/180)) * 180/PI()) * 60 * 1.1515) AS `distance` 
FROM table.work JOB 
LEFT JOIN table.notification_options NOTIFY 
ON JOB.category=NOTIFY.category 
LEFT JOIN table.user USR 
ON NOTIFY.user_id=USR.user_id 
WHERE NOTIFY.user_id IS NOT NULL 
AND USR.contact_frequency = 'daily' 
HAVING `distance`<=USR.distance_from_center 
ORDER BY USR.user_id ASC, distance ASC 

現在我們有查詢,讓我們創建的PHP代碼它。我們可以循環遍歷所有行並創建數組。顯然,不是創建數組,你也可以直接處理結果。因爲如果你首先創建一個數組,你確實需要在之後再次循環該數組。

<?php 
$arNotify = array(); 
foreach ($queryresult as $row) { 
    $userid = $row->user_id; 
    $jobid = $row->id; 

    //check if there is an entry for the user in the database, else create it 
    if (!array_key_exists($userid, $arNotify)) 
    $arNotify[$userid] = array(); 

    //and then push the job 
    $arNotify[$userid][] = $jobid; 

    //the array is being created, but I still like to process the job directly 
    //notify_user($userid, $jobid); 

} 

var_dump($arNotify); 
?> 

你去哪裏,按照你想要的數組,按照最接近的第一個排序的作業。

+0

中完成任何格式化即可。您的先生是冠軍。 – 2013-03-26 00:37:17

+0

謝謝。爲了壞你沒有獎勵賞金 – 2013-03-26 08:40:29

+0

這太糟糕了!我沒有意識到它會過期,對不起雨果。 – 2013-03-26 22:48:57

1

在我看來,就好像你拿起距離是從用戶表採取反正(distance_from_center場?)

SELECT DISTINCT ser.user_id, work.ID as workID, (6371 * acos(cos(radians(-46.409939)) * cos(radians(jobs.lat)) * cos(radians(jobs.lng) - radians(168.366180)) + sin(radians(-46.409939)) * sin(radians(jobs.lat)))) AS distance 
FROM notification_options 
INNER JOIN jobs ON notification_options.category = jobs.category 
INNER JOIN user ON notification_options.user_id = user.user_id 
HAVING distance < user.distance_from_center 
ORDER BY distance 

編輯 - 如果你只是想爲每個用戶的工作列表在距離目(如果需要的話,你可能會爆炸到一個數組處理PHP中 - 雖然可能更容易使用上面的查詢建立起來的數組),那麼你可以使用這樣的事情: -

SELECT user_id, GROUP_CONCAT(workID ORDER BY distance) 
FROM (
SELECT DISTINCT ser.user_id, work.ID as workID, (6371 * acos(cos(radians(-46.409939)) * cos(radians(jobs.lat)) * cos(radians(jobs.lng) - radians(168.366180)) + sin(radians(-46.409939)) * sin(radians(jobs.lat)))) AS distance 
FROM notification_options 
INNER JOIN jobs ON notification_options.category = jobs.category 
INNER JOIN user ON notification_options.user_id = user.user_id 
HAVING distance < user.distance_from_center) Sub1