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:
- Truy vấn không gian trên bảng lớn có nhiều liên kết tự hoạt động chậm
- Làm cách nào để truy vấn tất cả các hàng trong bán kính 5 dặm tính từ tọa độ của tôi?
Đọc thêm:
- http://workshops.boundlessgeo.com/postgis-intro/knn. html
- http://www.postgresonline.com/journal/archives/306-KNN-GIST-with-a-Lateral-twist-Coming-soon-to-a-database-near- you.html
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