-- audit_address_sample.sql -- Random sample of EKB houses for the address-mismatch audit (issue #582). -- -- Strategy: -- 1. Filter to houses with non-null lat/lon and non-empty address. -- 2. Random shuffle via `ORDER BY random()` — repeatable enough for spot -- sampling without needing a stable PRNG seed (the audit table dedupes -- via UNIQUE (house_id, audit_batch), so re-running gives idempotent -- results regardless of which rows land in the sample first). -- 3. Cap the result at :limit_per_district * 8 rows — keeps the bind-param -- contract compatible with the old stratified sampler (`:limit_per_district` -- is still honored, just multiplied by the assumed 8-district count). -- -- Why no spatial stratification anymore: -- The previous version JOINed to `gendesign_ekb_districts_geom` (FDW -- polygon table) to bucket houses by admin district. That join is fine on -- prod where FDW is wired, but it adds a dependency we don't need for -- Phase 2-3 (backfill + canonical reverse). Aggregation by district at -- report time still works — we re-derive district during the audit via -- spatial containment in the report SQL when needed. -- -- Bind param: -- :limit_per_district — kept for back-compat with the audit driver. -- Effective sample size = :limit_per_district * 8 (e.g. 25 → 200). -- -- Columns returned: -- id, address, lat, lon, district -- `district` is always NULL here — the audit driver will reverse-derive it -- from Yandex Geocoder response (Yandex returns admin component) or leave -- it NULL if not present in the response. -- -- NB: uses CAST(:x AS int) per project sql.md rule (psycopg v3 ignores ::type -- after bind params). SELECT h.id, h.address, h.lat, h.lon, NULL::text AS district FROM houses h WHERE h.lat IS NOT NULL AND h.lon IS NOT NULL AND h.address IS NOT NULL AND length(trim(h.address)) > 0 ORDER BY random() LIMIT CAST(:limit_per_district AS int) * 8;