⧍⧍ āĻŽā§‡, ⧍ā§Ļ⧍ā§Ģ¡5 āĻŽāĻŋāύāĻŋāϟ āĻĒāĻĄāĻŧāϤ⧇

āĻ…ā§āϝāĻžāĻĄāĻŽāĻŋāύ āĻĢāĻŋāĻ˛ā§āϟāĻžāϰ⧇āϰ āϜāĻ¨ā§āϝ PostgreSQL generated columns āĻĻāĻŋā§Ÿā§‡ āĻĻā§āϰ⧁āϤāϤāĻž

PostgreSQL generated columns āϕ⧀āĻ­āĻžāĻŦ⧇ āĻ…ā§āϝāĻžāĻĄāĻŽāĻŋāύ āĻ¸ā§āĻ•ā§āϰāĻŋāύ⧇āϰ āĻĢāĻŋāĻ˛ā§āϟāĻžāϰ āĻ“ āϏ⧋āĻ°ā§āϟāĻŋāĻ‚ āĻĻā§āϰ⧁āϤ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇, āωāĻĻāĻžāĻšāϰāĻŖ āĻ“ āĻĻā§āϰ⧁āϤ āĻšā§‡āĻ•āĻŋāĻ‚ āϏāĻš āĻĒāĻĄāĻŧ⧁āύāĨ¤

āĻ…ā§āϝāĻžāĻĄāĻŽāĻŋāύ āĻĢāĻŋāĻ˛ā§āϟāĻžāϰ⧇āϰ āϜāĻ¨ā§āϝ PostgreSQL generated columns āĻĻāĻŋā§Ÿā§‡ āĻĻā§āϰ⧁āϤāϤāĻž

āϕ⧇āύ āĻ…ā§āϝāĻžāĻĄāĻŽāĻŋāύ āĻ¸ā§āĻ•ā§āϰāĻŋāύ āĻĻā§āϰ⧁āϤ āϧ⧀āϰ āĻāĻŦāĻ‚ āϜāϟāĻŋāϞ āĻšā§Ÿā§‡ āĻ“āϠ⧇\n\nāĻ…ā§āϝāĻžāĻĄāĻŽāĻŋāύ āĻ¸ā§āĻ•ā§āϰāĻŋāύāϗ⧁āϞ⧋ āϏāĻžāϧāĻžāϰāĻŖāϤ āϏāϰāϞāĻ­āĻžāĻŦ⧇ āĻļ⧁āϰ⧁ āĻšā§Ÿ: āĻāĻ•āϟāĻŋ āĻŸā§‡āĻŦāĻŋāϞ, āĻ•ā§Ÿā§‡āĻ•āϟāĻž āĻĢāĻŋāĻ˛ā§āϟāĻžāϰ, āĻšā§ŸāϤ⧋ “āύāϤ⧁āύāϗ⧁āϞ⧋ āĻĒā§āϰāĻĨāĻŽâ€ āύāĻžāĻŽā§‡ āĻāĻ•āϟāĻŋ āϏ⧋āĻ°ā§āϟāĨ¤ āϤāĻžāϰāĻĒāϰ āĻ•āĻžāϜ āĻŦāĻžā§œāϤ⧇ āĻļ⧁āϰ⧁ āĻ•āϰ⧇āĨ¤ āϏāĻžāĻĒā§‹āĻ°ā§āϟ āϚāĻžāχāĻŦ⧇ āύāĻžāĻŽ, āχāĻŽā§‡āχāϞ, āĻĢā§‹āύ āĻĻāĻŋā§Ÿā§‡ āĻ–ā§‹āρāϜ āĻ•āϰāϤ⧇āĨ¤ āϏ⧇āϞāϏ āϚāĻžāχāĻŦ⧇ “āĻļ⧇āώ āĻ•āĻžāĻ°ā§āϝāĻ•āϞāĻžāĻĒ” āĻ…āύ⧁āϝāĻžā§Ÿā§€ āϏāĻžāϜāĻžāϤ⧇āĨ¤ āĻĢāĻžāχāĻ¨ā§āϝāĻžāĻ¨ā§āϏ āϚāĻžāχāĻŦ⧇ “āĻŦāĻ•ā§‡ā§ŸāĻž āĻŦā§āϝāĻžāϞāĻžāĻ¨ā§āĻ¸â€ āĻ…āύ⧁āϝāĻžā§Ÿā§€ āĻĢāĻŋāĻ˛ā§āϟāĻžāϰāĨ¤ āĻĒā§āϰāϤāĻŋ āĻ…āύ⧁āϰ⧋āϧ⧇ āĻļāĻ°ā§āϤ, āĻœā§Ÿā§‡āύ, āĻāĻŦāĻ‚ āĻ…āϤāĻŋāϰāĻŋāĻ•ā§āϤ āĻ—āĻŖāύāĻž āĻŦāĻžā§œā§‡āĨ¤\n\nāĻ…āϧāĻŋāĻ•āĻžāĻ‚āĻļ āĻ…ā§āϝāĻžāĻĄāĻŽāĻŋāύ āϤāĻžāϞāĻŋāĻ•āĻž āĻāĻ•āχ āĻ•āĻžāϰāϪ⧇ āϧ⧀āϰ āĻšā§Ÿ: āĻĒā§āϰāϤāĻŋāϟāĻŋ āĻ•ā§āϞāĻŋāĻ• āĻ•ā§ā§Ÿā§‡āϰāĻŋ āĻŦāĻĻāϞ⧇ āĻĻā§‡ā§ŸāĨ¤ āĻĢāĻŋāĻ˛ā§āϟāĻžāϰ āĻ“ āϏ⧋āĻ°ā§āϟ āĻ•āϰāĻžāϰ āϏāĻŽā§Ÿ āĻĄāĻžāϟāĻžāĻŦ⧇āϏ āĻ…āύ⧇āĻ• āϰ⧋ āĻ¸ā§āĻ•ā§āϝāĻžāύ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇, āĻŦāĻŋāĻļ⧇āώāϤ āϝāĻ–āύ āĻ•ā§ā§Ÿā§‡āϰāĻŋ āĻĒā§āϰāϤāĻŋāϟāĻŋ āϰ⧋āϤ⧇ āĻāĻ•āϟāĻŋ āĻŽāĻžāύ āĻšāĻŋāϏāĻžāĻŦ āĻ•āϰ⧇ āϤāĻžāϰāĻĒāϰ āϏāĻŋāĻĻā§āϧāĻžāĻ¨ā§āϤ āĻ¨ā§‡ā§Ÿ āϕ⧋āύāϟāĻŋ āĻŽā§āϝāĻžāϚ āĻ•āϰ⧇āĨ¤\n\nāĻāĻ•āϟāĻŋ āϏāĻžāϧāĻžāϰāĻŖ āĻŽā§‹ā§œ āφāϏ⧇ āϝāĻ–āύ WHERE āĻāĻŦāĻ‚ ORDER BY āĻāĻ•ā§āϏāĻĒā§āϰ⧇āĻļāύ āĻĻāĻŋā§Ÿā§‡ āĻ­āϰ⧇ āĻ“āϠ⧇āĨ¤ āϏāĻžāϧāĻžāϰāĻŖ āĻ•āϞāĻžāĻŽā§‡āϰ āĻŦāĻĻāϞ⧇ āφāĻĒāύāĻŋ lower(email), date_trunc('day', last_seen_at), āĻŦāĻž āĻāĻ•āĻžāϧāĻŋāĻ• āĻ¸ā§āĻŸā§āϝāĻžāϟāĻžāϏāϕ⧇ āĻāĻ• “āĻŦāĻžāϕ⧇āĻŸâ€ āĻ āĻŽāĻžāύāϚāĻŋāĻ¤ā§āϰ āĻ•āϰāϤ⧇ CASE āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇ āĻĢāĻŋāĻ˛ā§āϟāĻžāϰ āĻ•āϰ⧇āύāĨ¤ āĻāχ āĻāĻ•ā§āϏāĻĒā§āϰ⧇āĻļāύāϗ⧁āϞ⧋ āĻļ⧁āϧ⧁āχ āϧ⧀āϰ āύ⧟ — āĻāϗ⧁āϞ⧋ SQL āĻĒ⧜āϤ⧇ āĻ•āĻ āĻŋāύ āĻ•āϰ⧇, āχāύāĻĄā§‡āĻ•ā§āϏ āĻ•āϰāĻž āĻ•āĻ āĻŋāύ āĻ•āϰ⧇, āĻāĻŦāĻ‚ āϏāĻšāĻœā§‡ āϭ⧁āϞ āĻšāĻ“ā§ŸāĻžāϰ āϏ⧁āϝ⧋āĻ— āϤ⧈āϰāĻŋ āĻ•āϰ⧇āĨ¤\n\nāĻ…āĻ—ā§‹āĻ›āĻžāϞ⧋ āĻ…ā§āϝāĻžāĻĄāĻŽāĻŋāύ SQL āϏāĻžāϧāĻžāϰāĻŖāϤ āĻ•ā§Ÿā§‡āĻ•āϟāĻŋ āĻĒ⧁āύāϰāĻžāĻŦ⧃āĻ¤ā§āϤ āĻĒā§āϝāĻžāϟāĻžāĻ°ā§āύ āĻĨ⧇āϕ⧇āχ āϜāĻ¨ā§āĻŽāĻžā§Ÿ:\n\n- āĻāĻ•āϟāĻŋ “āϏāĻžāĻ°ā§āĻšâ€ āχāύāĻĒ⧁āϟ āϝāĻž āĻŦāĻŋāĻ­āĻŋāĻ¨ā§āύ āύāĻŋ⧟āĻŽā§‡ āĻāĻ•āĻžāϧāĻŋāĻ• āĻĢāĻŋāĻ˛ā§āĻĄ āĻšā§‡āĻ• āĻ•āϰ⧇\n- āĻ‰ā§ŽāĻĒāĻ¨ā§āύ āĻŽāĻžāύ āĻĻāĻŋā§Ÿā§‡ āϏ⧋āĻ°ā§āϟ āĻ•āϰāĻž (āĻĢ⧁āϞ āύ⧇āĻŽ, āĻĒā§āϰāĻžāϧāĻžāĻ¨ā§āϝ āĻ¸ā§āϕ⧋āϰ, “āĻļ⧇āώ āĻ…āĻ°ā§āĻĨāĻĒā§‚āĻ°ā§āύ āχāϭ⧇āĻ¨ā§āĻŸâ€)\n- āĻŦā§āϝāĻŦāϏāĻžāϝāĻŧāĻŋāĻ• āύāĻŋ⧟āĻŽāϗ⧁āϞ⧋ āĻ¸ā§āĻ•ā§āϰāĻŋāύ āĻœā§ā§œā§‡ āĻ•āĻĒāĻŋ āĻ•āϰāĻž (active vs inactive, paid vs overdue)\n- āϛ⧋āϟ “āĻšā§‡āĻ˛ā§āĻĒāĻžāĻ°â€ āϟ⧁āχāĻ• (trim, lower, coalesce) āĻ›ā§œāĻŋā§Ÿā§‡ āĻĨāĻžāĻ•āĻž\n- āĻāĻ•āχ āĻ—āĻŖāĻŋāϤ āĻ•āϰāĻž āĻŽāĻžāύ āϤāĻžāϞāĻŋāĻ•āĻž, āĻĢāĻŋāĻ˛ā§āϟāĻžāϰ, āĻāĻŦāĻ‚ āϏ⧋āĻ°ā§āϟāĻŋāĻ‚ āĻ āĻŦāĻžāϰāĻŦāĻžāϰ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻšā§Ÿ\n\nāϟāĻŋāĻŽāϗ⧁āϞ⧋ āĻĒā§āϰāĻžāϝāĻŧāχ āĻāϟāĻŋ āĻ…ā§āϝāĻžāĻĒ āĻ˛ā§‡ā§ŸāĻžāϰ⧇ āϞ⧁āĻ•āĻžāϤ⧇ āϚāĻžā§Ÿ: āĻĄāĻžā§ŸāύāĻžāĻŽāĻŋāĻ• āĻ•ā§ā§Ÿā§‡āϰāĻŋ āĻŦāĻŋāĻ˛ā§āĻĄāĻžāϰ, āĻļāĻ°ā§āϤāĻ­āĻŋāĻ¤ā§āϤāĻŋāĻ• āĻœā§Ÿā§‡āύ, āĻŦāĻž āϕ⧋āĻĄā§‡ āĻĒā§āϰāĻŋāĻ•āĻŽā§āĻĒāĻŋāωāĻŸā§‡āĻĄ āĻŽāĻžāύāĨ¤ āϤāĻž āĻ•āĻžāϜ āĻ•āϰāϞ⧇āĻ“, āĻāϟāĻŋ UI āĻāĻŦāĻ‚ āĻĄāĻžāϟāĻžāĻŦ⧇āϏ⧇āϰ āĻŽāĻ§ā§āϝ⧇ āϞāϜāĻŋāĻ• āĻ­āĻžāĻ— āĻ•āϰ⧇ āĻĻā§‡ā§Ÿ, āĻĢāϞ⧇ āϧ⧀āϰ āĻ•ā§ā§Ÿā§‡āϰāĻŋ āĻĄāĻŋāĻŦāĻžāĻ— āĻ•āϰāĻž āĻ•āĻˇā§āϟāϏāĻžāĻ§ā§āϝ āĻšā§Ÿā§‡ āĻĒā§œā§‡āĨ¤\n\nāϞāĻ•ā§āĻˇā§āϝ āϏ⧋āϜāĻž: āĻĻā§āϰ⧁āϤ āĻ•ā§ā§Ÿā§‡āϰāĻŋ āϝ⧇āϗ⧁āϞ⧋ āĻĒ⧜āϤ⧇ āϏāĻšāϜ āĻĨāĻžāϕ⧇āĨ¤ āϝāĻ–āύ āϕ⧋āύ⧋ āĻšāĻŋāϏāĻžāĻŦ āĻ•āϰāĻž āĻŽāĻžāύ āĻŦāĻžāϰāĻŦāĻžāϰ āĻ…ā§āϝāĻžāĻĄāĻŽāĻŋāύ āĻ¸ā§āĻ•ā§āϰāĻŋāύ⧇ āĻĻ⧇āĻ–āĻž āϝāĻžā§Ÿ, PostgreSQL generated columns āϏ⧇āχ āύāĻŋ⧟āĻŽāϕ⧇ āĻāĻ• āϜāĻžā§ŸāĻ—āĻžā§Ÿ āϰ⧇āϖ⧇ āĻĄāĻžāϟāĻžāĻŦ⧇āϏāϕ⧇ āĻ…āĻĒā§āϟāĻŋāĻŽāĻžāχāϜ āĻ•āϰāĻžāϰ āϏ⧁āϝ⧋āĻ— āĻĻā§‡ā§ŸāĨ¤\n\n## āĻœā§‡āύāĻžāϰ⧇āĻŸā§‡āĻĄ āĻ•āϞāĻžāĻŽ āϏāϰāϞ āĻ­āĻžāώāĻžā§Ÿ\n\nāĻāĻ•āϟāĻŋ generated column āĻšāĻšā§āϛ⧇ āĻŸā§‡āĻŦāĻŋāϞ⧇āϰ āĻāĻ• āϏāĻžāϧāĻžāϰāĻŖ āĻ•āϞāĻžāĻŽ āϝāĻžāϰ āĻŽāĻžāύ āĻ…āĻ¨ā§āϝ āĻ•āϞāĻžāĻŽāϗ⧁āϞ⧋ āĻĨ⧇āϕ⧇ āĻšāĻŋāϏāĻžāĻŦ āĻ•āϰāĻž āĻšā§ŸāĨ¤ āφāĻĒāύāĻŋ āĻŽāĻžāύāϟāĻŋ āύāĻŋāĻœā§‡ āϞāĻŋāϖ⧇āύ āύāĻžāĨ¤ PostgreSQL āφāĻĒāύāĻžāϰ āĻĻ⧇āĻ“ā§ŸāĻž āĻāĻ•ā§āϏāĻĒā§āϰ⧇āĻļāύ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇ āĻāϟāĻŋ āĻĒā§‚āϰāĻŖ āĻ•āϰ⧇āĨ¤\n\nPostgreSQL-āĻ generated columns āĻ¸ā§āĻŸā§‹āϰ āĻšā§ŸāĨ¤ PostgreSQL āĻāĻ•āϟāĻŋ āϰ⧋ āχāύāϏāĻžāĻ°ā§āϟ āĻ…āĻĨāĻŦāĻž āφāĻĒāĻĄā§‡āϟ āĻšāϞ⧇ āĻŽāĻžāύāϟāĻŋ āĻ—āĻŖāύāĻž āĻ•āϰ⧇, āϤāĻžāϰāĻĒāϰ āĻĄāĻŋāĻ¸ā§āϕ⧇ āĻ…āĻ¨ā§āϝ āĻ•āϞāĻžāĻŽā§‡āϰ āĻŽāϤ⧋āχ āϏāĻ‚āϰāĻ•ā§āώāĻŖ āĻ•āϰ⧇āĨ¤ āĻ…ā§āϝāĻžāĻĄāĻŽāĻŋāύ āĻ¸ā§āĻ•ā§āϰāĻŋāύ⧇āϰ āϜāĻ¨ā§āϝ āĻāϟāĻŋāχ āϏāĻžāϧāĻžāϰāĻŖāϤ āϚāĻžāύ: āĻĻā§āϰ⧁āϤ āϰāĻŋāĻĄ āĻāĻŦāĻ‚ āĻ—āĻŖāĻŋāϤ āĻ•āϰāĻž āĻŽāĻžāύ āχāύāĻĄā§‡āĻ•ā§āϏ āĻ•āϰāĻžāϰ āĻ•ā§āώāĻŽāϤāĻžāĨ¤\n\nāĻāϟāĻŋ āĻĒā§āϰāϤāĻŋāϟāĻŋ āĻ•ā§ā§Ÿā§‡āϰāĻŋāϰ āϭ⧇āϤāϰ⧇ āĻāĻ•āχ āĻ—āĻŖāύāĻž āĻŦāĻžāϰ āĻŦāĻžāϰ āĻ•āϰāĻžāϰ āĻĨ⧇āϕ⧇ āφāϞāĻžāĻĻāĻžāĨ¤ āϝāĻĻāĻŋ āφāĻĒāύāĻŋ āĻŦāĻžāϰāĻŦāĻžāϰ āϞāĻŋāϖ⧇āύ WHERE lower(email) = lower($1) āĻŦāĻž ORDER BY last_name || ', ' || first_name — āφāĻĒāύāĻŋ āĻŦāĻžāϰāĻŦāĻžāϰ āĻ–āϰāϚ āĻĻ⧇āĻŦ⧇āύ āĻāĻŦāĻ‚ SQL āĻ—ā§‹āϞāĻŽā§‡āϞ⧇ āĻšāĻŦ⧇āĨ¤ āĻāĻ•āϟāĻŋ generated column āϏ⧇āχ āĻĒ⧁āύāϰāĻžāĻŦ⧃āĻ¤ā§āϤ āĻ—āĻŖāύāĻžāϕ⧇ āĻŸā§‡āĻŦāĻŋāϞ āϏāĻ‚āĻœā§āĻžāĻžā§Ÿ āϏāϰāĻŋā§Ÿā§‡ āĻĻā§‡ā§ŸāĨ¤ āφāĻĒāύāĻžāϰ āĻ•ā§ā§Ÿā§‡āϰāĻŋ āϏāϰāϞ āĻšā§Ÿ, āĻāĻŦāĻ‚ āĻĢāϞāĻžāĻĢāϞ āϏāĻŦ āϜāĻžā§ŸāĻ—āĻžā§Ÿ āϏāĻ™ā§āĻ—āϤāĻŋāĻĒā§‚āĻ°ā§āĻŖ āĻĨāĻžāϕ⧇āĨ¤\n\nāϝāĻ–āύ āϏ⧋āĻ°ā§āϏ āĻĄā§‡āϟāĻž āĻŦāĻĻāϞ⧇ āϝāĻžā§Ÿ, PostgreSQL āĻ¸ā§āĻŦ⧟āĻ‚āĻ•ā§āϰāĻŋ⧟āĻ­āĻžāĻŦ⧇ āϐ āϰ⧋-āĻāϰ āϜāĻ¨ā§āϝ generated āĻŽāĻžāύ āφāĻĒāĻĄā§‡āϟ āĻ•āϰ⧇āĨ¤ āφāĻĒāύāĻžāϰ āĻ…ā§āϝāĻžāĻĒāϕ⧇ āϤāĻž āĻ¸ā§āĻŽāϰāĻŖ āϰāĻžāĻ–āĻžāϰ āĻĻāϰāĻ•āĻžāϰ āύ⧇āχāĨ¤\n\nāĻāĻ•āϟāĻŋ āϏāĻšāϜ āĻŽā§‡āĻ¨ā§āϟāĻžāϞ āĻŽāĻĄā§‡āϞ:\n\n- āϏ⧂āĻ¤ā§āϰ āĻāĻ•āĻŦāĻžāϰ āϏāĻ‚āĻœā§āĻžāĻžā§ŸāĻŋāϤ āĻ•āϰ⧁āύāĨ¤\n- PostgreSQL āϞāĻŋāĻ–āĻžāϰ āϏāĻŽā§Ÿ āĻāϟāĻŋ āĻ—āĻŖāύāĻž āĻ•āϰ⧇āĨ¤\n- āĻ•ā§ā§Ÿā§‡āϰāĻŋ āĻāϟāĻŋ āϏāĻžāϧāĻžāϰāĻŖ āĻ•āϞāĻžāĻŽā§‡āϰ āĻŽāϤ⧋ āĻĒā§œā§‡āĨ¤\n- āϝ⧇āĻšā§‡āϤ⧁ āĻāϟāĻŋ āĻ¸ā§āĻŸā§‹āϰ āĻ•āϰāĻž āĻšā§Ÿ, āφāĻĒāύāĻŋ āĻāϟāĻŋ āχāύāĻĄā§‡āĻ•ā§āϏāĻ“ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇āύāĨ¤\n\nāĻĒāϰāĻŦāĻ°ā§āϤ⧀ āϏāĻŽā§Ÿā§‡ āϏ⧂āĻ¤ā§āϰ āĻŦāĻĻāϞāĻžāϞ⧇, āφāĻĒāύāĻžāϰ āĻāĻ•āϟāĻŋ āĻ¸ā§āĻ•āĻŋāĻŽāĻž āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ āϞāĻžāĻ—āĻŦ⧇āĨ¤ āĻāϟāĻŋ āϝ⧇āϕ⧋āύ āĻŽāĻžāχāĻ—ā§āϰ⧇āĻļāύ⧇āϰ āĻŽāϤ⧋ āĻĒāϰāĻŋāĻ•āĻ˛ā§āĻĒāύāĻž āĻ•āϰ⧁āύ, āĻ•āĻžāϰāĻŖ āĻŦāĻŋāĻĻā§āϝāĻŽāĻžāύ āϰ⧋ āϗ⧁āϞ⧋āĻ“ āύāϤ⧁āύ āĻāĻ•ā§āϏāĻĒā§āϰ⧇āĻļāύ⧇āϰ āϏāĻ™ā§āϗ⧇ āφāĻĒāĻĄā§‡āϟ āĻšāĻŦ⧇āĨ¤\n\n## āĻĢāĻŋāĻ˛ā§āϟāĻžāϰ āĻ“ āϏ⧋āĻ°ā§āϟāĻŋāĻ‚ā§Ÿā§‡ āĻšāĻŋāϏāĻžāĻŦ āĻ•āϰāĻž āĻĢāĻŋāĻ˛ā§āĻĄā§‡āϰ āĻ­āĻžāϞ⧋ āĻŦā§āϝāĻŦāĻšāĻžāϰ\n\nGenerated columns āϏ⧇āχ āϏāĻŦ āĻ•ā§āώ⧇āĻ¤ā§āϰ⧇ āωāĻœā§āĻœā§āĻŦāϞ āϝāĻ–āύ āĻŽāĻžāύāϟāĻŋ āϏāĻŦāϏāĻŽā§Ÿ āĻ…āĻ¨ā§āϝ āĻ•āϞāĻžāĻŽ āĻĨ⧇āϕ⧇ āύāĻŋāĻ°ā§āĻ­āϰ āĻ•āϰ⧇ āĻāĻŦāĻ‚ āφāĻĒāύāĻŋ āϏ⧇āϟāĻŋāϤ⧇ āĻĒā§āϰāĻžāϝāĻŧāχ āĻĢāĻŋāĻ˛ā§āϟāĻžāϰ āĻŦāĻž āϏ⧋āĻ°ā§āϟ āĻ•āϰ⧇āύāĨ¤ āĻāĻ•-āĻŦāĻžāϰ⧇āϰ āϰāĻŋāĻĒā§‹āĻ°ā§āĻŸā§‡āϰ āϜāĻ¨ā§āϝ āĻāϗ⧁āϞ⧋ āϤāϤāϟāĻž āωāĻĒāĻ•āĻžāϰ⧀ āύ⧟āĨ¤\n\n### āĻŦā§āϝāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀āϰāĻž āĻŦāĻžāĻ¸ā§āϤāĻŦ⧇ āϝ⧇ āϏāĻžāĻ°ā§āϚ āĻĢāĻŋāĻ˛ā§āĻĄ āϚāĻžāύ\n\nāĻ…ā§āϝāĻžāĻĄāĻŽāĻŋāύ āϏāĻžāĻ°ā§āϚ āϖ⧁āĻŦ āĻ•āĻŽāχ “āĻļ⧁āĻĻā§āĻ§â€ āϏāĻžāĻ°ā§āϚ āĻšā§ŸāĨ¤ āĻŽāĻžāύ⧁āώ āφāĻļāĻž āĻ•āϰ⧇ āϏāĻžāĻ°ā§āϚ āĻŦāĻ•ā§āϏ āĻāĻžāĻŽā§‡āϞāĻžā§Ÿ, āĻ…āĻĒā§āϰāϤāĻŋāĻˇā§āĻ āĻŋāϤ āϕ⧇āϏāĻŋāĻ‚ āĻāĻŦāĻ‚ āĻ…āϤāĻŋāϰāĻŋāĻ•ā§āϤ āĻ¸ā§āĻĒ⧇āϏ āϏāĻžāĻŽāϞāĻžāĻŦ⧇āĨ¤ āϝāĻĻāĻŋ āφāĻĒāύāĻŋ āĻāĻ•āϟāĻŋ generated “search key” āϏāĻ‚āϰāĻ•ā§āώāĻŖ āĻ•āϰ⧇āύ āϝāĻž āφāϗ⧇ āĻĨ⧇āϕ⧇āχ āύāϰāĻŽāĻžāϞāĻžāχāϜ āĻ•āϰāĻž, āφāĻĒāύāĻžāϰ WHERE āĻ•ā§āϞāϜ āĻĒ⧜āϤ⧇ āϏāĻšāϜ āĻĨāĻžāĻ•āĻŦ⧇ āĻāĻŦāĻ‚ āĻ¸ā§āĻ•ā§āϰāĻŋāύ āĻœā§ā§œā§‡ āφāϚāϰāĻŖ āĻāĻ•āχ āĻĨāĻžāĻ•āĻŦ⧇āĨ¤\n\nāĻ­āĻžāϞ⧋ āĻĒā§āϰāĻžāĻ°ā§āĻĨā§€ āĻšāϤ⧇ āĻĒāĻžāϰ⧇: āĻāĻ•āĻ¤ā§āϰāĻŋāϤ āĻĢ⧁āϞ āύāĻžāĻŽ, āϛ⧋āϟ āĻšāĻžāϤ⧇āϰ āĻŦāĻžāύāĻžāύ⧇ āĻŸā§āϰāĻŋāĻŽ āĻ•āϰāĻž āĻŸā§‡āĻ•ā§āϏāϟ, āĻ…āϤāĻŋāϰāĻŋāĻ•ā§āϤ āĻ¸ā§āĻĒ⧇āϏ āϕ⧇āĻŸā§‡ āĻĻā§‡ā§ŸāĻž, āĻ…āĻĨāĻŦāĻž āĻāĻ•āĻžāϧāĻŋāĻ• āĻĢāĻŋāĻ˛ā§āĻĄ āĻĨ⧇āϕ⧇ āĻĒāĻžāĻ“ā§ŸāĻž āĻ¸ā§āĻŸā§āϝāĻžāϟāĻžāϏ āϞ⧇āĻŦ⧇āϞāĨ¤\n\nāωāĻĻāĻžāĻšāϰāĻŖ: āĻĒā§āϰāϤāĻŋāϟāĻŋ āĻ•ā§ā§Ÿā§‡āϰāĻŋāϤ⧇ lower(trim(first_name || ' ' || last_name)) āĻŦāĻžāϰāĻŦāĻžāϰ āϞ⧇āĻ–āĻžāϰ āĻŦāĻĻāϞ⧇ full_name_key āĻāĻ•āĻŦāĻžāϰ āĻœā§‡āύāĻžāϰ⧇āϟ āĻ•āϰ⧇ āϤāĻžāϰ āωāĻĒāϰ āĻĢāĻŋāĻ˛ā§āϟāĻžāϰ āĻ•āϰ⧁āύāĨ¤\n\n### āĻŽāĻžāύ⧁āώ āĻ•āĻŋāĻ­āĻžāĻŦ⧇ āϏ⧋āĻ°ā§āϟ āĻ•āϰ⧇ āϤāĻžāϰ āϏāĻžāĻĨ⧇ āĻŽāĻŋāϞāĻŋā§Ÿā§‡ āϏ⧋āĻ°ā§āϟ āϕ⧀\n\nāϏ⧋āĻ°ā§āϟāĻŋāĻ‚ āĻšāĻšā§āϛ⧇ āϝ⧇āĻ–āĻžāύ⧇ āĻ—āĻŖāĻŋāϤ āĻ•āϰāĻž āĻĢāĻŋāĻ˛ā§āĻĄ āĻĻā§āϰ⧁āϤāχ āϏ⧁āĻŦāĻŋāϧāĻž āĻĻā§‡ā§Ÿ, āĻ•āĻžāϰāĻŖ āϏ⧋āĻ°ā§āϟāĻŋāĻ‚ PostgreSQL-āϕ⧇ āĻ…āύ⧇āĻ• āϰ⧋-āϤ⧇ āĻāĻ•ā§āϏāĻĒā§āϰ⧇āĻļāύ āĻŽā§‚āĻ˛ā§āϝāĻžā§Ÿāύ āĻ•āϰāĻžāϤ⧇ āĻŦāĻžāĻ§ā§āϝ āĻ•āϰ⧇āĨ¤\n\nāϏāĻžāϧāĻžāϰāĻŖ āϏ⧋āĻ°ā§āϟ āϕ⧀-āϤ⧇ āĻĨāĻžāϕ⧇ āĻāĻ•āϟāĻŋ āύāĻŽā§āĻŦāϰ āĻ°â€ā§āϝāĻžāĻ‚āĻ• (āĻĒā§āĻ˛ā§āϝāĻžāύ āϟāĻŋ⧟āĻžāϰāϕ⧇ 1,2,3 āĻŽā§āϝāĻžāĻĒ āĻ•āϰāĻž), āĻāĻ•āϟāĻŋ “āϏāĻ°ā§āĻŦāĻļ⧇āώ āĻ•āĻžāĻ°ā§āϝāĻ•āϞāĻžāĻĒ” āϟāĻžāχāĻŽāĻ¸ā§āĻŸā§āϝāĻžāĻŽā§āĻĒ (āϝ⧇āĻŽāύ āĻĻ⧁āχ āϟāĻžāχāĻŽāĻ¸ā§āĻŸā§āϝāĻžāĻŽā§āĻĒ⧇āϰ max), āĻŦāĻž āĻŸā§‡āĻ•ā§āϏāϟ āĻšāĻŋāϏ⧇āĻŦ⧇ āϏāĻ āĻŋāĻ•āĻ­āĻžāĻŦ⧇ āϏāĻžāϜāĻžāύ⧋ āĻĒā§āϝāĻžāĻĄ āĻ•āϰāĻž āϕ⧋āĻĄāĨ¤\n\nāϝāĻ–āύ āϏ⧋āĻ°ā§āϟ āϕ⧀āϟāĻŋ āĻāĻ•āϟāĻŋ āϏāĻžāϧāĻžāϰāĻŖ āχāύāĻĄā§‡āĻ•ā§āϏ āĻ•āϰāĻž āĻ•āϞāĻžāĻŽ āĻšā§Ÿ, ORDER BY āĻ…āύ⧇āĻ• āϏāĻ¸ā§āϤāĻž āĻšā§Ÿā§‡ āϝāĻžā§ŸāĨ¤\n\n### āĻĻā§āϰ⧁āϤ āĻĢāĻŋāĻ˛ā§āϟāĻžāϰ⧇āϰ āϜāĻ¨ā§āϝ āĻĄā§‡āϰāĻžāχāϭ⧇āĻĄ āĻĢā§āĻ˛ā§āϝāĻžāĻ—\n\nāĻ…ā§āϝāĻžāĻĄāĻŽāĻŋāύ āĻŦā§āϝāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀āϰāĻž “Overdue” āĻŦāĻž “High value” āϧāϰāĻžāϰ āĻšā§‡āĻ•āĻŦāĻ•ā§āϏ āϖ⧁āĻŦ āĻĒāĻ›āĻ¨ā§āĻĻ āĻ•āϰ⧇āĨ¤ āϝāĻĻāĻŋ āϞāϜāĻŋāĻ•āϟāĻŋ āĻ¸ā§āĻĨāĻŋāϤāĻŋāĻļā§€āϞ āĻāĻŦāĻ‚ āϕ⧇āĻŦāϞ āϰ⧋-āĻĄā§‡āϟāĻžāϰ āωāĻĒāϰ āύāĻŋāĻ°ā§āĻ­āϰ āĻ•āϰ⧇ āϤāĻžāĻšāϞ⧇ āĻāϗ⧁āϞ⧋ generated column āĻšāĻŋāϏ⧇āĻŦ⧇ āĻ­āĻžāϞ āĻ•āĻžāϜ āĻ•āϰ⧇āĨ¤\n\nāωāĻĻāĻžāĻšāϰāĻŖāĻ¸ā§āĻŦāϰ⧂āĻĒ, āϝāĻĻāĻŋ āĻ•āĻžāĻ¸ā§āϟāĻŽāĻžāϰ āϤāĻžāϞāĻŋāĻ•āĻžā§Ÿ “Has unread messages” āĻāĻŦāĻ‚ “Is overdue” āϞāĻžāϗ⧇, āϤāĻžāĻšāϞ⧇ āĻāĻ•āϟāĻŋ generated has_unread boolean (āϝāĻž unread_count > 0 āĻĨ⧇āϕ⧇ āφāϏ⧇) āĻāĻŦāĻ‚ is_overdue (āϝāĻž due_date < now() āĻāĻŦāĻ‚ paid_at is null āĻĨ⧇āϕ⧇ āύāĻŋāĻ°ā§āϧāĻžāϰāĻŋāϤ) UI āĻĢāĻŋāĻ˛ā§āϟāĻžāϰāϕ⧇ āϏāϰāϞ āĻļāĻ°ā§āϤ⧇ āĻŦāĻĻāϞ⧇ āĻĻā§‡ā§ŸāĨ¤\n\n## generated columns, āχāύāĻĄā§‡āĻ•ā§āϏ āĻ“ āĻ…āĻ¨ā§āϝāĻžāĻ¨ā§āϝ āĻŦāĻŋāĻ•āĻ˛ā§āĻĒ⧇āϰ āĻŽāĻ§ā§āϝ⧇ āύāĻŋāĻ°ā§āĻŦāĻžāϚāύ\n\nāĻ…ā§āϝāĻžāĻĄāĻŽāĻŋāύ āĻ¸ā§āĻ•ā§āϰāĻŋāύāϗ⧁āϞ⧋āϕ⧇ āϤāĻŋāύāϟāĻŋ āϜāĻŋāύāĻŋāϏ āϞāĻžāϗ⧇: āĻĻā§āϰ⧁āϤ āĻĢāĻŋāĻ˛ā§āϟāĻžāϰāĻŋāĻ‚, āĻĻā§āϰ⧁āϤ āϏ⧋āĻ°ā§āϟāĻŋāĻ‚, āĻāĻŦāĻ‚ āĻāĻŽāύ SQL āϝāĻž āĻŽāĻžāϏ āĻĒāϰ⧇ āĻĒ⧜āϤ⧇ āϏāĻšāϜ āĻĨāĻžāϕ⧇āĨ¤ āĻŦāĻžāĻ¸ā§āϤāĻŦ āϏāĻŋāĻĻā§āϧāĻžāĻ¨ā§āϤ āĻšāĻšā§āϛ⧇ āĻšāĻŋāϏāĻžāĻŦāϟāĻŋ āϕ⧋āĻĨāĻžā§Ÿ āϰāĻžāĻ–āĻž āωāϚāĻŋāϤ: āĻŸā§‡āĻŦāĻŋāϞ⧇, āχāύāĻĄā§‡āĻ•ā§āϏ⧇, āĻ­āĻŋāωāϤ⧇, āύāĻž āĻ•āĻŋ āĻ…ā§āϝāĻžāĻĒ āϕ⧋āĻĄā§‡āĨ¤\n\nGenerated columns āωāĻĒāϝ⧋āĻ—ā§€ āϝāĻ–āύ āφāĻĒāύāĻŋ āϚāĻžāύ āϝ⧇ āĻŽāĻžāύāϟāĻŋ āĻāĻ•āϟāĻŋ āĻŦāĻžāĻ¸ā§āϤāĻŦ āĻ•āϞāĻžāĻŽā§‡āϰ āĻŽāϤ⧋ āφāϚāϰāĻŖ āĻ•āϰ⧁āĻ•: āϰ⧇āĻĢāĻžāϰ āĻ•āϰāĻž āϏāĻšāϜ, SELECT-āĻ āĻĻ⧇āĻ–āĻž āϝāĻžā§Ÿ, āĻāĻŦāĻ‚ āύāϤ⧁āύ āĻĢāĻŋāĻ˛ā§āϟāĻžāϰ āϝ⧋āĻ— āĻ•āϰāĻžāϰ āϏāĻŽā§Ÿ āϭ⧁āϞ⧇ āύāĻž āϝāĻžāĻ“ā§ŸāĻž āϝāĻžā§ŸāĨ¤ āĻāϗ⧁āϞ⧋ āϏāĻžāϧāĻžāϰāĻŖ āχāύāĻĄā§‡āĻ•ā§āϏ⧇āϰ āϏāĻ™ā§āϗ⧇ āĻ­āĻžāϞ⧋ āĻŦā§‹āĻāĻžāĻĒ⧜āĻž āĻ•āϰ⧇āĨ¤\n\nExpression indexes āϤāĻžā§œāĻžāϤāĻžā§œāĻŋ āϝ⧋āĻ— āĻ•āϰāĻž āϏāĻšāϜ āĻ•āĻžāϰāĻŖ āĻŸā§‡āĻŦāĻŋāϞ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ āĻ•āϰāϤ⧇ āĻšā§Ÿ āύāĻžāĨ¤ āϝāĻĻāĻŋ āφāĻĒāύāĻŋ āϕ⧇āĻŦāϞ āĻ—āϤāĻŋ āϚāĻžāύ āĻāĻŦāĻ‚ āĻ—ā§‹āϞāĻŽā§‡āϞ SQL āĻŽāĻžāύāϤ⧇ āĻĒāĻžāϰ⧇āύ, āĻāĻ•āϟāĻŋ expression index āĻĒā§āϰāĻžāϝāĻŧāχ āϝāĻĨ⧇āĻˇā§āϟāĨ¤ āĻ–āĻžāϰāĻžāĻĒ āĻĻāĻŋāĻ• āĻšāϞ⧋ āĻĒ⧜āϤ⧇ āĻ…āĻ¸ā§āĻŦāĻžāĻšā§āĻ›āĻŋāĻ• āĻāĻŦāĻ‚ āĻĒā§āĻ˛ā§āϝāĻžāύāĻžāϰāϕ⧇ āφāĻĒāύāĻžāϰ āĻāĻ•ā§āϏāĻĒā§āϰ⧇āĻļāύ⧇āϰ āϏāĻ āĻŋāĻ• āĻŽāĻŋāϞ āϖ⧁āρāĻœā§‡ āĻĒ⧇āϤ⧇ āĻšāĻŦ⧇āĨ¤\n\nViews āωāĻĒāĻ•āĻžāϰ⧀ āϝāĻ–āύ āφāĻĒāύāĻŋ āϚāĻžāχāϞ⧇ āĻāĻ•āϟāĻŋ āĻļā§‡ā§ŸāĻžāĻ°ā§āĻĄ āĻĄā§‡āϟāĻž-āφāĻ•ā§ƒāϤāĻŋ, āĻŦāĻŋāĻļ⧇āώāϤ āϝāĻĻāĻŋ āĻ…ā§āϝāĻžāĻĄāĻŽāĻŋāύ āϤāĻžāϞāĻŋāĻ•āĻžā§Ÿ āĻ…āύ⧇āĻ• āĻŸā§‡āĻŦāĻŋāϞ āĻœā§Ÿā§‡āύ āĻ•āϰ⧇āĨ¤ āĻ•āĻŋāĻ¨ā§āϤ⧁ āϜāϟāĻŋāϞ āĻ­āĻŋāω āĻĒā§āϰāϚāϞāĻŋāϤ āĻŦā§āϝ⧟āĻŦāĻšā§āϞ āĻ•āĻžāϜāϕ⧇ āϞ⧁āĻ•āĻŋā§Ÿā§‡ āϰāĻžāĻ–āϤ⧇ āĻĒāĻžāϰ⧇ āĻāĻŦāĻ‚ āĻĄāĻŋāĻŦāĻžāĻ— āĻ•āϰāĻžāϰ āϜāĻ¨ā§āϝ āφāϰ⧇āĻ•āϟāĻŋ āϜāĻžā§ŸāĻ—āĻž āϝ⧋āĻ— āĻ•āϰ⧇āĨ¤\n\nTriggers āĻāĻ•āϟāĻŋ āϏāĻžāϧāĻžāϰāĻŖ āĻ•āϞāĻžāĻŽ āϏāĻŋāĻ™ā§āĻ• āϰāĻžāĻ–āϤ⧇ āĻĒāĻžāϰ⧇, āĻ•āĻŋāĻ¨ā§āϤ⧁ āĻāϗ⧁āϞ⧋ āĻŽā§‹āĻŦāĻžāχāϞ āĻĒāĻžāϟāĻžāϤāύ⧇āϰ āĻŽāϤ⧋āĨ¤ āϤāĻžāϤ⧇ āĻŦāĻžāĻ˛ā§āĻ• āφāĻĒāĻĄā§‡āϟ āϧ⧀āϰ āĻšāϤ⧇ āĻĒāĻžāϰ⧇ āĻāĻŦāĻ‚ āĻŸā§āϰāĻžāĻŦāϞāĻļ⧁āϟ āĻ•āϰāĻžāϰ āϏāĻŽā§Ÿ āϏāĻšāĻœā§‡āχ āύāϜāϰ⧇ āύāĻž āφāϏāϤ⧇ āĻĒāĻžāϰ⧇āĨ¤\n\nāĻ•āĻ–āύ⧋ āĻ•āĻ–āύ⧋ āϏāĻŦāĻšā§‡āϝāĻŧ⧇ āĻ­āĻžāϞ⧋ āĻ…āĻĒāĻļāύ āĻšāϞ⧋ āĻ…ā§āϝāĻžāĻĒ āĻĻā§āĻŦāĻžāϰāĻž āĻ­āϰāĻž āĻāĻ•āϟāĻŋ āϏāĻžāϧāĻžāϰāĻŖ āĻ•āϞāĻžāĻŽāĨ¤ āϝāĻĻāĻŋ āĻŦā§āϝāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀ āĻāϟāĻžāϕ⧇ āϏāĻŽā§āĻĒāĻžāĻĻāύāĻž āĻ•āϰ⧇, āĻ…āĻĨāĻŦāĻž āϏ⧂āĻ¤ā§āϰāϟāĻŋ āĻŦāĻžāϰāĻŦāĻžāϰ āĻŦā§āϝāĻŦāϏāĻžāϝāĻŧāĻŋāĻ• āϏāĻŋāĻĻā§āϧāĻžāĻ¨ā§āϤ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāĻŋāϤ āĻšā§Ÿ (āĻļ⧁āϧ⧁ āϰ⧋ āĻĄā§‡āϟāĻž āύ⧟), āϤāĻžāĻšāϞ⧇ āĻāϟāĻŋ āĻ¸ā§āĻĒāĻˇā§āϟ āϰāĻžāĻ–āĻž āĻ­āĻžāϞ⧋āĨ¤\n\nāĻāĻ•āϟāĻŋ āĻĻā§āϰ⧁āϤ āϏāĻŋāĻĻā§āϧāĻžāĻ¨ā§āϤ⧇āϰ āωāĻĒāĻžā§Ÿ:\n\n- āĻĒ⧜āϤ⧇ āϏāĻšāϜ āĻ•ā§ā§Ÿā§‡āϰāĻŋ āĻ“ āϕ⧇āĻŦāϞ āϰ⧋ āĻĄā§‡āϟāĻžāϰ āωāĻĒāϰ āĻ­āĻŋāĻ¤ā§āϤāĻŋ āĻ•āϰ⧇ āĻ¸ā§āĻĨāĻŋāϤāĻŋāĻļā§€āϞ āϏ⧂āĻ¤ā§āϰ āϚāĻžāύ? āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧁āύ generated columnāĨ¤\n- āĻāĻ•āϟāĻŋ āύāĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āϟ āĻĢāĻŋāĻ˛ā§āϟāĻžāϰ⧇āϰ āϜāĻ¨ā§āϝ āĻ—āϤāĻŋ āϚāĻžāύ āĻāĻŦāĻ‚ āĻ—ā§‹āϞāĻŽā§‡āϞ SQL āĻŽāĻžāύāϤ⧇ āĻĒāĻžāϰ⧇āύ? āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧁āύ expression indexāĨ¤\n- āĻ…āύ⧇āĻ• āϜāĻžā§ŸāĻ—āĻžā§Ÿ āĻāĻ•āχ āϜāϝāĻŧ⧇āύ āĻ•āϰāĻž āϰāĻŋāĻĒā§‹āĻ°ā§āϟ-āφāĻ•ā§ƒāϤāĻŋ āϚāĻžāύ? āĻ­āĻŋāω āĻŦāĻŋāĻŦ⧇āϚāύāĻž āĻ•āϰ⧁āύāĨ¤\n- āĻ•ā§āϰāϏ-āĻŸā§‡āĻŦāĻŋāϞ āϞāϜāĻŋāĻ• āĻŦāĻž āϏāĻžāχāĻĄ-āĻāĻĢ⧇āĻ•ā§āϟ āĻĻāϰāĻ•āĻžāϰ? āφāϗ⧇ āĻ…ā§āϝāĻžāĻĒ āϞāϜāĻŋāĻ•, āĻĒāϰ⧇ āĻŸā§āϰāĻŋāĻ—āĻžāϰ āĻŦāĻŋāĻŦ⧇āϚāύāĻž āĻ•āϰ⧁āύāĨ¤\n\n## āϧāĻžāĻĒ⧇ āϧāĻžāĻĒ⧇: āĻāĻ•āϟāĻŋ generated column āϝ⧋āĻ— āĻ•āϰāĻž āĻāĻŦāĻ‚ āĻ•ā§ā§Ÿā§‡āϰāĻŋāϤ⧇ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāĻž\n\nāĻāĻ•āϟāĻž āϧ⧀āϰ āĻ…ā§āϝāĻžāĻĄāĻŽāĻŋāύ āϞāĻŋāĻ¸ā§āϟ āĻ•ā§ā§Ÿā§‡āϰāĻŋ āĻĻāĻŋā§Ÿā§‡ āĻļ⧁āϰ⧁ āĻ•āϰ⧁āύ āϝāĻž UI-āϤ⧇ āφāĻĒāύāĻŋ āĻ…āύ⧁āĻ­āĻŦ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇āύāĨ¤ āĻ¸ā§āĻ•ā§āϰāĻŋāύ⧇ āϏāĻŦāĻšā§‡ā§Ÿā§‡ āĻŦ⧇āĻļāĻŋ āĻŦā§āϝāĻŦāĻšā§ƒāϤ āĻĢāĻŋāĻ˛ā§āϟāĻžāϰ āĻāĻŦāĻ‚ āϏ⧋āĻ°ā§āϟāϗ⧁āϞ⧋ āύ⧋āϟ āĻ•āϰ⧁āύāĨ¤ āĻĒā§āϰāĻĨāĻŽā§‡ āϏ⧇āχ āĻāĻ•āĻ• āĻ•ā§ā§Ÿā§‡āϰāĻŋāϟāĻž āωāĻ¨ā§āύāϤ āĻ•āϰ⧁āύāĨ¤\n\nāĻāĻ•āϟāĻŋ āĻ—āĻŖāĻŋāϤ āĻ•āϰāĻž āĻĢāĻŋāĻ˛ā§āĻĄ āĻŦ⧇āϛ⧇ āύāĻŋāύ āϝāĻž āĻŦāĻžāϰāĻŦāĻžāϰ āĻ•āĻžāϜ āĻ•āĻŽāĻžā§Ÿ, āĻāĻŦāĻ‚ snake_case-āĻ āĻ¸ā§āĻĒāĻˇā§āϟāĻ­āĻžāĻŦ⧇ āύāĻžāĻŽ āĻĻāĻŋāύ āϝ⧇āύ āĻ…āĻ¨ā§āϝāϰāĻž āĻāĻ•āĻŦāĻžāϰ āĻĻ⧇āϖ⧇ āϧāĻžāϰāĻŖāĻž āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇ āϏ⧇āϟāĻŋ āϕ⧀ āϧāϰ⧇ āϰāĻžāϖ⧇āĨ¤\n\n### 1) GENERATED āĻ•āϞāĻžāĻŽ āϝ⧋āĻ— āĻ•āϰ⧁āύ (STORED)\n\nsql\nALTER TABLE customers\nADD COLUMN full_name_key text\nGENERATED ALWAYS AS (\n lower(concat_ws(' ', last_name, first_name))\n) STORED;\n\n\nāχāύāĻĄā§‡āĻ•ā§āϏ āϝ⧋āĻ— āĻ•āϰāĻžāϰ āφāϗ⧇ āĻŦāĻžāĻ¸ā§āϤāĻŦ āϰ⧋āϤ⧇ āϝāĻžāϚāĻžāχ āĻ•āϰ⧁āύ:\n\nsql\nSELECT id, first_name, last_name, full_name_key\nFROM customers\nORDER BY id DESC\nLIMIT 5;\n\n\nāφāωāϟāĻĒ⧁āϟ āϭ⧁āϞ āĻšāϞ⧇ āĻāĻ–āύāχ āĻāĻ•ā§āϏāĻĒā§āϰ⧇āĻļāύ āĻ āĻŋāĻ• āĻ•āϰ⧁āύāĨ¤ STORED āĻŽāĻžāύ⧇ PostgreSQL āĻĒā§āϰāϤāĻŋāϟāĻŋ āχāύāϏāĻžāĻ°ā§āϟ āĻ“ āφāĻĒāĻĄā§‡āĻŸā§‡ āĻāϟāĻŋ āφāĻĒāĻĄā§‡āϟ āϰāĻžāĻ–āĻŦ⧇āĨ¤\n\n### 2) āφāĻĒāύāĻžāϰ āĻ…ā§āϝāĻžāĻĄāĻŽāĻŋāύ āĻ¸ā§āĻ•ā§āϰāĻŋāύ āĻŽāĻŋāϞ⧇ āĻāĻŽāύ āχāύāĻĄā§‡āĻ•ā§āϏ āϝ⧋āĻ— āĻ•āϰ⧁āύ\n\nāφāĻĒāύāĻžāϰ āĻ…ā§āϝāĻžāĻĄāĻŽāĻŋāύ āĻ¸ā§āĻ•ā§āϰāĻŋāύ āϝāĻĻāĻŋ āĻ¸ā§āĻĨāĻŋāϤāĻŋ āĻ…āύ⧁āϝāĻžā§Ÿā§€ āĻĢāĻŋāĻ˛ā§āϟāĻžāϰ āĻ•āϰ⧇ āĻāĻŦāĻ‚ āύāĻžāĻŽ āĻ…āύ⧁āϝāĻžā§Ÿā§€ āϏ⧋āĻ°ā§āϟ āĻ•āϰ⧇, āϏ⧇āχ āĻĒā§āϝāĻžāϟāĻžāĻ°ā§āύ āĻŽā§‡āϞāĻžāύ⧋ āχāύāĻĄā§‡āĻ•ā§āϏ āϤ⧈āϰāĻŋ āĻ•āϰ⧁āύ:\n\nsql\nCREATE INDEX customers_status_full_name_key_idx\nON customers (status, full_name_key);\n\n\n### 3) āĻ…ā§āϝāĻžāĻĄāĻŽāĻŋāύ āĻ•ā§ā§Ÿā§‡āϰāĻŋ āφāĻĒāĻĄā§‡āϟ āĻ•āϰ⧇ āύāϤ⧁āύ āĻ•āϞāĻžāĻŽ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧁āύ\n\nāφāϗ⧇ āĻšā§ŸāϤ⧋ āφāĻĒāύāĻžāϰ ORDER BY āĻ—ā§‹āϞāĻŽā§‡āϞ⧇ āĻ›āĻŋāϞāĨ¤ āĻĒāϰ⧇ āĻāϟāĻŋ āĻĒāϰāĻŋāĻˇā§āĻ•āĻžāϰ āĻšā§Ÿā§‡ āϝāĻžā§Ÿ:\n\nsql\nSELECT id, status, first_name, last_name\nFROM customers\nWHERE status = 'active'\nORDER BY full_name_key ASC\nLIMIT 50 OFFSET 0;\n\n\nāĻĒā§āϰāϤāĻŋāĻĻāĻŋāύ āĻŽāĻžāύ⧁āώ āϝ⧇āϗ⧁āϞ⧋ āĻĢāĻŋāĻ˛ā§āϟāĻžāϰ āĻāĻŦāĻ‚ āϏ⧋āĻ°ā§āϟ āĻ•āϰ⧇ āϏ⧇āϗ⧁āϞ⧋āϰ āϜāĻ¨ā§āϝ generated columns āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧁āύ, āĻŦāĻŋāϰāϞ āĻ¸ā§āĻ•ā§āϰāĻŋāύ⧇āϰ āϜāĻ¨ā§āϝ āύ⧟āĨ¤\n\n## āĻŦāĻžāĻ¸ā§āϤāĻŦāϏāĻŽā§āĻŽāϤ āχāύāĻĄā§‡āĻ•ā§āϏāĻŋāĻ‚ āĻĒā§āϝāĻžāϟāĻžāĻ°ā§āύ āϝāĻž āĻŦāĻžāĻ¸ā§āϤāĻŦ āĻ…ā§āϝāĻžāĻĄāĻŽāĻŋāύ āĻ¸ā§āĻ•ā§āϰāĻŋāύ āĻŽāĻŋāϞāĻžā§Ÿ\n\nāĻ…ā§āϝāĻžāĻĄāĻŽāĻŋāύ āĻ¸ā§āĻ•ā§āϰāĻŋāύāϗ⧁āϞ⧋ āĻ•ā§Ÿā§‡āĻ•āϟāĻŋ āφāϚāϰāĻŖ āĻŦāĻžāϰāĻŦāĻžāϰ āĻ•āϰ⧇: āĻ•ā§Ÿā§‡āĻ•āϟāĻŋ āĻĢāĻŋāĻ˛ā§āϟāĻžāϰ āĻ•āϞāĻžāĻŽ āĻĻāĻŋā§Ÿā§‡ āĻĢāĻŋāĻ˛ā§āϟāĻžāϰ, āĻāĻ•āϟāĻŋ āĻ•āϞāĻžāĻŽ āĻĻāĻŋā§Ÿā§‡ āϏ⧋āĻ°ā§āϟ, āĻāĻŦāĻ‚ āĻĒā§āϝāĻžāϜāĻŋāύ⧇āĻļāύāĨ¤ āϏāĻ°ā§āĻŦā§‹āĻ¤ā§āϤāĻŽ āϏ⧇āϟāφāĻĒ āϏāĻžāϧāĻžāϰāĻŖāϤ “āϏāĻŦāĻ•āĻŋāϛ⧁ āχāύāĻĄā§‡āĻ•ā§āϏ āĻ•āϰāĻžâ€ āύ⧟āĨ¤ āĻŦāϰāĻ‚ āĻāϟāĻž āĻšāϞ⧋ “āĻ…āϧāĻŋāĻ•āĻžāĻ‚āĻļ āϏāĻžāϧāĻžāϰāĻŖ āĻ•ā§ā§Ÿā§‡āϰāĻŋāϰ āϏāĻ āĻŋāĻ• āφāĻ•ā§ƒāϤāĻŋ āχāύāĻĄā§‡āĻ•ā§āϏ āĻ•āϰāĻžāĨ¤â€\n\nāĻāĻ•āϟāĻŋ āĻŦā§āϝāĻŦāĻšāĻžāϰāĻŋāĻ• āύāĻŋ⧟āĻŽ: āϏāĻŦāĻšā§‡ā§Ÿā§‡ āϏāĻžāϧāĻžāϰāĻŖ āĻĢāĻŋāĻ˛ā§āϟāĻžāϰ āĻ•āϞāĻžāĻŽāϗ⧁āϞ⧋ āĻĒā§āϰāĻĨāĻŽā§‡ āϰāĻžāϖ⧁āύ, āĻāĻŦāĻ‚ āϏāĻŦāĻšā§‡ā§Ÿā§‡ āϏāĻžāϧāĻžāϰāĻŖ āϏ⧋āĻ°ā§āϟ āĻ•āϞāĻžāĻŽāϕ⧇ āĻļ⧇āώ⧇ āϰāĻžāϖ⧁āύāĨ¤ āφāĻĒāύāĻŋ āϝāĻĻāĻŋ āĻŽāĻžāĻ˛ā§āϟāĻŋ-āĻŸā§‡āĻ¨ā§āϝāĻžāĻ¨ā§āϟ āĻšāύ, workspace_id (āĻŦāĻž āĻ…āύ⧁āϰ⧂āĻĒ) āĻĒā§āϰāĻžāϝāĻŧāχ āĻĒā§āϰāĻĨāĻŽ āφāϏ⧇: (workspace_id, status, created_at)āĨ¤\n\nāĻŸā§‡āĻ•ā§āϏāϟ āϏāĻžāĻ°ā§āϚ āύāĻŋāĻœā§‡āχ āĻāĻ•āϟāĻŋ āϏāĻŽāĻ¸ā§āϝāĻžāĨ¤ āĻ…āύ⧇āĻ• āϏāĻžāĻ°ā§āϚ āĻŦāĻ•ā§āϏ āĻļ⧇āώ āĻĒāĻ°ā§āϝāĻ¨ā§āϤ ILIKE '%term%' āĻšā§Ÿā§‡ āϝāĻžā§Ÿ, āϝāĻž āϏāĻžāϧāĻžāϰāĻŖ btree āχāύāĻĄā§‡āĻ•ā§āϏ āĻĻāĻŋā§Ÿā§‡ āĻĻā§āϰ⧁āϤ āĻ•āϰāĻž āĻ•āĻ āĻŋāύāĨ¤ āĻāĻ•āϟāĻŋ āϏāĻšāĻžā§ŸāĻ• āĻĒā§āϝāĻžāϟāĻžāĻ°ā§āύ āĻšāĻšā§āϛ⧇ āĻāĻ•āϟāĻŋ āύāϰāĻŽāĻžāϞāĻžāχāϜāĻĄ āĻšā§‡āĻ˛ā§āĻĒāĻžāϰ āĻ•āϞāĻžāĻŽā§‡ āϏāĻžāĻ°ā§āϚ āĻ•āϰāĻž (āϛ⧋āϟ āĻšāĻžāϤ⧇āϰ, āĻŸā§āϰāĻŋāĻŽ āĻ•āϰāĻž, āĻšā§ŸāϤ⧋ āĻāĻ•āĻ¤ā§āϰāĻŋāϤ)āĨ¤ āϝāĻĻāĻŋ UI āĻĒā§āϰāĻŋāĻĢāĻŋāĻ•ā§āϏ āϏāĻžāĻ°ā§āϚ (term%) āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇, āϤāĻžāĻšāϞ⧇ āϏ⧇āχ āύāϰāĻŽāĻžāϞāĻžāχāϜāĻĄ āĻ•āϞāĻžāĻŽā§‡āϰ āωāĻĒāϰ btree āχāύāĻĄā§‡āĻ•ā§āϏ āϏāĻžāĻšāĻžāĻ¯ā§āϝ āĻ•āϰāĻŦ⧇āĨ¤ āϝāĻĻāĻŋ āĻāϟāĻŋ āĻ•āύāĻŸā§‡āχāύāϏ āϏāĻžāĻ°ā§āϚ (%term%) āĻšāϤ⧇ āĻšā§Ÿ, UI āφāϚāϰāĻŖ āϟāĻžāχāĻŸā§‡āύ āĻ•āϰāĻž āĻŦāĻž āϛ⧋āϟ āϏāĻžāĻŦāϏ⧇āϟ-āĻ āϏāĻžāĻ°ā§āϚ āϏ⧀āĻŽāĻžāĻŦāĻĻā§āϧ āĻ•āϰāĻžāϰ āĻ•āĻĨāĻž āĻ­āĻžāĻŦ⧁āύāĨ¤\n\nāχāύāĻĄā§‡āĻ•ā§āϏ āϝ⧋āĻ— āĻ•āϰāĻžāϰ āφāϗ⧇ āϏāĻŋāϞ⧇āĻ•ā§āϟāĻŋāĻ­āĻŋāϟāĻŋ āĻšā§‡āĻ• āĻ•āϰ⧁āύāĨ¤ āϝāĻĻāĻŋ 95% āϰ⧋ āĻāĻ•āχ āĻ­ā§āϝāĻžāϞ⧁ āĻļā§‡ā§ŸāĻžāϰ āĻ•āϰ⧇ (āϝ⧇āĻŽāύ status = 'active'), āĻ“āχ āĻ•āϞāĻžāĻŽ āĻāĻ•āĻž āχāύāĻĄā§‡āĻ•ā§āϏ āĻ•āϰāĻž āϖ⧁āĻŦ āϏāĻžāĻšāĻžāĻ¯ā§āϝ āĻ•āϰāĻŦ⧇ āύāĻžāĨ¤ āĻāϟāĻŋ āφāϰāĻ“ āϏāĻŋāϞ⧇āĻ•ā§āϟāĻŋāĻ­ āĻ•āϞāĻžāĻŽā§‡āϰ āϏāĻ™ā§āϗ⧇ āĻĒā§‡ā§ŸāĻžāϰ āĻ•āϰ⧁āύ, āĻ…āĻĨāĻŦāĻž āĻŽāĻžāχāύ⧋āϰāĻŋāϟāĻŋ āϕ⧇āϏ⧇āϰ āϜāĻ¨ā§āϝ āĻĒāĻžāϰāĻļāĻŋ⧟āĻžāϞ āχāύāĻĄā§‡āĻ•ā§āϏ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧁āύāĨ¤\n\n## āĻŦāĻžāĻ¸ā§āϤāĻŦ āωāĻĻāĻžāĻšāϰāĻŖ: āĻāĻ•āϟāĻŋ āĻĻā§āϰ⧁āϤ āĻĨāĻžāĻ•āĻž āĻ•āĻžāĻ¸ā§āϟāĻŽāĻžāϰ āĻ…ā§āϝāĻžāĻĄāĻŽāĻŋāύ āϞāĻŋāĻ¸ā§āϟ\n\nāĻāĻ•āϟāĻŋ āϏāĻžāϧāĻžāϰāĻŖ āĻ•āĻžāĻ¸ā§āϟāĻŽāĻžāϰ āĻ…ā§āϝāĻžāĻĄāĻŽāĻŋāύ āĻĒ⧇āϜ āĻ•āĻ˛ā§āĻĒāύāĻž āĻ•āϰ⧁āύ: āĻāĻ•āϟāĻŋ āϏāĻžāĻ°ā§āϚ āĻŦāĻ•ā§āϏ, āĻ•ā§Ÿā§‡āĻ•āϟāĻž āĻĢāĻŋāĻ˛ā§āϟāĻžāϰ (inactive, balance range), āĻāĻŦāĻ‚ āĻāĻ•āϟāĻŋ sortable “Last seen” āĻ•āϞāĻžāĻŽāĨ¤ āϏāĻŽā§Ÿā§‡āϰ āϏāĻžāĻĨ⧇ āĻāϟāĻŋ āĻ—ā§‹āϞāĻŽā§‡āϞ⧇ SQL āĻšā§Ÿā§‡ āϝāĻžā§Ÿ: LOWER(), TRIM(), COALESCE(), āϤāĻžāϰāĻŋāĻ– āĻ—āĻŖāύāĻž, āĻāĻŦāĻ‚ CASE āĻŦā§āϞāĻ•āϗ⧁āϞ⧋ āĻ¸ā§āĻ•ā§āϰāĻŋāύ āĻœā§ā§œā§‡ āĻŦāĻžāϰāĻŦāĻžāϰāĨ¤\n\nāĻāĻ•āϟāĻŋ āĻĒāĻĨ āĻšāϞ⧋ āĻāχ āĻĒ⧁āύāϰāĻžāĻŦ⧃āĻ¤ā§āϤ āĻāĻ•ā§āϏāĻĒā§āϰ⧇āĻļāύāϗ⧁āϞ⧋ generated columns-āĻ āϠ⧇āϞ⧇ āĻĻā§‡ā§ŸāĻž āϝāĻžāϤ⧇ āĻāϟāĻŋ āĻĻā§āϰ⧁āϤ āĻ“ āĻĒ⧜āϤ⧇ āϏāĻšāϜ āĻĨāĻžāϕ⧇āĨ¤\n\n### āĻŸā§‡āĻŦāĻŋāϞ āĻ“ generated āĻ•āϞāĻžāĻŽāϗ⧁āϞ⧋\n\nāϧāϰāĻŋ āĻāĻ•āϟāĻŋ customers āĻŸā§‡āĻŦāĻŋāϞ āφāϛ⧇ āϝāĻžāϰ āĻŽāĻ§ā§āϝ⧇ name, email, last_seen, āĻāĻŦāĻ‚ balance āĻ°ā§Ÿā§‡āϛ⧇āĨ¤ āϤāĻŋāύāϟāĻŋ āĻ—āĻŖāĻŋāϤ āĻ•āϰāĻž āĻĢāĻŋāĻ˛ā§āĻĄ āϝ⧋āĻ— āĻ•āϰ⧁āύ:\n\n- search_key: āϏāĻžāϧāĻžāϰāĻŖ āϏāĻžāĻ°ā§āĻšā§‡āϰ āϜāĻ¨ā§āϝ āύāϰāĻŽāĻžāϞāĻžāχāϜ āĻ•āϰāĻž āĻŸā§‡āĻ•ā§āϏāϟ\n- is_inactive: āĻāĻ•āϟāĻŋ āĻŦ⧁āϞāĻŋ⧟āĻžāύ āϝāĻžāϤ⧇ āĻĄā§‡āϟ āϞāϜāĻŋāĻ• āĻŦāĻžāϰāĻŦāĻžāϰ āϞ⧇āĻ–āĻž āϞāĻžāϗ⧇ āύāĻž\n- balance_bucket: āĻĻā§āϰ⧁āϤ āϏ⧇āĻ—āĻŽā§‡āĻ¨ā§āĻŸā§‡āĻļāύ⧇āϰ āϜāĻ¨ā§āϝ āϞ⧇āĻŦ⧇āϞ\n\nsql\nALTER TABLE customers\n ADD COLUMN search_key text\n GENERATED ALWAYS AS (\n lower(trim(coalesce(name, ''))) || ' ' || lower(trim(coalesce(email, '')))\n ) STORED,\n ADD COLUMN is_inactive boolean\n GENERATED ALWAYS AS (\n last_seen IS NULL OR last_seen < (now() - interval '90 days')\n ) STORED,\n ADD COLUMN balance_bucket text\n GENERATED ALWAYS AS (\n CASE\n WHEN balance < 0 THEN 'negative'\n WHEN balance < 100 THEN '0-99'\n WHEN balance < 500 THEN '100-499'\n ELSE '500+'\n END\n ) STORED;\n\n\nāĻāĻ–āύ āĻ…ā§āϝāĻžāĻĄāĻŽāĻŋāύ āĻ•ā§ā§Ÿā§‡āϰāĻŋ UI-āĻāϰ āĻŽāϤ⧋ āĻĒ⧜āĻŦ⧇āĨ¤\n\n### āĻĒāĻžāĻ āϝ⧋āĻ—ā§āϝ āĻĢāĻŋāĻ˛ā§āϟāĻžāϰ + āϏ⧋āĻ°ā§āϟāĻŋāĻ‚\n\n“Inactive customers, newest activity first” āĻšā§Ÿā§‡ āϝāĻžā§Ÿ:\n\nsql\nSELECT id, name, email, last_seen, balance\nFROM customers\nWHERE is_inactive = true\nORDER BY last_seen DESC NULLS LAST\nLIMIT 50;\n\n\nāĻāĻŦāĻ‚ āĻāĻ•āϟāĻŋ āĻŦ⧇āϏāĻŋāĻ• āϏāĻžāĻ°ā§āϚ āĻšā§Ÿā§‡ āϝāĻžā§Ÿ:\n\nsql\nSELECT id, name, email, last_seen, balance\nFROM customers\nWHERE search_key LIKE '%' || lower(trim($1)) || '%'\nORDER BY last_seen DESC NULLS LAST\nLIMIT 50;\n\n\nāĻŦāĻžāĻ¸ā§āϤāĻŋāĻ• āϜ⧟ āĻšāϞ⧋ āϏāĻ™ā§āĻ—āϤāĻŋāĨ¤ āĻāĻ•āχ āĻĢāĻŋāĻ˛ā§āĻĄ āĻŦāĻšā§ āĻ¸ā§āĻ•ā§āϰāĻŋāύ⧇ āϞāϜāĻŋāĻ• āĻĒ⧁āύāϰāĻžāĻŦ⧃āĻ¤ā§āϤāĻŋ āĻ›āĻžā§œāĻžāχ āϚāϞāĻŦ⧇:\n\n- āĻ•āĻžāĻ¸ā§āϟāĻŽāĻžāϰ āϞāĻŋāĻ¸ā§āϟ āϏāĻžāĻ°ā§āϚ āĻŦāĻ•ā§āϏ search_key āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāĻŦ⧇\n- “Inactive customers” āĻŸā§āϝāĻžāĻŦ is_inactive āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāĻŦ⧇\n- āĻŦā§āϝāĻžāϞāĻžāĻ¨ā§āϏ āĻĢāĻŋāĻ˛ā§āϟāĻžāϰ āϚāĻŋāĻĒāϗ⧁āϞ⧋ balance_bucket āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāĻŦ⧇\n\n## āϏāĻžāϧāĻžāϰāĻŖ āϭ⧁āϞ āĻ“ āĻĢāĻžāρāĻĻ\n\nGenerated columns āĻāĻ•āϟāĻŋ āϏāĻšāϜ āĻœā§Ÿā§‡āϰ āĻŽāϤ⧋ āĻĻ⧇āĻ–āĻžā§Ÿ: āĻŸā§‡āĻŦāĻŋāϞ⧇ āĻ—āĻŖāĻŋāϤ āϰāĻžāϖ⧁āύ āĻāĻŦāĻ‚ āĻ•ā§ā§Ÿā§‡āϰāĻŋ āĻĒāϰāĻŋāĻˇā§āĻ•āĻžāϰ āϰāĻžāϖ⧁āύāĨ¤ āĻāϗ⧁āϞ⧋ āϕ⧇āĻŦāϞ āϤāĻ–āύāĻŋ āϏāĻžāĻšāĻžāĻ¯ā§āϝ āĻ•āϰ⧇ āϝāĻ–āύ āϏ⧇āϗ⧁āϞ⧋ āĻ¸ā§āĻ•ā§āϰāĻŋāύ āĻ•āĻŋāĻ­āĻžāĻŦ⧇ āĻĢāĻŋāĻ˛ā§āϟāĻžāϰ āĻ“ āϏ⧋āĻ°ā§āϟ āĻ•āϰ⧇ āϤāĻžāϰ āϏāĻžāĻĨ⧇ āĻŽāĻŋāϞ⧇, āĻāĻŦāĻ‚ āφāĻĒāύāĻŋ āϏāĻ āĻŋāĻ• āχāύāĻĄā§‡āĻ•ā§āϏ āϝ⧋āĻ— āĻ•āϰ⧇āύāĨ¤\n\nāϏāĻŦāĻšā§‡ā§Ÿā§‡ āϏāĻžāϧāĻžāϰāĻŖ āϭ⧁āϞāϗ⧁āϞ⧋:\n\n- āχāύāĻĄā§‡āĻ•ā§āϏ āĻ›āĻžā§œāĻžāχ āĻāϟāĻŋ āĻĻā§āϰ⧁āϤ āĻšāĻŦ⧇ āĻ­āĻžāĻŦāĻžāĨ¤ āĻ—āĻŖāĻŋāϤ āĻ•āϰāĻž āĻŽāĻžāύāϟāĻŋ āĻŦ⧜ āĻĒāϰāĻŋāϏāϰ⧇ āĻĢāĻŋāĻ˛ā§āϟāĻžāϰ āĻŦāĻž āϏ⧋āĻ°ā§āϟ āĻ•āϰāĻžāϰ āϜāĻ¨ā§āϝ āχāύāĻĄā§‡āĻ•ā§āϏ āϚāĻžāχāĨ¤\n- āĻāĻ• āĻ•āϞāĻžāĻŽā§‡ āϖ⧁āĻŦ āĻŦ⧇āĻļāĻŋ āϞāϜāĻŋāĻ• āĻĒā§āϝāĻžāĻ• āĻ•āϰāĻžāĨ¤ āϝāĻĻāĻŋ āĻāĻ•āϟāĻŋ generated column āĻāĻ•āϟāĻŋ āĻ•ā§āώ⧁āĻĻā§āϰ āĻĒā§āϰ⧋āĻ—ā§āϰāĻžāĻŽā§‡āϰ āĻŽāϤ⧋ āĻšā§Ÿā§‡ āϝāĻžā§Ÿ, āĻŽāĻžāύ⧁āώ āϏ⧇āϟāĻžāϰ āĻ“āĻĒāϰ āύāĻŋāĻ°ā§āĻ­āϰ āĻ•āϰāϤ⧇ āĻŦāĻ¨ā§āϧ āĻ•āϰāĻŦ⧇āĨ¤ āϏāĻ‚āĻ•ā§āώāĻŋāĻĒā§āϤ āϰāĻžāϖ⧁āύ āĻāĻŦāĻ‚ āĻ¸ā§āĻĒāĻˇā§āϟ āύāĻžāĻŽ āĻĻāĻŋāύāĨ¤\n- āĻ…-āχāĻŽāĻŋāωāĻŸā§‡āĻŦāϞ āĻĢāĻžāĻ‚āĻļāύ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāĻžāĨ¤ PostgreSQL āĻ¸ā§āĻŸā§‹āϰāĻĄ generated column-āĻāϰ āĻāĻ•ā§āϏāĻĒā§āϰ⧇āĻļāύ āχāĻŽāĻŋāωāĻŸā§‡āĻŦāϞ āĻšāĻ“ā§ŸāĻž āϚāĻžāχ — now() āĻŦāĻž random() āĻŽāϤ āĻĢāĻžāĻ‚āĻļāύ āϏāĻŽāĻ¸ā§āϝāĻž āĻ•āϰ⧇ āĻŦāĻž āĻ…āύ⧁āĻŽā§‹āĻĻāĻŋāϤ āύāĻžāĻ“ āĻšāϤ⧇ āĻĒāĻžāϰ⧇āĨ¤\n- āϰāĻžāχāϟ āĻ•āĻ¸ā§āϟ āωāĻĒ⧇āĻ•ā§āώāĻž āĻ•āϰāĻžāĨ¤ āχāύāϏāĻžāĻ°ā§āϟ āĻ“ āφāĻĒāĻĄā§‡āϟāϗ⧁āϞ⧋āϕ⧇ āĻ—āĻŖāĻŋāϤ āĻ•āϰāĻž āĻŽāĻžāύāϟāĻŋ āĻŦāϜāĻžā§Ÿ āϰāĻžāĻ–āϤ⧇ āĻšāĻŦ⧇āĨ¤ āϝāĻĻāĻŋ āχāĻŽā§āĻĒā§‹āĻ°ā§āϟ āĻŦāĻž āχāĻ¨ā§āϟāĻŋāĻ—ā§āϰ⧇āĻļāύ āϧ⧀āϰ āĻšā§Ÿā§‡ āϝāĻžā§Ÿ āϤāĻŦ⧇ āĻĻā§āϰ⧁āϤ āϰāĻŋāĻĄā§‡āϰ āĻŽā§‚āĻ˛ā§āϝ āĻ•āĻŽā§‡ āϝāĻžā§ŸāĨ¤\n- āĻ•āĻžāĻ›āĻžāĻ•āĻžāĻ›āĻŋ āĻĄā§āĻĒā§āϞāĻŋāϕ⧇āϟ āϤ⧈āϰāĻŋ āĻ•āϰāĻžāĨ¤ āĻāĻ• āĻŦāĻž āĻĻ⧁āχ āĻŽāĻžāύāĻ•āϰāĻŖ āĻĒā§āϝāĻžāϟāĻžāĻ°ā§āύ āĻ¸ā§āĻŸā§āϝāĻžāĻ¨ā§āĻĄāĻžāĻ°ā§āĻĄāĻžāχāϜ āĻ•āϰ⧁āύ (āϝ⧇āĻŽāύ āĻāĻ• āύāϰāĻŽāĻžāϞāĻžāχāϜāĻĄ āϕ⧀) āĻĒāĻžāρāϚāϟāĻŋ āĻ…āύ⧁āϰ⧂āĻĒ āĻ•āϞāĻžāĻŽ āϜāĻŽāĻž āĻ•āϰāĻžāϰ āĻŦāĻĻāϞ⧇āĨ¤\n\nāϝāĻĻāĻŋ āφāĻĒāύāĻžāϰ āĻ…ā§āϝāĻžāĻĄāĻŽāĻŋāύ āϞāĻŋāĻ¸ā§āϟ ILIKE '%ann%' āĻŽāϤ⧋ āĻ•āύāĻŸā§‡āχāύāϏ āϏāĻžāĻ°ā§āϚ āĻ•āϰ⧇, āĻāĻ•āϟāĻŋ generated column āĻāĻ•āĻž āϤāĻž āϰāĻ•ā§āώāĻž āĻ•āϰāĻŦ⧇ āύāĻžāĨ¤ āĻ•āĻŋāĻ¨ā§āϤ⧁ āĻĻ⧈āύāĻ¨ā§āĻĻāĻŋāύ “āĻĢāĻŋāĻ˛ā§āϟāĻžāϰ āĻ“ āϏ⧋āĻ°ā§āĻŸâ€ āĻ•āĻžāĻœā§‡āϰ āϜāĻ¨ā§āϝ generated columns āĻāĻŦāĻ‚ āϏāĻ āĻŋāĻ• āχāύāĻĄā§‡āĻ•ā§āϏ āϏāĻžāϧāĻžāϰāĻŖāϤ āĻĒāĻžāϰāĻĢāϰāĻŽā§āϝāĻžāĻ¨ā§āϏāϕ⧇ āĻ…āύ⧇āĻ• āĻŦ⧇āĻļāĻŋ āĻĒā§‚āĻ°ā§āĻŦāĻžāύ⧁āĻŽā§‡āϝāĻŧ āĻ•āϰ⧇ āϤ⧋āϞ⧇āĨ¤\n\n## āĻļāĻŋāĻĒ āĻ•āϰāĻžāϰ āφāϗ⧇ āĻĻā§āϰ⧁āϤ āĻšā§‡āĻ•āϞāĻŋāĻ¸ā§āϟ\n\nāĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ āĻĒāĻžāĻ āĻžāύ⧋āϰ āφāϗ⧇ āύāĻŋāĻļā§āϚāĻŋāϤ āĻ•āϰ⧁āύ āĻ—āĻŖāĻŋāϤ āĻŽāĻžāύ, āĻ•ā§ā§Ÿā§‡āϰāĻŋ, āĻāĻŦāĻ‚ āχāύāĻĄā§‡āĻ•ā§āϏ āĻāϕ⧇ āĻ…āĻĒāϰ⧇āϰ āϏāĻ™ā§āϗ⧇ āĻŽāĻŋāϞāϛ⧇:\n\n- āϏ⧂āĻ¤ā§āϰāϟāĻŋ āĻ¸ā§āĻĨāĻŋāϤāĻŋāĻļā§€āϞ āĻāĻŦāĻ‚ āĻāĻ• āĻŦāĻžāĻ•ā§āϝ⧇ āϏāĻšāĻœā§‡ āĻŦ⧁āĻāĻžāύ⧋ āϝāĻžā§ŸāĨ¤\n- āφāĻĒāύāĻžāϰ āĻ•ā§ā§Ÿā§‡āϰāĻŋ āφāϏāϞ⧇ generated column WHERE āĻāĻŦāĻ‚/āĻ…āĻĨāĻŦāĻž ORDER BY-āĻ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāϛ⧇āĨ¤\n- āχāύāĻĄā§‡āĻ•ā§āϏ āĻŦāĻžāĻ¸ā§āϤāĻŦ⧇ āĻŦā§āϝāĻŦāĻšāĻžāϰ⧇āϰ āϏāĻžāĻĨ⧇ āĻŽā§‡āϞ⧇, āĻ•āĻ–āύ⧋ āĻāĻ•āĻŦāĻžāϰ⧇āϰ āĻŸā§‡āĻ¸ā§āϟ āύ⧟āĨ¤\n- āĻĒ⧁āϰāύ⧋ āϞāϜāĻŋāϕ⧇āϰ āϏāĻžāĻĨ⧇ āĻĢāϞāĻžāĻĢāϞ āϤ⧁āϞāύāĻž āĻ•āϰ⧇āϛ⧇āύ āĻ•āĻŋāύāĻž (NULLs, āĻĢāĻžāρāĻ•āĻž āĻ¸ā§āĻŸā§āϰāĻŋāĻ‚, āĻ…āĻĻā§āϭ⧁āϤ āĻ¸ā§āĻĒ⧇āϏāĻŋāĻ‚, āĻŽāĻŋāĻ•ā§āϏāĻĄ āϕ⧇āϏ)āĨ¤\n- āĻŸā§‡āĻŦāĻŋāϞ āĻŦā§āϝāĻ¸ā§āϤ āĻšāϞ⧇ āϰāĻžāχāϟ āĻĒāĻžāϰāĻĢāϰāĻŽā§āϝāĻžāĻ¨ā§āϏ āĻŸā§‡āĻ¸ā§āϟ āĻ•āϰ⧇āϛ⧇āύ āĻ•āĻŋāύāĻž (āχāĻŽā§āĻĒā§‹āĻ°ā§āϟ, āĻŦā§āϝāĻžāĻ•āĻ—ā§āϰāĻžāωāĻ¨ā§āĻĄ āφāĻĒāĻĄā§‡āϟ, āχāĻ¨ā§āϟāĻŋāĻ—ā§āϰ⧇āĻļāύ)āĨ¤\n\n## āĻĒāϰāĻŦāĻ°ā§āϤ⧀ āϧāĻžāĻĒ: āφāĻĒāύāĻžāϰ āĻ…ā§āϝāĻžāĻĄāĻŽāĻŋāύ āĻ¸ā§āĻ•ā§āϰāĻŋāύ⧇ āĻāϟāĻŋ āĻĒā§āϰāϝāĻŧā§‹āĻ— āĻ•āϰāĻž\n\nāĻāĻ•āϟāĻž āϛ⧋āϟ, āωāĻšā§āϚ-āĻĒā§āϰāĻ­āĻžāĻŦ āĻļ⧁āϰ⧁ āĻŦ⧇āϛ⧇ āύāĻŋāύ: ⧍-ā§Š āϟāĻŋ āĻ…ā§āϝāĻžāĻĄāĻŽāĻŋāύ āĻ¸ā§āĻ•ā§āϰāĻŋāύ āϝ⧇āϗ⧁āϞ⧋ āĻŽāĻžāύ⧁āώ āϏāĻžāϰāĻž āĻĻāĻŋāύ āϖ⧁āϞ⧇ āϰāĻžāϖ⧇ (āĻ…āĻ°ā§āĻĄāĻžāϰ, āĻ•āĻžāĻ¸ā§āϟāĻŽāĻžāϰ, āϟāĻŋāϕ⧇āϟ)āĨ¤ āϕ⧀ āϧ⧀āϰ āϞāĻžāĻ—āϛ⧇ āύ⧋āϟ āĻ•āϰ⧁āύ (āĻāĻ•āϟāĻŋ āϤāĻžāϰāĻŋāĻ– āϰ⧇āĻžā§āϜ āĻĢāĻŋāĻ˛ā§āϟāĻžāϰ, “āĻļ⧇āώ āĻ•āĻžāĻ°ā§āϝāĻ•āϞāĻžāĻĒ” āĻ…āύ⧁āϝāĻžāϝāĻŧā§€ āϏ⧋āĻ°ā§āϟ āĻ•āϰāĻž, āĻŽāĻŋāϞāĻŋāϤ āύāĻžāĻŽ āĻĻāĻŋā§Ÿā§‡ āϏāĻžāĻ°ā§āϚ, āĻ¸ā§āĻŸā§āϝāĻžāϟāĻžāϏ āϞ⧇āĻŦ⧇āϞ āĻĻā§āĻŦāĻžāϰāĻž āĻĢāĻŋāĻ˛ā§āϟāĻžāϰ)āĨ¤ āϤāĻžāϰāĻĒāϰ āĻāĻŽāύ āĻāĻ•āϟāĻŋ āϏāĻ‚āĻ•ā§āώāĻŋāĻĒā§āϤ āϏ⧇āĻŸā§‡āϰ āĻ—āĻŖāĻŋāϤ āĻ•āϰāĻž āĻĢāĻŋāĻ˛ā§āĻĄ āĻ¸ā§āĻŸā§āϝāĻžāĻ¨ā§āĻĄāĻžāĻ°ā§āĻĄāĻžāχāϜ āĻ•āϰ⧁āύ āϝ⧇āϗ⧁āϞ⧋ āφāĻĒāύāĻŋ āĻ¸ā§āĻ•ā§āϰāĻŋāύ āĻœā§ā§œā§‡ āĻĒ⧁āύāϰāĻžā§Ÿ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāϤ⧇ āĻĒāĻžāϰāĻŦ⧇āύāĨ¤\n\nāĻāĻ•āϟāĻŋ āϰ⧋āϞāφāωāϟ āĻĒā§āĻ˛ā§āϝāĻžāύ āϝāĻž āĻĒāϰāĻŋāĻŽāĻžāĻĒ āĻ•āϰāĻž āϏāĻšāϜ āĻāĻŦāĻ‚ āωāĻ˛ā§āĻŸā§‡ āĻĢ⧇āϞāĻž āϏāĻšāϜ:\n\n- āĻ¸ā§āĻĒāĻˇā§āϟ āύāĻžāĻŽ āĻĻāĻŋā§Ÿā§‡ generated column(s) āϝ⧁āĻ•ā§āϤ āĻ•āϰ⧁āύāĨ¤\n- āϝāĻĻāĻŋ āφāĻĒāύāĻŋ āĻŦāĻŋāĻĻā§āϝāĻŽāĻžāύ āϞāϜāĻŋāĻ• āϰāĻŋāĻĒā§āϞ⧇āϏ āĻ•āϰ⧇ āĻĨāĻžāϕ⧇āύ, āĻ•āĻŋāϛ⧁āĻ•ā§āώāĻŖ āĻĒ⧁āϰāύ⧋ āĻ“ āύāϤ⧁āύ āĻĒāĻžāĻļ⧇ āĻĒāĻžāĻļ⧇ āϚāĻžāϞāĻžāύāĨ¤\n- āĻŽā§‡āχāύ āĻĢāĻŋāĻ˛ā§āϟāĻžāϰ āĻŦāĻž āϏ⧋āĻ°ā§āϟ āĻŽā§‡āϞāĻžāύ⧋ āχāύāĻĄā§‡āĻ•ā§āϏ āϝ⧋āĻ— āĻ•āϰ⧁āύāĨ¤\n- āĻ¸ā§āĻ•ā§āϰāĻŋāύ⧇āϰ āĻ•ā§ā§Ÿā§‡āϰāĻŋ āύāϤ⧁āύ āĻ•āϞāĻžāĻŽ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāϤ⧇ āĻ¸ā§āϝ⧁āχāϚ āĻ•āϰ⧁āύāĨ¤\n- āφāϗ⧇āϰ āĻ“ āĻĒāϰ⧇ āĻŽāĻžāĻĒ⧁āύ (āĻ•ā§ā§Ÿā§‡āϰāĻŋ āϟāĻžāχāĻŽ āĻāĻŦāĻ‚ āĻ¸ā§āĻ•ā§āϝāĻžāύ āĻ•āϰāĻž āϰ⧋), āϤāĻžāϰāĻĒāϰ āĻĒ⧁āϰāύ⧋ āĻ“ā§ŸāĻžāĻ°ā§āĻ•āĻ…ā§āϝāĻžāϰāĻžāωāĻ¨ā§āĻĄ āϏāϰāĻžāύāĨ¤\n\nāφāĻĒāύāĻŋ āϝāĻĻāĻŋ AppMaster-āĻ (AppMaster) āφāĻ­ā§āϝāĻ¨ā§āϤāϰ⧀āĻŖ āĻ…ā§āϝāĻžāĻĄāĻŽāĻŋāύ āϟ⧁āϞāϏ āĻŦāĻžāύāĻžāύ, āĻāχ āĻ—āĻŖāĻŋāϤ āĻ•āϰāĻž āĻĢāĻŋāĻ˛ā§āĻĄāϗ⧁āϞ⧋ āĻāĻ•āϟāĻŋ āĻļā§‡ā§ŸāĻžāĻ°ā§āĻĄ āĻĄā§‡āϟāĻž āĻŽāĻĄā§‡āϞ⧇ āϏ⧁āĻ¨ā§āĻĻāϰāĻ­āĻžāĻŦ⧇ āĻĢāĻŋāϟ āĻ•āϰ⧇: āĻĄāĻžāϟāĻžāĻŦ⧇āϏ āύāĻŋ⧟āĻŽ āĻŦāĻšāύ āĻ•āϰ⧇, āĻāĻŦāĻ‚ āφāĻĒāύāĻžāϰ UI āĻĢāĻŋāĻ˛ā§āϟāĻžāϰāϗ⧁āϞ⧋ āϏāϰāĻžāϏāϰāĻŋ āĻāĻ•āϟāĻŋ āϏāϰāϞ āĻĢāĻŋāĻ˛ā§āĻĄ āύāĻžāĻŽā§‡āϰ āĻĻāĻŋāϕ⧇ āĻĒāϝāĻŧ⧇āĻ¨ā§āϟ āĻ•āϰ⧇, āĻ•ā§ā§Ÿā§‡āϰāĻŋ āĻāĻ•ā§āϏāĻĒā§āϰ⧇āĻļāύ āĻŦāĻžāϰāĻŦāĻžāϰ āύāĻž āϞ⧇āĻ–āĻžāχāĨ¤

āĻĒā§āϰāĻļā§āύ⧋āĻ¤ā§āϤāϰ

When should I use a PostgreSQL generated column for an admin screen?

Generated columns help when you keep repeating the same expression in WHERE or ORDER BY, like normalizing names, mapping statuses, or building a sorting key. They’re especially useful for admin lists that are opened all day and need predictable filtering and sorting.

What’s the difference between a stored generated column and an expression index?

A stored generated column is computed on insert or update and saved like a normal column, so reads can be fast and indexable. An expression index stores the result in the index without adding a new table column, but your queries still need to use the exact expression for the planner to match it.

Will a generated column automatically make my query faster?

No, not by itself. A generated column mainly makes the query simpler and makes indexing a computed value straightforward, but you still need an index that matches your common filters and sorts if you want real speedups at scale.

What are the best generated columns to add for admin search and sorting?

Usually it’s a field you filter or sort on constantly: a normalized search key, a “full name” sort key, a derived boolean like is_overdue, or a ranking number that matches how people expect results to sort. Pick one value that removes repeated work from many queries, not a one-off calculation.

How do I choose the right index for an admin list that filters and sorts?

Start with the most common filter columns, then put the main sort key last, like (workspace_id, status, full_name_key) if that matches the screen. This lets PostgreSQL filter quickly and then return rows already ordered without extra work.

Can generated columns fix slow contains search like ILIKE '%term%'?

Not very. A generated column can normalize text so behavior is consistent, but ILIKE '%term%' still tends to be slow with basic btree indexes on large tables. If performance matters, prefer prefix-style search where you can, reduce the searched dataset with other filters, or adjust the UI behavior for big tables.

Can I create a generated column that depends on now() for “inactive” flags?

Stored generated columns have to be based on immutable expressions, so functions like now() typically aren’t allowed and would also be conceptually wrong because the value would go stale. For time-based flags like “inactive for 90 days,” consider a normal column maintained by a job, or compute it at query time if it’s not heavily used.

What happens if I need to change the formula of a generated column later?

Yes, but plan it like a real migration. Changing the expression means updating the schema and recomputing values for existing rows, which can take time and add write load, so do it in a controlled deployment window if the table is large.

Do generated columns add overhead to inserts and updates?

Yes. The database has to compute and store the value on every insert and update, so heavy write workloads (imports, sync jobs) can slow down if you add too many generated fields or complex expressions. Keep expressions short, add only what you use, and measure write performance on busy tables.

What’s the safest way to roll out generated columns to speed up an existing admin screen?

Add a generated column, validate a few real rows, then add the index that matches the screen’s main filter and sort. Update the admin query to use the new column directly, and compare query time and rows scanned before and after to confirm the change helped.

āĻļ⧁āϰ⧁ āĻ•āϰāĻž āϏāĻšāϜ
āĻ•āĻŋāϛ⧁ āφāĻļā§āϚāĻ°ā§āϝāϜāύāĻ•āϤ⧈āϰāĻŋ āĻ•āϰ⧁āύ

āĻŦāĻŋāύāĻžāĻŽā§‚āĻ˛ā§āϝ⧇āϰ āĻĒāϰāĻŋāĻ•āĻ˛ā§āĻĒāύāĻž āϏāĻš āĻ…ā§āϝāĻžāĻĒāĻŽāĻžāĻ¸ā§āϟāĻžāϰ⧇āϰ āϏāĻžāĻĨ⧇ āĻĒāϰ⧀āĻ•ā§āώāĻž āĻ•āϰ⧁āύāĨ¤
āφāĻĒāύāĻŋ āϝāĻ–āύ āĻĒā§āϰāĻ¸ā§āϤ⧁āϤ āĻšāĻŦ⧇āύ āϤāĻ–āύ āφāĻĒāύāĻŋ āϏāĻ āĻŋāĻ• āϏāĻĻāĻ¸ā§āϝāϤāĻž āĻŦ⧇āϛ⧇ āύāĻŋāϤ⧇ āĻĒāĻžāϰ⧇āύ⧎

āĻāĻŦāĻžāϰ āĻļ⧁āϰ⧁ āĻ•āϰāĻž āϝāĻžāĻ•
āĻ…ā§āϝāĻžāĻĄāĻŽāĻŋāύ āĻĢāĻŋāĻ˛ā§āϟāĻžāϰ⧇āϰ āϜāĻ¨ā§āϝ PostgreSQL generated columns āĻĻāĻŋā§Ÿā§‡ āĻĻā§āϰ⧁āϤāϤāĻž | AppMaster