使用联接和条件聚合的SQL更新

时间:2019-06-05 13:19:21

标签: sql postgresql

我在PostgreSQL 11.3上有两个表:

销售

+------------+-----------+--------+----------+-----------+
| event_week | store_num | fsaldu | latitude | longitude |
+------------+-----------+--------+----------+-----------+
|     201946 |     11249 | K2K3C2 | null     | null      |
|     201947 |     11250 | V6B0G5 | null     | null      |
|     201948 |     11251 | N9H0G6 | null     | null      |
+------------+-----------+--------+----------+-----------+

fsa_latlong

+--------+-------+------+--------------------+-----------+
| fsaldu | fsa5  | fsa4 |      latitude      | longitude |
+--------+-------+------+--------------------+-----------+
| K2K3C2 | K2K32 | K2K3 | 47.006479999999996 |  -52.9587 |
| V6B0G5 | V6B0G | V6B0 | 47.416990000000006 | -53.19438 |
| N9H0G1 | N9H0G | N9H0 | 47.326370000000004 | -52.80969 |
| N9H0G2 | N9H0G | N9H0 | 47.326370000000004 | -52.80969 |
| N9H0G3 | N9H0G | N9H0 | 47.326370000000004 | -52.80969 |
| N9H0G4 | N9H0G | N9H0 | 47.326370000000004 | -52.80969 |
| N9H0G5 | N9H0G | N9H0 | 47.326370000000004 | -52.80969 |
+--------+-------+------+--------------------+-----------+

我想执行联接以更新 fsa_latlong 表中 sales 表中的纬度经度 。这是通过以下查询完成的:

update sales s
set latitude = l.latitude,
    longitude = l.longitude
from fsa_latlong l
where s.fsaldu = l.fsaldu;

但是,由于邮递区号已退役,销售表中的某些 fsaldu 值在 fsa_latlong 表中不匹配或重新分配。

因此,我要完成的工作是从 fsa5 fsa4 纬度经度 >不能与整个邮政编码完全匹配。

我的第一次尝试是在第一次连接完成后使用第二个查询。

update sales s
set latitude = (SELECT AVG(l.latitude)
                  FROM fsa_latlong l
                  WHERE s.latitude is null
                  AND (
                      l.fsa5 = substring(s.fsaldu, 1, 5)
                      OR l.fsa4 = substring(s.fsaldu, 1, 4)
                            )),
    longitude = (SELECT AVG(l.longitude)
                  FROM fsa_latlong l
                  WHERE s.longitude is null
                  AND (
                      l.fsa5 = substring(s.fsaldu, 1, 5)
                      OR l.fsa4 = substring(s.fsaldu, 1, 4)
                            ));

但是,这没有按要求进行。理想情况下,我想通过一个查询来完成此操作,我怀疑它使用CASE语句或COALESCE来计算初始匹配为null时的汇总。

任何帮助,我们将不胜感激。

3 个答案:

答案 0 :(得分:1)

类似的东西应该可以工作(未经测试):

WITH
average_values AS (
    SELECT    s.fsaldu,
              AVG(l.latitute)  AS avg_latitude,
              AVG(l.longitude) AS avg_longitude
    FROM      sales s
    LEFT
    JOIN      fsa_latlong   fl
       ON     l.fsa5 = substring(s.fsaldu, 1, 5)
       OR     l.fsa4 = substring(s.fsaldu, 1, 4)
    GROUP BY 1
),
selected_values AS (
    SELECT   av.fsaldu,
             coalesce(fl.laitude,   av.avg_latitude)  AS latitude,
             coalesce(fl.longitude, av.avg_longitude) AS longitue
    FROM     average_values av
    LEFT
    JOIN     fsa_latlong   fl
        ON   wf.fsaldu = fl.fsaldu
)
UPDATE sales s
SET latitude = sv.latitude,
    longitude = sv.longitude
FROM selected_values sv
WHERE s.fsaldu = sv.fsaldu;

注意:

  • 您是否有充分的理由写“我想用一个查询完成此操作”?通常最好有两个单独的查询,而不是一个过于复杂的
  • 我的查询效率不高,因为计算了sales表中所有fsaldu值的平均值。可以轻松改善这一点。

另一项一般性说明-将CTE用于子查询,CTE具有可读性和高效性(例如,您不必在单独的位置计算经度/纬度)。

答案 1 :(得分:0)

我个人比较喜欢逐步进行这种事情。在这种情况下,通过测试前面的步骤是否失败(通过纬度/经度为NULL)来确定条件。


UPDATE sales s
SET latitude = l.latitude
    ,longitude = l.longitude
FROM fsa_latlong l
WHERE s.fsaldu = l.fsaldu;

UPDATE sales s
SET latitude = agg.latitude
    ,longitude = agg.longitude  
FROM (SELECT l.fsa5
        , AVG(l.latitude) AS latitude
        , AVG(l.longitude) AS longitude
        FROM fsa_latlong l
        GROUP BY l.fsa5
        ) agg
WHERE agg.fsa5 = substring(s.fsaldu, 1, 5)
AND (s.latitude is null OR s.longitude is null)
        ;

UPDATE sales s
SET latitude = agg.latitude
    ,longitude = agg.longitude
FROM (SELECT l.fsa4
        , AVG(l.latitude) AS latitude
        , AVG(l.longitude) AS longitude
        FROM fsa_latlong l
        GROUP BY l.fsa4
        ) agg
WHERE agg.fsa4 = substring(s.fsaldu, 1, 4)
AND (s.latitude is null OR s.longitude is null)
        ;

答案 2 :(得分:0)

让我假设sales每行都有一个唯一的ID,我将其称为sale_id

UPDATE sales
    SET latitude = COALESCE(l.latitude, l5.latitude, l4.latitude),
        longitude = COALESCE(l.longitude, l5.longitude, l4.longitude)
FROM sales s LEFT JOIN
     fsa_latlong l
     ON s.fsaldu = l.fsaldu LEFT JOIN
     (SELECT SUBSTR(fsaldu, 1, 5) fsaldu5, AVG(latitude) as latitude, AVG(longitude) as longitude
      FROM fsa_latlong l
      GROUP BY SUBSTR(fsaldu, 1, 5)
     ) l5
     ON l5.fsaldu5 = SUBSTR(s.fsaldu, 1, 5) LEFT JOIN
     (SELECT SUBSTR(fsaldu, 1, 4) as fsaldu4, AVG(latitude) as latitude, AVG(longitude) as longitude
      FROM fsa_latlong l
      GROUP BY SUBSTR(fsaldu, 1, 4)
     ) l4
     ON l4.fsaldu4 = SUBSTR(s.fsaldu, 1, 4) 
WHERE s.sales_id = sales.sales_id;