postmeta 的 SQL 联接问题

SQL join issues with postmeta

提问人:kfc06 提问时间:10/26/2023 最后编辑:kfc06 更新时间:10/27/2023 访问量:40

问:

我正在尝试构建一个应用程序,获取用户的位置并找到离当前位置最近的商店。数据存在于 wordpress 后端。我已经成功创建了自定义端点并运行了该函数。除了一个例外,一切都按预期工作。当数据不存在时,它会从返回的结果中删除该记录。

信息存储在自定义帖子类型 (wp_shops) 中,纬度、经度、地址字段等都存储在帖子元中。如果 meta 中存在所有字段,则返回正确,但如果不存在(例如,商店 2 可能不需要使用 address2 字段,因此不填写。如果未填写,则在帖子元表中不存在它)。我以此为起点:https://shorturl.at/gnSX3 及以下是它目前所处的位置。

 Post Meta Example data:

meta_id  |  post_id   |  meta_key         |  meta_value
78652    |  100036    |  wpshops_address  |  Somewhere
78653    |  100036    |  wpshops_address2 |  Someplace
78654    |  100036    |  wpshops_city     |  Somecity
78655    |  100036    |  wpshops_state    |  Somestate
78656    |  100036    |  wpshops_zip      |  AZIP-CODE
78657    |  100036    |  wpshops_country  |  Some Country
78658    |  100036    |  wpshops_phone    |  01234 567 8901
78652    |  100037    |  wpshops_address  |  Somewhere2
78654    |  100037    |  wpshops_city     |  Somecity2
78655    |  100037    |  wpshops_state    |  Somestate2
78656    |  100037    |  wpshops_zip      |  AZIP-CODE2
78657    |  100037    |  wpshops_country  |  Some Country2
78658    |  100037    |  wpshops_phone    |  01234 567 8902

Post Table
ID      |  post_title  |  post_type
100036  |  Shop 1      |  wp_shops
100037  |  Shop 2      |  wp_shops

由于表中缺少“地址 2”,因此它不会返回post_id 100037(商店 2)的任何信息。

感谢任何帮助,即使其中一个字段未显示在元表中,也可以返回结果。

我见过类似的问题,但还没有设法解决它。我尝试过左连接、右连接、为 null 等。但这一切都没有运气。我是mySQL查询的新手,所以我可能错过了显而易见的东西或没有正确构建它。当前代码如下所示:

function my_nearest_shop() {

    global $wpdb;
    $distance = "50 miles";
    $lat =  "37.831313606716186";
    $lng = "-122.038259135056737";
 
    $earth_radius = 3959;

    $sql = $wpdb->prepare( "
        SELECT DISTINCT
            p.ID,
            p.post_title,
            latitude.meta_value as locLat,
            longitude.meta_value as locLong,
            address.meta_value as address,
            address2.meta_value as address2,
            city.meta_value as city,
            state.meta_value as state,
            zip.meta_value as zip,
            country.meta_value as country,
            phone.meta_value as phone,
            ( %d * acos(
            cos( radians( %s ) )
            * cos( radians( latitude.meta_value ) )
            * cos( radians( longitude.meta_value ) - radians( %s ) )
            + sin( radians( %s ) )
            * sin( radians( latitude.meta_value ) )
            ) )
            AS distance
        FROM $wpdb->posts p
    LEFT JOIN $wpdb->postmeta latitude ON p.ID = latitude.post_id
    LEFT JOIN $wpdb->postmeta longitude ON p.ID = longitude.post_id
        LEFT JOIN $wpdb->postmeta address ON p.ID = address.post_id
        LEFT JOIN $wpdb->postmeta address2 ON p.ID = address2.post_id
        LEFT JOIN $wpdb->postmeta city ON p.ID = city.post_id
        LEFT JOIN $wpdb->postmeta state ON p.ID = state.post_id
    LEFT JOIN $wpdb->postmeta zip ON p.ID = zip.post_id
        LEFT JOIN $wpdb->postmeta country ON p.ID = country.post_id
        LEFT JOIN $wpdb->postmeta phone ON p.ID = phone.post_id
        WHERE 1 = 1
        AND p.post_type = 'wp_shops'
        AND p.post_status = 'publish'
        AND latitude.meta_key = 'wpshops_lat'
        AND longitude.meta_key = 'wpshops_lng'
        AND address.meta_key = 'wpshops_address'
        AND address2.meta_key = 'wpshops_address2'
        AND city.meta_key = 'wpshops_city'
        AND state.meta_key = 'wpshops_state'
        AND zip.meta_key = 'wpshops_zip'
        AND country.meta_key = 'wpshops_country'
        AND phone.meta_key = 'wpshops_phone'
        HAVING distance < %s
        ORDER BY distance ASC",
        $earth_radius,
        $lat,
        $lng,
        $lat,
        $distance
);
 
$nearbyLocations = $wpdb->get_results( $sql );
 
if ( $nearbyLocations ) {
return $nearbyLocations;
}
else{
    return "no results";
}
MySQL 的WordPress

评论


答:

0赞 Jelmer 10/26/2023 #1

您是否尝试过简单地不查询 address2 字段?

您似乎只需要一个 ID 来识别商店,以及纬度和经度来计算地理距离。

一旦你有了id的有序列表,你就可以获取你可能需要的任何其他东西。

我想出于性能原因,您正在尝试在一个高效的查询中完成所有操作?我不知道你的数据库有多大,但我无法想象它会产生多大影响,除非你有数百家商店要处理。

1赞 O. Jones 10/27/2023 #2

查询中有此模式来检索 postmeta 的每个项目。

         FROM $wpdb->posts p
    LEFT JOIN $wpdb->postmeta latitude ON p.ID = latitude.post_id
 ...
        WHERE ...
          AND latitude.meta_key = 'wpshops_lat'

将其更改为

         FROM $wpdb->posts p
    LEFT JOIN $wpdb->postmeta latitude ON p.ID = latitude.post_id
                                      AND latitude.meta_key = 'wpshops_lat'
 ...
        WHERE ...

您需要在每个 LEFT JOIN 的 ON 子句中提及meta_key条件,而不是在查询范围的 WHERE 子句中提及。每当您在 WHERE 子句中提到 LEFT JOINed 表中的列时,它都会将 LEFT JOIN 转换为普通的内部 JOIN。这将禁止结果集中缺少元数据的行。

评论

0赞 kfc06 10/27/2023
完美,这完全是诀窍,而且是有道理的。谢谢你的解释!