批处理作业跟踪通知

时间:2013-03-13 06:13:14

标签: php mysql

目前我们正在使用三个嵌套的foreach循环来获取运行批处理的信息。但是我很确定我们可以使用带有连接和子查询的单个MySQL语句来获取信息。

我们有大约30个类别,2000个用户。我们的目标是大约100个类别,拥有100000个用户,但显然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);
            }
        }
   }
}

所需的结果是以user_id为键的job_id数组数组 [USER_ID] =&GT; {作业}

e.g。

    {
        [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";

2 个答案:

答案 0 :(得分:1)

在我看来,你拿起的距离无论如何都取自用户表(distance_from_center field?)

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

答案 1 :(得分: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

这将为每个作业创建一个列表,所有想要通知它的用户ID。我添加了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);
?>

你去,你想要的数组,工作排序最靠近。