4

現時点では、ネストされた 3 つの foreach ループを使用して、バッチを実行するための情報を取得しています。ただし、結合とサブクエリを使用した単一の MySQL ステートメントで情報を取得できると確信しています。

2000 人のユーザーを持つ約 30 のカテゴリがあります。私たちの目標は、100000 人のユーザーを持つ約 100 のカテゴリですが、明らかに foreach ループは理想的ではありません (現在でも実行に約 1 分かかります)。

状況: ユーザーは、特定の領域で行うことができる取引に利用可能な仕事がある場合に通知を受け取りたいと考えています。

目標: バッチ プロセス (毎日、毎週など) の通知を送信トレイに入れる

テクノロジー: 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]=>{jobs}

例えば

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

編集:

1 人のユーザーのすべてのジョブを選択できるので、もう少し効率的です。ただし、それでも 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";
4

2 に答える 2

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
于 2013-03-18T14:45:19.270 に答える
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、users テーブルでユーザーの詳細も取得できるようになりました。

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 で簡単に作成できます。

現在、毎日/毎週の間隔を実装する方法はたくさんあります。そのうちの 1 つは、間隔ごとに個別のスクリプトを作成し、それを設定したユーザーのみを選択することです。たとえば、毎日実行するスクリプト「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);
?>

ほら、ジョブが最初に最も近いものでソートされた、必要な配列です。

于 2013-03-21T09:59:15.020 に答える