PostgreSQL
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> PostgreSQL

Chỉ định duy nhất các điểm gần nhất giữa hai bảng

Lược đồ bảng

Để thực thi quy tắc của bạn, chỉ cần khai báo pvanlagen.buildid UNIQUE :

ALTER TABLE pvanlagen ADD CONSTRAINT pvanlagen_buildid_uni UNIQUE (buildid);

building.gid là PK, như bản cập nhật của bạn được tiết lộ. Để thực thi tính toàn vẹn của tham chiếu, hãy thêm TỪ KHÓA NGOẠI TỆ ràng buộc tới building.gid .

Bây giờ bạn đã thực hiện cả hai. Nhưng sẽ hiệu quả hơn nếu chạy UPDATE lớn dưới trước bạn thêm những ràng buộc này.

Còn rất nhiều thứ nữa cần được cải thiện trong định nghĩa bảng của bạn. Đối với một, building.gid cũng như pvanlagen.buildid phải là loại số nguyên (hoặc có thể là bigint nếu bạn đốt cháy rất nhiều giá trị PK). số là điều vô nghĩa đắt tiền.

Hãy tập trung vào vấn đề cốt lõi:

Truy vấn cơ bản để tìm tòa nhà gần nhất

Trường hợp này không đơn giản như nó có vẻ. Đó là "hàng xóm gần nhất" > vấn đề, với sự phức tạp bổ sung của nhiệm vụ duy nhất.

Truy vấn này tìm một gần nhất xây dựng cho mỗi PV (viết tắt của PV Anlage - hàng trong pvanlagen ), nơi chưa được chỉ định:

SELECT pv_gid, b_gid, dist
FROM  (
   SELECT gid AS pv_gid, ST_Transform(geom, 31467) AS geom31467
   FROM   pvanlagen
   WHERE  buildid IS NULL  -- not assigned yet
   ) p
     , LATERAL (
   SELECT b.gid AS b_gid
        , round(ST_Distance(p.geom31467
                      , ST_Transform(b.centroid, 31467))::numeric, 2) AS dist  -- see below
   FROM   buildings b
   LEFT   JOIN pvanlagen p1 ON p1.buildid = b.gid  -- also not assigned ...
   WHERE  p1.buildid IS NULL                       -- ... yet  
   -- AND    p.gemname = b.gemname                 -- not needed for performance, see below
   ORDER  BY p.geom31467 <-> ST_Transform(b.centroid, 31467)
   LIMIT  1
   ) b;

Để thực hiện truy vấn này nhanh chóng, bạn cần chỉ mục GiST chức năng, không gian trên tòa nhà để làm cho nó nhiều nhanh hơn:

CREATE INDEX build_centroid_gix ON buildings USING gist (ST_Transform(centroid, 31467));

Không chắc chắn tại sao bạn không

Các câu trả lời liên quan có giải thích thêm:

Đọc thêm:

Với chỉ mục tại chỗ, chúng tôi không cần giới hạn các kết quả phù hợp với cùng một gemname cho hiệu suất. Chỉ làm điều này nếu đó là một quy tắc thực sự cần thực thi. Nếu nó phải được quan sát mọi lúc, hãy đưa cột vào ràng buộc FK:

Vấn đề còn lại

Chúng ta có thể sử dụng truy vấn trên trong UPDATE bản tường trình. Mỗi PV chỉ được sử dụng một lần, nhưng nhiều PV vẫn có thể tìm thấy cùng một tòa nhà để được gần nhất. Bạn chỉ cho phép một PV mỗi tòa nhà. Vậy bạn sẽ giải quyết điều đó như thế nào?

Nói cách khác, bạn sẽ gán các đối tượng ở đây như thế nào?

Giải pháp đơn giản

Một giải pháp đơn giản sẽ là:

UPDATE pvanlagen p1
SET    buildid = sub.b_gid
     , dist    = sub.dist  -- actual distance
FROM  (
   SELECT DISTINCT ON (b_gid)
          pv_gid, b_gid, dist
   FROM  (
      SELECT gid AS pv_gid, ST_Transform(geom, 31467) AS geom31467
      FROM   pvanlagen
      WHERE  buildid IS NULL  -- not assigned yet
      ) p
        , LATERAL (
      SELECT b.gid AS b_gid
           , round(ST_Distance(p.geom31467
                         , ST_Transform(b.centroid, 31467))::numeric, 2) AS dist  -- see below
      FROM   buildings      b
      LEFT   JOIN pvanlagen p1 ON p1.buildid = b.gid  -- also not assigned ...
      WHERE  p1.buildid IS NULL                       -- ... yet  
      -- AND    p.gemname = b.gemname                 -- not needed for performance, see below
      ORDER  BY p.geom31467 <-> ST_Transform(b.centroid, 31467)
      LIMIT  1
      ) b
   ORDER  BY b_gid, dist, pv_gid  -- tie breaker
   ) sub
WHERE   p1.gid = sub.pv_gid;

Tôi sử dụng DISTINCT ON (b_gid) để giảm xuống chính xác một hàng trên mỗi tòa nhà, chọn PV với khoảng cách ngắn nhất. Chi tiết:

Đối với bất kỳ tòa nhà nào gần nhất cho nhiều PV, chỉ PV gần nhất được chỉ định. Cột PK gid (bí danh pv_gid ) đóng vai trò là tiebreaker nếu hai cột gần bằng nhau. Trong trường hợp như vậy, một số PV bị loại khỏi bản cập nhật và vẫn chưa được chỉ định . Lặp lại truy vấn cho đến khi tất cả PV được chỉ định.

Đây vẫn là một thuật toán đơn giản , Tuy nhiên. Nhìn vào sơ đồ của tôi ở trên, điều này chỉ định tòa nhà 4 cho PV 4 và tòa nhà 5 cho PV 5, trong khi 4-5 và 5-4 có lẽ sẽ là một giải pháp tốt hơn về tổng thể ...

Aside:gõ cho dist cột

Hiện tại, bạn sử dụng số cho nó. truy vấn ban đầu của bạn đã chỉ định một số nguyên không đổi , không có ý nghĩa gì trong số .

Trong truy vấn mới của tôi, ST_Distance () trả về khoảng cách thực tế tính bằng mét dưới dạng double độ chính xác . Nếu chúng tôi chỉ định đơn giản rằng chúng tôi nhận được 15 chữ số thập phân hoặc hơn trong số kiểu dữ liệu và số không phải là cái đó chính xác để bắt đầu. Tôi thực sự nghi ngờ bạn muốn lãng phí bộ nhớ.

Tôi muốn lưu double precision ban đầu khỏi tính toán. hoặc tốt hơn , làm tròn khi cần thiết. Nếu mét đủ chính xác, chỉ cần truyền tới và lưu một số nguyên (làm tròn số tự động). Hoặc nhân với 100 trước để tiết kiệm cm:

(ST_Distance(...) * 100)::int



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Các tính năng PostgreSQL Enterprise mới và đang phát triển với các bản phát hành gần đây

  2. Cách thay đổi giá trị mặc định của postgresql.conf trên Openshift

  3. Thêm chỉ mục để tăng tốc Bộ mã hóa địa lý gần tìm kiếm

  4. Hibernate + PostgreSQL:quan hệ không tồn tại - Lỗi SQL:0, SQLState:42P01

  5. Mối quan hệ giữa Django Postgres ArrayField vs One-to-Many