payroll-source-bulk.sql 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. -- Recompute authoritative source rows directly; no result cache or derived tables.
  2. -- Bulk lookup maps eliminate one common/appointment lookup per lesson.
  3. CREATE OR REPLACE FUNCTION xs_payroll_sources_bulk(co text)
  4. RETURNS SETOF jsonb LANGUAGE plpgsql STABLE SET jit=off SET work_mem='64MB' AS $$
  5. BEGIN
  6. IF NOT xs_payroll_authority_ready() OR EXISTS (
  7. SELECT 1 FROM "CourseAppointment" WHERE company=co AND id IS NOT NULL GROUP BY id HAVING count(*)>1
  8. ) THEN
  9. RETURN QUERY SELECT s FROM xs_payroll_sources(co) s;
  10. RETURN;
  11. END IF;
  12. RETURN QUERY
  13. WITH common59 AS MATERIALIZED (
  14. SELECT DISTINCT ON (c."itemId") c.* FROM "CommonModel" c
  15. WHERE c.company=co AND c."modelId"=59
  16. ORDER BY c."itemId",c."objectId"
  17. ), appointment_map AS MATERIALIZED (
  18. -- Join before choosing: the earliest CommonModel can refer to a missing appointment.
  19. SELECT DISTINCT ON (ac."generalId"::text) ac."generalId"::text AS join_key,a AS appointment
  20. FROM "CommonModel" ac JOIN "CourseAppointment" a ON a.company=co AND a.id=ac."itemId"
  21. WHERE ac.company=co AND ac."modelId"=54
  22. ORDER BY ac."generalId"::text,ac."objectId"
  23. ), lessons AS MATERIALIZED (
  24. SELECT l AS lesson,row_number() OVER () AS ord FROM "LessonRecord" l WHERE l.company=co
  25. ), canonical_info AS MATERIALIZED (
  26. SELECT l.lesson,l.ord,c AS common,
  27. to_jsonb(COALESCE(c."generalId",(l.lesson)."legacyGeneralId",0))#>>'{}' AS legacy_text,
  28. CASE WHEN COALESCE(c."generalId",(l.lesson)."legacyGeneralId",0)>0
  29. THEN 'legacy:'||COALESCE(c."generalId",(l.lesson)."legacyGeneralId")::text
  30. ELSE 'lesson:'||(l.lesson)."objectId" END AS standard_key
  31. FROM lessons l LEFT JOIN common59 c ON c."itemId"=(l.lesson).id
  32. ), canonical_keys AS MATERIALIZED (
  33. SELECT legacy_text FROM canonical_info
  34. ), facts AS MATERIALIZED (
  35. SELECT f AS fact,row_number() OVER () AS ord,
  36. CASE WHEN COALESCE(f."legacyGeneralId",0)>0 THEN 'legacy:'||f."legacyGeneralId"::text ELSE 'fact:'||f."objectId" END AS standard_key
  37. FROM "LessonRecordFact" f WHERE f.company=co AND NOT EXISTS (
  38. SELECT 1 FROM canonical_keys k WHERE k.legacy_text=f."legacyGeneralId"::text AND COALESCE(f."legacyGeneralId",0)>0
  39. )
  40. ), ambiguous AS MATERIALIZED (
  41. SELECT EXISTS (SELECT 1 FROM (
  42. SELECT standard_key FROM canonical_info UNION ALL SELECT standard_key FROM facts
  43. ) k GROUP BY standard_key HAVING count(*)>1) AS value
  44. ), output AS (
  45. SELECT 0 AS source_group,x.ord,
  46. (to_jsonb(l)-'kzsj')||jsonb_build_object(
  47. 'lessonObjectId',l."objectId",'legacyGeneralId',COALESCE(c."generalId",l."legacyGeneralId",0),
  48. 'standardLessonKey',CASE WHEN COALESCE(c."generalId",l."legacyGeneralId",0)>0 THEN 'legacy:'||COALESCE(c."generalId",l."legacyGeneralId")::text ELSE 'lesson:'||l."objectId" END,
  49. 'source','canonical','commonStatus',COALESCE(c."status",99),
  50. 'lessonAt',COALESCE(l."lessonAt",xs_payroll_time(l."plpjsj"),xs_payroll_time(a."jssj"),l."sourceCreatedAt",c."createdAt"),
  51. 'courseCategoryKey',COALESCE(NULLIF(l."courseCategoryKey",''),a."courseCategoryKey"),
  52. 'courseCategoryName',COALESCE(NULLIF(l."courseCategoryName",''),a."courseCategoryName"),
  53. 'teacherPaySnapshot',COALESCE(l."teacherPaySnapshot",a."teacherPaySnapshot"),
  54. 'durationMinutes',a."durationMinutes",'deliveryMode',COALESCE(NULLIF(l."jffs",''),a."deliveryMode",a."jffs"),
  55. 'teachingRuleId',a."teachingRuleId",'studentName',COALESCE(NULLIF(l."studentName",''),c."title"),
  56. 'courseName',COALESCE(NULLIF(l."courseName",''),NULLIF(l."kcmc",''),c."subtitle")
  57. ) AS row_data
  58. FROM canonical_info x
  59. CROSS JOIN LATERAL (SELECT (x.lesson).*) l
  60. CROSS JOIN LATERAL (SELECT (x.common).*) c
  61. LEFT JOIN appointment_map am ON am.join_key=l."yyds"::text
  62. LEFT JOIN LATERAL (SELECT (am.appointment).*) a ON true
  63. WHERE NOT (SELECT value FROM ambiguous)
  64. UNION ALL
  65. SELECT 1 AS source_group,x.ord,
  66. (to_jsonb(f)-'kzsj')||jsonb_build_object('lessonObjectId',f."objectId",'source','historical-fact','commonStatus',99,'courseCategoryKey',a."courseCategoryKey",'courseCategoryName',a."courseCategoryName",'teacherPaySnapshot',a."teacherPaySnapshot",'durationMinutes',a."durationMinutes",'deliveryMode',COALESCE(NULLIF(f."jffs",''),a."deliveryMode",a."jffs"),'teachingRuleId',a."teachingRuleId",
  67. 'standardLessonKey',CASE WHEN COALESCE(f."legacyGeneralId",0)>0 THEN 'legacy:'||f."legacyGeneralId"::text ELSE 'fact:'||f."objectId" END) AS row_data
  68. FROM facts x CROSS JOIN LATERAL (SELECT (x.fact).*) f
  69. LEFT JOIN appointment_map am ON am.join_key=f."yyds"::text
  70. LEFT JOIN LATERAL (SELECT (am.appointment).*) a ON true
  71. WHERE NOT (SELECT value FROM ambiguous)
  72. UNION ALL
  73. -- A changed join order must never choose a different duplicate representative.
  74. SELECT 0,s.ord,s.row_data FROM ambiguous guard
  75. CROSS JOIN LATERAL xs_payroll_sources(co) WITH ORDINALITY AS s(row_data,ord)
  76. WHERE guard.value
  77. ) SELECT output.row_data FROM output ORDER BY output.source_group,output.ord;
  78. END $$;