This guide covers working with data in FindU’s Supabase environments, including seeding test data, performing CRUD operations, and safely handling production data.
-- Generate realistic test data with loopsDO $$DECLARE i integer; student_id uuid; school_ids uuid[];BEGIN -- Get all school IDs SELECT ARRAY_AGG(id) INTO school_ids FROM public.schools; -- Create 100 test students FOR i IN 1..100 LOOP student_id := gen_random_uuid(); INSERT INTO public.students ( id, name, email, phone, gpa, sat_score, graduation_year, preferred_size, rigor ) VALUES ( student_id, 'Test Student ' || i, 'student' || i || '.test@example.com', '555-' || LPAD(i::text, 4, '0'), (2.5 + random() * 2)::numeric(3,2), -- GPA 2.5-4.5 (1000 + random() * 600)::integer, -- SAT 1000-1600 (2024 + (random() * 3)::integer)::text, CASE (random() * 3)::integer WHEN 0 THEN 'Small' WHEN 1 THEN 'Medium' ELSE 'Large' END, CASE (random() * 3)::integer WHEN 0 THEN 'Low' WHEN 1 THEN 'Medium' ELSE 'High' END ); -- Create 5-15 interactions per student FOR j IN 1..(5 + (random() * 10)::integer) LOOP INSERT INTO public.student_school_interactions ( student_id, school_id, viewed, liked, match_score ) VALUES ( student_id, school_ids[1 + (random() * array_length(school_ids, 1))::integer], true, random() > 0.5, (50 + random() * 50)::integer::text ); END LOOP; END LOOP;END $$;
-- Complex update with subqueryUPDATE public.students sSET match_count = ( SELECT COUNT(*) FROM public.student_school_interactions ssi WHERE ssi.student_id = s.id AND ssi.liked = true AND ssi.match_score::integer > 70)WHERE s.graduation_year = '2024';-- Batch operations with CTEsWITH inactive_students AS ( SELECT id FROM public.students WHERE NOT EXISTS ( SELECT 1 FROM public.student_school_interactions WHERE student_id = students.id AND interaction_date > NOW() - INTERVAL '30 days' ))UPDATE public.studentsSET status = 'inactive'WHERE id IN (SELECT id FROM inactive_students);-- Complex data migrationINSERT INTO public.partner_affiliations (partner_user_id, entity_id, role)SELECT pu.id, pe.id, 'admissions_officer'FROM public.partner_users puCROSS JOIN public.partner_entities peWHERE pu.email LIKE '%@' || SUBSTRING(pe.name FROM '.* ([^ ]+)$') || '.edu'ON CONFLICT (partner_user_id, entity_id) DO NOTHING;
-- Always use transactions for multiple operationsBEGIN;-- Your operations hereUPDATE public.students SET status = 'active' WHERE id = 'uuid';INSERT INTO public.activity_log (action, student_id) VALUES ('activated', 'uuid');-- Verify before committingSELECT * FROM public.students WHERE id = 'uuid';SELECT * FROM public.activity_log WHERE student_id = 'uuid' ORDER BY created_at DESC LIMIT 1;-- If good, commit; otherwise rollbackCOMMIT;-- or-- ROLLBACK;
-- Create temporary tableCREATE TEMP TABLE import_schools ( name text, city text, state text, size integer);-- Copy CSV data (in Supabase, use Table Editor import)COPY import_schools FROM '/path/to/schools.csv' CSV HEADER;-- Insert with validationINSERT INTO public.schools (name, city, state, size, api_id)SELECT name, city, state, size, 'IMPORT-' || gen_random_uuid()::textFROM import_schoolsWHERE name IS NOT NULL AND city IS NOT NULL AND state IS NOT NULL;
-- Import from JSONWITH json_data AS ( SELECT jsonb_array_elements('[ {"name": "Test College", "city": "Boston", "state": "MA"}, {"name": "Sample University", "city": "NYC", "state": "NY"} ]'::jsonb) AS school)INSERT INTO public.schools (name, city, state, api_id)SELECT school->>'name', school->>'city', school->>'state', 'JSON-' || gen_random_uuid()::textFROM json_data;
-- Export for analysis (remove PII)COPY ( SELECT s.graduation_year, s.gpa, s.preferred_size, COUNT(ssi.id) as interactions, COUNT(CASE WHEN ssi.liked THEN 1 END) as likes FROM public.students s LEFT JOIN public.student_school_interactions ssi ON s.id = ssi.student_id GROUP BY s.graduation_year, s.gpa, s.preferred_size) TO '/tmp/student_analytics.csv' CSV HEADER;-- Export for backup (dev only)pg_dump $DATABASE_URL \ --table=students \ --table=schools \ --table=student_school_interactions \ --data-only \ --file=dev_backup.sql
-- Function to anonymize PIICREATE OR REPLACE FUNCTION anonymize_students()RETURNS void AS $$DECLARE r record; counter integer := 1;BEGIN FOR r IN SELECT id FROM public.students LOOP UPDATE public.students SET name = 'Student ' || counter, email = 'student' || counter || '@example.com', phone = '555-' || LPAD(counter::text, 4, '0'), address = counter || ' Test Street' WHERE id = r.id; counter := counter + 1; END LOOP;END;$$ LANGUAGE plpgsql;-- Run anonymization (DEV ONLY!)SELECT anonymize_students();
Production data access should be restricted and audited. Never run write operations directly in production.
-- Read-only queries only-- Example: Checking data integritySELECT COUNT(*) as total_students, COUNT(DISTINCT email) as unique_emails, COUNT(CASE WHEN email IS NULL THEN 1 END) as missing_emailsFROM public.studentsWHERE deleted_at IS NULL;-- Monitoring queriesSELECT DATE(created_at) as signup_date, COUNT(*) as new_studentsFROM public.studentsWHERE created_at > NOW() - INTERVAL '30 days'GROUP BY DATE(created_at)ORDER BY signup_date DESC;
-- Use indexes effectivelyEXPLAIN ANALYZESELECT * FROM public.student_school_interactionsWHERE student_id = 'uuid' AND liked = true;-- Paginate large resultsSELECT * FROM public.studentsWHERE graduation_year = '2024'ORDER BY created_at DESCLIMIT 50OFFSET 100;-- Use CTEs for complex queriesWITH active_students AS ( SELECT DISTINCT student_id FROM public.student_school_interactions WHERE interaction_date > NOW() - INTERVAL '7 days')SELECT s.*FROM public.students sJOIN active_students a ON s.id = a.student_idORDER BY s.name;
INSERT INTO public.students (email, name)SELECT 'new@example.com', 'New Student'WHERE NOT EXISTS ( SELECT 1 FROM public.students WHERE email = 'new@example.com');
Foreign key constraints
Ensure referenced records exist:
-- Check before insertSELECT COUNT(*) FROM public.schools WHERE id = 'school-uuid';-- Or use ON CONFLICTINSERT INTO public.student_school_interactions (student_id, school_id)VALUES ('student-uuid', 'school-uuid')ON CONFLICT DO NOTHING;
RLS blocking operations
Test with service role key or check policies:
-- Check current userSELECT auth.uid();-- Test policySELECT * FROM public.studentsWHERE auth.uid() IN ( SELECT user_id FROM public.profiles WHERE student_id = students.id);
Performance issues
Identify slow queries:
-- Enable timing\timing on-- Run query with explainEXPLAIN (ANALYZE, BUFFERS) SELECT ... your slow query ...;-- Check missing indexesSELECT schemaname, tablename, attname, n_distinct, correlationFROM pg_statsWHERE tablename = 'your_table';