← back to Ventura Corridor

db/migrations/013_building_roster.sql

65 lines

-- Migration 013 — building roster view + multi-tenant grouping
-- Powers /buildings.html — one card per address showing every tenant + walked/pitched status.
-- Strips SUITE/STE/UNIT/# tokens from address to derive a canonical building key
-- (street_name in businesses table has the suite concatenated — see iter 53 notes).

CREATE OR REPLACE VIEW v_building_canonical AS
SELECT
  b.id,
  b.name,
  b.address,
  b.city,
  b.zip,
  b.lat,
  b.lng,
  b.phone,
  b.website,
  b.category,
  b.category_naics,
  b.on_corridor,
  -- Canonical building address: strip everything from "SUITE"/"STE"/"UNIT"/"#" onward
  TRIM(regexp_replace(b.address, '\s*(SUITE|STE|UNIT|#).*$', '', 'i')) AS bldg_address,
  -- Suite token alone, or '' if none. substring(...from'pattern') is scalar, not set-returning.
  COALESCE(
    substring(b.address from '(?:SUITE|STE|UNIT|#)\s*[#]?\s*([A-Z0-9-]+)'),
    ''
  ) AS suite,
  b.raw->>'primary_naics_description' AS naics
FROM businesses b
WHERE b.on_corridor
  AND b.address IS NOT NULL;

CREATE OR REPLACE VIEW v_building_roster AS
SELECT
  bc.bldg_address,
  bc.city,
  bc.zip,
  -- Only average geocoded points that fall in the LA basin
  -- (BTRC has bad lat/lng for ~5% of rows — some ended up in AZ/NM)
  AVG(bc.lat) FILTER (WHERE bc.lat BETWEEN 33.7 AND 34.5 AND bc.lng BETWEEN -118.9 AND -118.1) AS lat,
  AVG(bc.lng) FILTER (WHERE bc.lat BETWEEN 33.7 AND 34.5 AND bc.lng BETWEEN -118.9 AND -118.1) AS lng,
  count(*)                                                                          AS tenants_total,
  count(*) FILTER (WHERE p.id IS NOT NULL)                                          AS pitched,
  count(*) FILTER (WHERE p.outreach_channel = 'walk-in' AND p.sent_at IS NOT NULL)  AS walked,
  count(*) FILTER (WHERE p.replied_at IS NOT NULL)                                  AS replied,
  count(*) FILTER (WHERE p.status = 'won')                                          AS won,
  count(*) FILTER (WHERE p.status = 'skip')                                         AS skipped,
  count(*) FILTER (WHERE p.status = 'draft' AND p.sent_at IS NULL)                  AS draft,
  count(*) FILTER (WHERE p.id IS NULL)                                              AS unpitched,
  -- Distance from DW (15442 Ventura — 34.1542, -118.4703).
  -- Equirectangular approximation; 1° lat = 69.1mi, 1° lng @ 34°N ≈ 57.3mi.
  ROUND(
    (SQRT(
      POW((AVG(bc.lat) FILTER (WHERE bc.lat BETWEEN 33.7 AND 34.5 AND bc.lng BETWEEN -118.9 AND -118.1) - 34.1542) * 69.1, 2) +
      POW((AVG(bc.lng) FILTER (WHERE bc.lat BETWEEN 33.7 AND 34.5 AND bc.lng BETWEEN -118.9 AND -118.1) - -118.4703) * 57.3, 2)
    ))::numeric, 2
  ) AS dw_miles,
  -- Most-common pitch_type for this building
  MODE() WITHIN GROUP (ORDER BY p.pitch_type) FILTER (WHERE p.pitch_type IS NOT NULL) AS dominant_pitch_type
FROM v_building_canonical bc
LEFT JOIN pitches p ON p.business_id = bc.id
WHERE bc.bldg_address IS NOT NULL AND bc.bldg_address <> ''
GROUP BY bc.bldg_address, bc.city, bc.zip
HAVING count(*) >= 2
ORDER BY count(*) DESC;