Rails рдореЗрдВ SQL рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛

рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ, рд▓реЗрдЦ рдЗрд╕ рдмрд╛рд░реЗ рдореЗрдВ рдирд╣реАрдВ рд╣реИ рдХрд┐ рдореБрдЭреЗ рд░реЗрд▓ рдХреИрд╕реЗ рдкрд╕рдВрдж рд╣реИ, рдФрд░ рджреВрд╕рд░реА рдмрд╛рдд, рд▓реЗрдЦ рдЗрд╕ рдмрд╛рд░реЗ рдореЗрдВ рдирд╣реАрдВ рд╣реИ рдХрд┐ рдореИрдВ рдЙрдирд╕реЗ рдХреИрд╕реЗ рдирдлрд░рдд рдХрд░рддрд╛ рд╣реВрдВред рдЙрдирдХрд╛ рдЗрд▓рд╛рдЬ рдкреВрд░реА рддрд░рд╣ рд╕реЗ рдЕрд▓рдЧ рддрд░реАрдХреЗ рд╕реЗ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ рдФрд░ рд╡реЗ рддрднреА рдмреЗрд╣рддрд░ рдмрдиреЗрдВрдЧреЗ рдЬрдм рдЖрдк рдЙрдиреНрд╣реЗрдВ рдмрджрд▓реЗрдВрдЧреЗред рдФрд░ рд╡реЗ рдмрджрддрд░ рд╣реЛ рд╕рдХрддреЗ рд╣реИрдВ рдХреЗрд╡рд▓ рдЕрдЧрд░ рд╡реЗ рдмрджрд▓рдирд╛ рд╢реБрд░реВ рдХрд░рддреЗ рд╣реИрдВред рдЦреИрд░, рд╕рд╛рдорд╛рдиреНрдп рддреМрд░ рдкрд░, рдореИрдВрдиреЗ рдЖрдкрдХреЛ рдЪреЗрддрд╛рд╡рдиреА рджреА рдереА, рд▓реЗрдХрд┐рди рдЖрдкрдиреЗ рдореБрдЭреЗ рд╕рдордЭрд╛ред


ActiveRecord рдХреА рдореБрдЦреНрдп рдЕрд╡рдзрд╛рд░рдгрд╛рдУрдВ рдореЗрдВ рд╕реЗ рдПрдХ рдпрд╣ рд╣реИ рдХрд┐ рдбреЗрдЯрд╛рдмреЗрд╕ рдХрд╛рдлреА рдЙрдкрдпреЛрдЧрд┐рддрд╛рд╡рд╛рджреА рд╣реИ рдФрд░ рдЗрд╕реЗ рдмрджрд▓рд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред рдареАрдХ рд╣реИ, рдЖрдк рд╡рд╣рд╛рдВ рдмреИрдареЗ рд╣реИрдВ, MySQL рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдЕрдкрдиреЗ рдореЙрдбрд▓ рд▓рд┐рдЦ рд░рд╣реЗ рд╣реИрдВ, рдФрд░ рдЕрдЪрд╛рдирдХ рдЖрдкрдиреЗ рдХрд╣реАрдВ рдкрдврд╝рд╛ рд╣реИ рдХрд┐ рдЖрдк рдЗрд╕рдХреЗ рдмрдЬрд╛рдп MySQL рд▓реЗ рд╕рдХрддреЗ рд╣реИрдВ рдФрд░ рдЗрд╕реЗ MongbDB рдХреЗ рд╕рд╛рде рдмрджрд▓ рд╕рдХрддреЗ рд╣реИрдВред рдЦреИрд░, рдЗрддрдирд╛ рдХрдЯреНрдЯрд░рдкрдВрдереА рдирд╣реАрдВ рд╣реИ, рд▓реЗрдХрд┐рди, рдХрд╣рддреЗ рд╣реИрдВ, PostgreSQL рдкрд░, рдЖрдкрдХреЗ рдкрд╛рд╕ MySQL рдХреЛ рдмрджрд▓рдиреЗ рдХреЗ рдХрд╛рд░рдг рд╣реЛ рд╕рдХрддреЗ рд╣реИрдВред рдпрд╛ рдЗрд╕рдХреЗ рд╡рд┐рдкрд░реАрдд, рдореЗрд░реЗ рдкрд╛рд╕ MySQL рдХреЗ рдЦрд┐рд▓рд╛рдл рдХреБрдЫ рднреА рдирд╣реАрдВ рд╣реИред рдпрд╣рд╛рдВ ActiveRecord рдЖрдкрдХреЗ рд▓рд┐рдП рдЗрд╕реЗ рдЖрд╕рд╛рди рдмрдирд╛рдиреЗ рдХрд╛ рджрд╛рд╡рд╛ рдХрд░рддрд╛ рд╣реИ, рдорд╛рдирд╛ рдЬрд╛рддрд╛ рд╣реИ рдХрд┐ рдЗрд╕рд╕реЗ рдкрд╣рд▓реЗ, рдлрд╝рд┐рд▓реНрдЯрд░ рдФрд░ рдПрд╕реЛрд╕рд┐рдПрд╢рди рдХреЗ рдмрд╛рдж, рдбреЗрдЯрд╛рдмреЗрд╕ рдкреНрд░рд╢реНрдиреЛрдВ рдХреЛ рдЙрддреНрдкрдиреНрди рдХрд░рдиреЗ рдФрд░ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рд▓реЙрдЬрд┐рдХ рдХреА рджреЗрдЦрднрд╛рд▓ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЪрд┐рдВрддрд╛ рди рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдкрд░реНрдпрд╛рдкреНрдд рд╣реИрдВред рдЗрд╕рдХреЗ рдмрдЬрд╛рдП WHERE is_archived = TRUE рдмрдЬрд╛рдп рдЖрдк рдпрд╣ WHERE is_archived = TRUE рдЦреБрд╢ рд╣реИрдВ рдХрд┐ where(is_archived: true) рдФрд░ ActiveRecord рдЖрдкрдХреЗ рд▓рд┐рдП рд╕рдм рдХреБрдЫ рдХрд░реЗрдЧрд╛ред рд╕рднреА рдЙрджрд╛рд╣рд░рдг PostgreSQL рдХреЗ рд▓рд┐рдП рдЕрдиреБрд╡рд╛рджрд┐рдд рдХрд┐рдП рдЬрд╛рдПрдВрдЧреЗ, MySQL рдХреЗ рд▓рд┐рдП рдирд╣реАрдВ, рдЗрд╕рд▓рд┐рдП MySQL рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛рдУрдВ рдХреЛ рдЕрдкрдиреА рдмрд╛рдЗрдХ рдХрд╛ рдЖрд╡рд┐рд╖реНрдХрд╛рд░ рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред



рд▓реЗрдХрд┐рди рдХреЛрдИ рдлрд░реНрдХ рдирд╣реАрдВ рдкрдбрд╝рддрд╛ рдХрд┐ рдХреИрд╕реЗ! рд╡реНрдпрд╡рд╣рд╛рд░ рдореЗрдВ, рдпрд╣ рдкрддрд╛ рдЪрд▓рд╛ рд╣реИ рдХрд┐ рдЕрдореВрд░реНрдд рдХреА рдпрд╣ рдкрд░рдд рдкреВрд░реА рддрд░рд╣ рд╕реЗ рдЫрд┐рджреНрд░реЛрдВ рд╕реЗ рднрд░реА рд╣реБрдИ рд╣реИ, рдЬреИрд╕реЗ рдЧреЛрд▓реНрдбрди рдордЫрд▓реА рдХреА рдХрд╣рд╛рдиреА рд╕реЗ рдПрдХ рдЧрд░реНрддред рдФрд░ рдпрд╣ рдХрд┐ рдХрдИ рдмреБрдирд┐рдпрд╛рджреА рд╕реБрд╡рд┐рдзрд╛рдУрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдирд╣реАрдВ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ, рдЬреИрд╕реЗ рддрд╛рд░реАрдЦреЛрдВ рдХреА рддреБрд▓рдирд╛ рдХрд░рдирд╛ рдпрд╛ рд╕рд░рдгрд┐рдпреЛрдВ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдирд╛ред рдФрд░ рдЖрдкрдХреЛ рдордЬрдмреВрд░рди рд╕реНрдХреЛрдк рдорд┐рд▓рддреЗ рд╣реИрдВ where("#{quoted_table_name}.finished_at >= ?", Date.current) рдпрд╛ where("#{quoted_table_name}.other_ids <@ ARRAY[?]", ids) ред рдЬрд┐рд╕рдХреЗ рд▓рд┐рдП ActiveRecord рдкреВрд░реА рддрд░рд╣ рд╕реЗ рд╕рдЪреЗрдд рдФрд░ рддрд╛рд░реНрдХрд┐рдХ рдЙрддреНрддрд░ рджреЗрддрд╛ рд╣реИ: рдЗрд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рди рдХрд░реЗрдВред рд╕рд░рдгрд┐рдпреЛрдВ рдХреЗ рдмрдЬрд╛рдп, habtm рдХрдиреЗрдХреНрд╢рди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВ, рдФрд░ рдпрджрд┐ рдЖрдкрдХреЛ рддрд╛рд░реАрдЦреЛрдВ рдХреА рддреБрд▓рдирд╛ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ, рддреЛ рдЗрд╕рдХреЗ рд╕рд╛рде рд░рд╣реЗрдВред рд╣рд╛рдВ, рдФрд░ рднрдЧрд╡рд╛рди рдиреЗ рдЖрдкрдХреЛ рдЗрд╕ рддрд░рд╣ рдХреЗ рджрд╛рдпрд░реЗ рдореЗрдВ quoted_table_name рдпрд╛рдж рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдордирд╛ рдХрд┐рдпрд╛ рд╣реИ - рдкрд╣рд▓реЗ includes рдпрд╛ joins рд╕рдм рдХреБрдЫ рдЕрдкрдиреА рдЬрдЧрд╣ рдкрд░ joins ред рдпрд╣ рд╣рд░ рдЬрдЧрд╣ рдФрд░ рд╣рдореЗрд╢рд╛ рд▓рд┐рдЦрдирд╛ рдЖрд╕рд╛рди рд╣реЛрддрд╛ рд╣реИ, рддрд╛рдХрд┐ рдЕрдкрдирд╛ рд╣рд╛рде рдиреАрдЪреЗ рди рдЧрд┐рд░рд╛рдПрдВред


рдареАрдХ рд╣реИ, рдирд┐рд╢реНрдЪрд┐рдд рд░реВрдк рд╕реЗ, рдЬреИрд╕реЗ рд╣реА рдЖрдк рдпрд╣рд╛рдВ ActiveRecord рдХреЗ рд╕рд╛рде рд╣рд╕реНрддрдХреНрд╖реЗрдк рдХрд░рдиреЗ рдХрд╛ рдирд┐рд░реНрдгрдп рд▓реЗрддреЗ рд╣реИрдВ, рдХреЛрдИ рднреА рдореЛрдбрд╝ рдирд╣реАрдВ рд╣реЛрдЧрд╛ред рдРрд╕рд╛ рдирд╣реАрдВ рд╣реИ рдХрд┐ рдХреЛрдИ рдореМрдХрд╛ рдирд╣реАрдВ рд╣реЛрдЧрд╛, рдпрд╣рд╛рдВ рддрдХ тАЛтАЛрдХрд┐ рдХрд┐рд╕реА рдЕрдиреНрдп рдбреЗрдЯрд╛рдмреЗрд╕ рдореЗрдВ рджрд░реНрдж рд░рд╣рд┐рдд рд╕рдВрдХреНрд░рдордг рдХреЗ рд▓рд┐рдП рдПрдХ рднреВрддрд┐рдпрд╛ рдЖрд╢рд╛ рднреАред рдЗрд╕ рд╕реЛрд░реНрд╕ рдХреЛрдб рдХреЛ рдкреНрд░рд┐рдВрдЯ рдХрд░рдирд╛ рдФрд░ рдЬрд▓рд╛рдирд╛ рдЬреНрдпрд╛рджрд╛ рдЖрд╕рд╛рди рд╣реЛрдЧрд╛ред рдФрд░ рд╣рд╛рдВ, рдЖрдкрдХреЗ рдЖрд╡реЗрджрди рдореЗрдВ рдЕрддрд┐рд░рд┐рдХреНрдд рдбреЗрдЯрд╛рдмреЗрд╕ рд╕реБрд╡рд┐рдзрд╛рдУрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдирд╣реАрдВ рдХрд░рдиреЗ рдХрд╛ рдХреЛрдИ рдЕрдиреНрдп рдХрд╛рд░рдг рдирд╣реАрдВ рд╣реИред рд╕реНрд╡рд╛рд╕реНрдереНрдп рдкрд░ рдкреНрд░рдпреЛрдЧ рдХрд░реЗрдВ рдФрд░ рджреВрд╕рд░реЛрдВ рдХреЛ рдордЬрдмреВрд░ рдХрд░реЗрдВ!


рдФрд░ рдЬрдм рдпрд╣ рдкрддрд╛ рдЪрд▓рддрд╛ рд╣реИ рдХрд┐ рдореЙрдбрд▓ рдлрд╝реЛрд▓реНрдбрд░ рдореЗрдВ рдЖрдкрдХреЗ рд╕реНрдХреЛрдк рдЗрди рдЕрддрд┐рд░рд┐рдХреНрдд рд╕реБрд╡рд┐рдзрд╛рдУрдВ рдХреЗ рдЖрдзреЗ рд╕реЗ рдЕрдзрд┐рдХ рд╣реИрдВ, рддреЛ рдпрд╣ рдХрд╛рдлреА рд╕реНрдкрд╖реНрдЯ рд╣реИ рдХрд┐ рдХреЛрдб рдХреЗ рдПрдХ рдЯреБрдХрдбрд╝реЗ рдХреЗ рд╕рд╛рде рд▓реЗрдмрд▓ рдХреЗ рд╕рд╛рде рдХреЛрдб рдХреЗ рдПрдХ рдЯреБрдХрдбрд╝реЗ рдХреЛ рдПрдХреАрдХреГрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП ActiveRecord рд╕рд┐рд░реНрдл рдПрдХ рд╕реБрд╡рд┐рдзрд╛рдЬрдирдХ рд╢реЗрд▓ рд╣реИред рдФрд░ scopes, where(is_archived: true).joins(:sprint).merge(Sprint.archived) , рдареАрдХ рдХрд╛рдо рдХрд░реЗрдЧрд╛ рдФрд░ рдЙрдиреНрд╣реЗрдВ рд╕рдВрдпреЛрдЬрд┐рдд рдХрд░рдирд╛ рддрд▓реЗ рд╣реБрдП рдЕрдВрдбреЗ рдмрдирд╛рдиреЗ рд╕реЗ рдЬреНрдпрд╛рджрд╛ рдореБрд╢реНрдХрд┐рд▓ рдирд╣реАрдВ рд╣реЛрдЧрд╛, рд╣реИ рдирд╛?



рдЕрдЧрд▓реЗ рдЪрд░рдг рдХрд╛ рдирд╛рдордХрд░рдг рд╣реЛрдЧрд╛ред рдирд╣реАрдВ, рд╡рд┐рдХреЗрдВрджреНрд░реАрдХрд░рдг, рдЬреИрд╕рд╛ рдХрд┐ рдпрд╣ рдерд╛, рд╣рдореЗрд╢рд╛ рдЧрд╛рдпрдм рдирд╣реАрдВ рд╣реБрдЖ, рд▓реЗрдХрд┐рди рдЗрд╕рдХреА рджреЗрдЦрднрд╛рд▓ рд░реЗрд▓ рдФрд░ ActiveRecord рдХреЗ рд╢рдХреНрддрд┐рд╢рд╛рд▓реА рдХрдВрдзреЛрдВ рдХреЗ рд╕рд╛рде рдЖрд░рд╛рдо рдХрд░рддреА рд╣реИ, рдФрд░ рдЖрдк рдЬрд╛рдирддреЗ рд╣реИрдВ рдХрд┐ рдЗрди рджреЛ рд▓реЛрдЧреЛрдВ рдХреЛ рд╕рдВрд╕рд╛рдзрди рдЖрд╡рд╢реНрдпрдХрддрд╛рдУрдВ рдореЗрдВ рддреЗрдЬ рдФрд░ рддрдкрд╕реНрд╡ рдореЗрдВ рднрд┐рдиреНрдирддрд╛ рдирд╣реАрдВ рдереАред рдорд╛рди рд▓реАрдЬрд┐рдП рдХрд┐ counter_cache: true COUNT(*) AS sprints_count рдУрд░ рдкрд╣рд▓рд╛ рдХрджрдо рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ COUNT(*) AS sprints_count рдЖрдкрдХреЛ ActiveRecord рдмрдирд╛рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рдирд╣реАрдВ рджреЗрдЧрд╛ (рдЖрдк рд╕рд╣реА select() ?), select() рдХреЛ рдмрджрд▓рдирд╛ рдирд╣реАрдВ рдЪрд╛рд╣реЗрдВрдЧреЗ)ред рдФрд░ counter_cache рдкрд░рд┐рдкреВрд░реНрдг рд╕реЗ рдмрд╣реБрдд рджреВрд░ рдирд╣реАрдВ рд╣реИ, рдФрд░ рдХреБрдЫ рдорд╛рдорд▓реЛрдВ рдореЗрдВ рдХреИрд╢реНрдб рд╕реЗ рд╡рд╛рд╕реНрддрд╡рд┐рдХ рд░рд╛рд╢рд┐ рдХрд╛ рдПрдХ рд╡рдВрд╢рд╛рдиреБрдХреНрд░рдо рд╣реЛ рд╕рдХрддрд╛ рд╣реИред рдЕрд▓реМрдХрд┐рдХ, рдмреЗрд╢рдХ, рд▓реЗрдХрд┐рди рдЕрдкреНрд░рд┐рдпред рдФрд░ рдпрд╣ рдбреЗрдЯрд╛рдмреЗрд╕ рдореЗрдВ рдмрд╕рдиреЗ рд╡рд╛рд▓рд╛ рдкрд╣рд▓рд╛ рдЙрдореНрдореАрджрд╡рд╛рд░ рд╣реИ рдФрд░ рдорд╛рдгрд┐рдХ рдорд╢реАрди рдХреЗ рдкрд╣рд▓реЗ рд╕реЗ рд▓реЛрдб рдХрд┐рдП рдЧрдП рд╕рд┐рд░ рдХреЛ рд▓реЛрдб рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИред рд╕рд┐рд░реНрдл рдПрдХ рджреЛ рдЯреНрд░рд┐рдЧрд░реНрд╕ рдФрд░ рдЖрдк рдХрд░ рд░рд╣реЗ рд╣реИрдВ! рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ, рдкреНрд▓реЗрдЯ рдП рдореЗрдВ рдПрдХ рдирдпрд╛ рд░рд┐рдХреЙрд░реНрдб рд╣рдЯрд╛рддреЗ рдФрд░ рдЬреЛрдбрд╝рддреЗ рд╕рдордп, рдЖрдкрдХреЛ рдкреНрд▓реЗрдЯ рдмреА рдореЗрдВ рд░рд┐рдХреЙрд░реНрдб рдХреА рд╕рдВрдЦреНрдпрд╛ рдФрд░ рдпрд╣ рд╕рд╣реА рд╣реИ, рдХреА рдЧрдгрдирд╛ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ? рдареАрдХ рд╣реИ, рдЬрдм рд╕рдВрдкрд╛рджрди, рдЬрд╝рд╛рд╣рд┐рд░ рд╣реИ, рдЕрдЧрд░ foreign_key рдмрджрд▓ рдЧрдпрд╛ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ UPDATE B SET a_id = $1 WHERE id = $2 рдкреБрд░рд╛рдиреЗ A рдФрд░ рдирдП рджреЛрдиреЛрдВ рдХреЗ рд▓рд┐рдП UPDATE B SET a_id = $1 WHERE id = $2 рддреЛрдбрд╝ рджреЗрдЧрд╛ред


  CREATE OR REPLACE FUNCTION update_#{parent_table}_#{child_table}_counter_on_insert() RETURNS TRIGGER AS $$ BEGIN UPDATE #{parent_table} SET #{counter_column} = COALESCE((SELECT COUNT(id) FROM #{child_table} GROUP BY #{foreign_column} HAVING #{foreign_column} = NEW.#{foreign_column}), 0) WHERE (#{parent_table}.id = NEW.#{foreign_column}); RETURN NULL; END; $$ LANGUAGE plpgsql; 

рдЕрдЧрд▓рд╛ рдбреЗрдЯрд╛рдмреЗрд╕ рдкрде рджрд┐рдирд╛рдВрдХ-рд╕рдордп рд╕реЗ рд╕рдВрдмрдВрдзрд┐рдд рд╣реЛрдЧрд╛ред рдФрд░ рд╢реБрд░реБрдЖрдд рдХреЗ рд▓рд┐рдП, рдЖрдЗрдП рдбреЗрдЯрд╛рдмреЗрд╕ рдореЗрдВ created_at рдФрд░ updated_at рдлрд╝реАрд▓реНрдбреНрд╕ рдХреА рд╕реЗрд╡рд╛ рдХрд░реЗрдВ, рд╕реМрднрд╛рдЧреНрдп рд╕реЗ, рдпрд╣ рдмрд╣реБрдд рд╕рд░рд▓ рд╣реИред рдкрд╣рд▓рд╛ рд╕реЗрдЯ рдЪреВрдХ:


  change_column_default :table_name, :created_at, -> { 'CURRENT_TIMESTAMP' } change_column_default :table_name, :updated_at, -> { 'CURRENT_TIMESTAMP' } 

рдФрд░ рддреБрд░рдВрдд рдЗрд╕реЗ рд╣рд░ рдЬрдЧрд╣ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдЖрдк рд╕рднреА рдкреНрд▓реЗрдЯреЛрдВ рдХреЗ рд▓рд┐рдП рдПрдХ рдЪрдХреНрд░ рдХрд╛ рдЖрдпреЛрдЬрди рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдЬрд╣рд╛рдВ рдпреЗ рдХреНрд╖реЗрддреНрд░ рд╣реИрдВред schema_migrations рдФрд░ ar_internal_metadata рдЕрд▓рд╛рд╡рд╛, рдирд┐рд╢реНрдЪрд┐рдд рд░реВрдк рд╕реЗ:


  (tables - %w(schema_migrations ar_internal_metadata)).each { ... } 

рдпрд╣реА рд╣реИ, рдЕрдм рдЗрди рддрд╛рд▓рд┐рдХрд╛рдУрдВ рдХреЗ рд▓рд┐рдП рдбрд┐рдлрд╝реЙрд▓реНрдЯ рдореВрд▓реНрдп рд╡рд╣реА рд╣реЛрдЧрд╛ рдЬреЛ рд╣рдореЗрдВ рдЪрд╛рд╣рд┐рдПред рдФрд░ рдЕрдм рдпрд╣ рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдХрд░рдиреЗ рдХрд╛ рд╕рдордп рд╣реИ рдХрд┐ рд░реЗрд▓ рдЗрди рдХреНрд╖реЗрддреНрд░реЛрдВ рдХреЛ рди рдЫреВрдПрдВред рдпрд╣ рд╕рд╣реА рд╕реНрдерд╛рдиреЛрдВ рдореЗрдВ рджреЛ рдмреЛрд▓реНрдЯреЛрдВ рдХреЗ рд╕рд╛рде рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рдФрд░ рд╣рд╛рдВ, рдЗрд╕рдХреЗ рд▓рд┐рдП рдЬрд┐рдореНрдореЗрджрд╛рд░ рдврд╛рдВрдЪрд╛ рд╕реНрдерд╛рдкрд┐рдд рдХрд░рдиреЗ рдХрд╛ рдПрдХ рд╡рд┐рдХрд▓реНрдк рд╣реИ:


 Rails.application.config.active_record.record_timestamps = false 

рдЗрд╕рд▓рд┐рдП, рдЕрдЧрд▓рд╛ рдЪрд░рдг рд░рд┐рдХреЙрд░реНрдб рдХрд┐рдП рдЬрд╛рдиреЗ рдХреЗ рд╕рдордп рдЕрдкрдбреЗрдЯ рдХрд┐рдП updated_at рдлреАрд▓реНрдб рдХреЛ рдЕрдкрдбреЗрдЯ рдХрд░рдирд╛ рд╣реИред рдпрд╣ рд╕рд░рд▓ рд╣реИ:


  CREATE OR REPLACE FUNCTION touch_for_#{table_name}_on_update() RETURNS TRIGGER AS $$ BEGIN SELECT CURRENT_TIMESTAMP INTO NEW.updated_at; RETURN NEW; END; $$ LANGUAGE plpgsql; 

рдЕрдм рдЖрдкрдХреЛ рдкреВрд░реА рддрд░рд╣ рд╕реЗ touch: true рд╕реЗ рдЫреБрдЯрдХрд╛рд░рд╛ рдкрд╛рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ touch: true рдореЙрдбрд▓ рдореЗрдВ touch: true ред рдпрд╣ рдмрд╛рдд рдбреИрд╢ рдореЗрдВ рд▓рдХреНрд╖реНрдп рдХреЗ рд╕рдорд╛рди рд╣реИ - рдкреВрд░реА рддрд░рд╣ рд╕реЗ рдЫрд┐рджреНрд░рдкреВрд░реНрдг рднреАред рдФрд░ рдореИрдВ рдпрд╣ рднреА рдирд╣реАрдВ рдмрддрд╛ рд╕рдХрддрд╛ рдХрд┐ рдХреНрдпреЛрдВ, рдХреНрдпреЛрдВрдХрд┐ рдЖрдк рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдЗрди рд╕рднреА рдорд╛рдорд▓реЛрдВ рдХреЛ рдЬрд╛рдирддреЗ рд╣реИрдВред рдпрд╣ рдмрд╣реБрдд рдЕрдзрд┐рдХ рдЬрдЯрд┐рд▓ рдирд╣реАрдВ рд╣реИ, рдЖрдкрдХреЛ рдХреЗрд╡рд▓ рдЕрдкрдиреЗ рдЖрдк рдХреЛ рд╣реА рдЕрджреНрдпрддрди рдХрд░рдиреЗ рдХреА рдЬрд░реВрд░рдд рд╣реИ:


  CREATE OR REPLACE FUNCTION touch_for_#{table_name}_on_update() RETURNS TRIGGER AS $$ BEGIN UPDATE foreign_table_name SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.foreign_column_name; SELECT CURRENT_TIMESTAMP INTO NEW.updated_at; RETURN NEW; END; $$ LANGUAGE plpgsql; 

рдмреЗрд╢рдХ, рдРрд╕реЗ рдЯреНрд░рд┐рдЧрд░реНрд╕ рдХреА рдХреЙрд▓ рдХреА рд╢реНрд░реГрдВрдЦрд▓рд╛ рдПрдХ рдЕрдирд╛рд╡рд╢реНрдпрдХ рдХрд╛рд░реНрд░рд╡рд╛рдИ рдХрд░реЗрдЧреА, рд▓реЗрдХрд┐рди рд╕рд╛рдиреЗ рддрдВрддреНрд░ рдХреЗ рдкреЛрд╕реНрдЯрдЧреНрд░реЗрдЬ рдореЗрдВ, рдЯреНрд░рд┐рдЧрд░реНрд╕ рдХреЛ рд░рд┐рдХреЙрд░реНрдб рдХреЛ рдмрджрд▓рдиреЗ рдХреЗ рдмрд┐рдирд╛ рд╣реА рд▓рд╛рдЧреВ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рдЖрдк SET title = title рдХрд░рдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рд▓реЗрдХрд┐рди рдпрд╣ SET updated_at = CURRENT_TIMESTAMP рд╕реЗ рдмреЗрд╣рддрд░ рдирд╣реАрдВ рд╣реИред


рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдПрдХ рд╣реА рдЯреНрд░рд┐рдЧрд░ рдбрд╛рд▓рдиреЗ рдкрд░ рд╣реЛрдЧрд╛, рдХреЗрд╡рд▓ updated_at рдЕрджреНрдпрддрди рдХрд░рдирд╛ рдЖрд╡рд╢реНрдпрдХ рдирд╣реАрдВ рд╣реИ:


  CREATE OR REPLACE FUNCTION touch_for_#{table_name}_on_insert() RETURNS TRIGGER AS $$ BEGIN UPDATE foreign_table_name SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.foreign_column_name; RETURN NEW; END; $$ LANGUAGE plpgsql; 

рдмреЗрд╢рдХ, рдЖрдк IF TG_OP = 'UPDATE' THEN рд╕рдорд╛рди, рдЯреНрд░рд┐рдЧрд░ рдХреЗ рдЕрдВрджрд░ рд╡рд░реНрддрдорд╛рди рдИрд╡реЗрдВрдЯ рдХреЗ рд▓рд┐рдП рдПрдХ рдЪреЗрдХ рдЬреЛрдбрд╝рдХрд░, рдЗрд╕реЗ рдПрдХ рдлрд╝рдВрдХреНрд╢рди рдХреЗ рд╕рд╛рде рд▓рд┐рдЦрдиреЗ рдХрд╛ рдкреНрд░рдпрд╛рд╕ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рд▓реЗрдХрд┐рди рдХрд┐рд╕реА рддреНрд░реБрдЯрд┐ рдХреА рд╕рдВрднрд╛рд╡рдирд╛ рдХреЛ рдХрдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╕рднреА рдЯреНрд░рд┐рдЧрд░реНрд╕ рдХреЛ рдпрдерд╛рд╕рдВрднрд╡ рд╕рд░рд▓ рдмрдирд╛рдирд╛ рдмреЗрд╣рддрд░ рд╣реИред


рдЖрдк рдЗрд╕ рддрд░рд╣ рдХреЗ рдЯреНрд░рд┐рдЧрд░реНрд╕ рдХреА рдкреАрдврд╝реА рдХреЛ рдХрд┐рд╕реА рддрд░рд╣ рд╕реЗ рд╕реНрд╡рдЪрд╛рд▓рд┐рдд рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ, рдФрд░ рдлрд┐рд░ рдЖрдкрдХреЛ рд╡рд░реНрддрдорд╛рди рдЯреЗрдмрд▓ рдФрд░ рдмрд╛рдХреА рдХреЗ рдмреАрдЪ рд╕рднреА рд╡рд┐рджреЗрд╢реА рд╕рдВрдмрдВрдзреЛрдВ рдХреЛ рдЦреЛрдЬрдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрдЧреАред рдЖрдк рдЗрд╕ рдкреНрд░рд╢реНрди рдХреЛ рдЖрд╕рд╛рдиреА рд╕реЗ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ:


  SELECT ccu.table_name AS foreign_table_name, kcu.column_name AS column_name FROM information_schema.table_constraints AS tc JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name WHERE constraint_type = 'FOREIGN KEY' AND tc.table_name = '#{table_name}' ORDER BY ccu.table_name; 

рдПрдХ рдФрд░ рдмрд╣реБрдд рд╣реА рдЙрдкрдпреЛрдЧреА рдЯрд┐рдкред рдЯреНрд░рд┐рдЧрд░ рдХреЛ рдЙрд╕реА рддрд░рд╣ рд╕реЗ рдХреЙрд▓ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ рдЬреЛ рдХрд┐рд╕реА рдЕрдиреБрд░реЛрдз рдХреЗ рд▓рд┐рдП рдЙрдкрд╕реНрдерд┐рддрд┐ рдпрд╛ рдЕрдиреБрдкрд╕реНрдерд┐рддрд┐ рдХреЛ рд╕рддреНрдпрд╛рдкрд┐рдд рдХрд░рдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рд╣реЛ, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдпрд╣ рдЕрдиреБрд░реЛрдз рд╕рднреА рдЯрдЪ-рдЗрдВрд╕рд░реНрдЯ рдЯреНрд░рд┐рдЧрд░ рдкрд╛рдПрдВрдЧреЗ:


  SELECT routine_name AS name FROM information_schema.routines WHERE routine_name LIKE 'touch_for_%_on_insert' AND routine_type ='FUNCTION' AND specific_schema='public'; 

рдФрд░ рдЕрдВрдд рдореЗрдВ, рд╕рдмрд╕реЗ рдЦрд░рд╛рдм рдмрдЪрд╛ рд╣реИред рддрдереНрдп рдпрд╣ рд╣реИ рдХрд┐ рд░реЗрд▓ рдХреЛ рдХрдо рд╕реЗ рдХрдо рдХрд┐рд╕реА рддрд░рд╣ рдХреЗ рд╕реНрдорд╛рд░реНрдЯ рдбреЗрдЯрд╛рдмреЗрд╕ рдХреЗ рд▓рд┐рдП рдбрд┐рдЬрд╝рд╛рдЗрди рдирд╣реАрдВ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ, рдФрд░ рдЙрдиреНрд╣реЛрдВрдиреЗ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдЗрд╕ рддрдереНрдп рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдкрд░рд╡рд╛рд╣ рдирд╣реАрдВ рдХреА рд╣реИ рдХрд┐ id рдХреНрд╖реЗрддреНрд░ рдХреЗ рдЕрд▓рд╛рд╡рд╛ рдХрдо рд╕реЗ рдХрдо рдХреБрдЫ рдФрд░ рдЪреАрдЬреЗрдВ рдбреЗрдЯрд╛рдмреЗрд╕ рдореЗрдВ рдмрджрд▓ рд╕рдХрддреА рд╣реИрдВ, рдФрд░ рдХреЗрд╡рд▓ рдЬрдм рдбрд╛рд▓рд╛ рдЬрд╛рддрд╛ рд╣реИред рдЗрд╕рд▓рд┐рдП, рдЕрдиреБрд░реЛрдзреЛрдВ рдХреЛ рдЕрджреНрдпрддрди рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП RETURNING updated_at рдХреЛ рдЬреЛрдбрд╝рдиреЗ рдХреЗ рд▓рд┐рдП рдХреЛрдИ рднреА рддрдВрддреНрд░ рдирд╣реАрдВ рд╣реИред



рдореИрдирд┐рдкреИрдЪ рдмрд╣реБрдд рд╕рд╛рдл рдирд╣реАрдВ рдирд┐рдХрд▓рд╛, рд▓реЗрдХрд┐рди рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ рдпрд╣ рд▓рдХреНрд╖реНрдп рдерд╛ рдХрд┐ рдЬрд┐рддрдирд╛ рд╕рдВрднрд╡ рд╣реЛ рдХрдо рд╕реЗ рдХрдо рдлреНрд░реЗрдорд╡рд░реНрдХ рдХреЗ рдореМрдЬреВрджрд╛ рдХрд╛рдо рдХреЛ рдиреБрдХрд╕рд╛рди рдкрд╣реБрдВрдЪрд╛рдпрд╛ рдЬрд╛рдПред


рдореИрдВ рдЙрд╕реЗ рдкреВрд░реА рддрд░рд╣ рд╕реЗ рд▓рд╛рдКрдВрдЧрд╛
 module ActiveRecord module Persistence # https://github.com/rails/rails/blob/v5.2.0/activerecord/lib/active_record/persistence.rb#L729-L741 def _create_record(attribute_names = self.attribute_names) attribute_names &= self.class.column_names attributes_values = attributes_with_values_for_create(attribute_names) an_id, *affected_rows = self.class._insert_record(attributes_values).dup self.id ||= an_id if self.class.primary_key Hash[ApplicationRecord.custom_returning_columns(self.class.quoted_table_name, :create).take(affected_rows.size).zip(affected_rows)].each do |column_name, value| public_send("#{column_name}=", self.class.attribute_types[column_name.to_s].deserialize(value)) if value end @new_record = false yield(self) if block_given? id end private :_create_record # https://github.com/rails/rails/blob/v5.2.0/activerecord/lib/active_record/persistence.rb#L710-L725 def _update_record(attribute_names = self.attribute_names) attribute_names &= self.class.column_names attribute_names = attributes_for_update(attribute_names) if attribute_names.empty? affected_rows = [] @_trigger_update_callback = true else affected_rows = _update_row(attribute_names) @_trigger_update_callback = affected_rows.any? end Hash[ApplicationRecord.custom_returning_columns(self.class.quoted_table_name, :update).take(affected_rows.size).zip(affected_rows)].each do |column_name, value| public_send("#{column_name}=", self.class.attribute_types[column_name.to_s].deserialize(value)) end yield(self) if block_given? affected_rows.none? ? 0 : 1 end private :_update_record end module ConnectionAdapters module PostgreSQL module DatabaseStatements # https://github.com/rails/rails/blob/v5.2.0/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb#L93-L96 def exec_update(sql, name = nil, binds = []) execute_and_clear(sql_with_returning(sql), name, binds) { |result| Array.wrap(result.values.first) } end # https://github.com/rails/rails/blob/v5.2.0/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb#L147-L152 def insert(arel, name = nil, pk = nil, _id_value = nil, sequence_name = nil, binds = []) sql, binds = to_sql_and_binds(arel, binds) exec_insert(sql, name, binds, pk, sequence_name).rows.first end alias create insert # https://github.com/rails/rails/blob/v5.2.0/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb#L98-L111 def sql_for_insert(sql, pk, id_value, sequence_name, binds) # :nodoc: table_ref = extract_table_ref_from_insert_sql(sql) if pk.nil? # Extract the table from the insert sql. Yuck. pk = primary_key(table_ref) if table_ref end returning_columns = quote_returning_column_names(table_ref, pk, :create) if returning_columns.any? sql = "#{sql} RETURNING #{returning_columns.join(', ')}" end super end # No source in original repo def quote_returning_column_names(table_ref, pk, action) returning_columns = [] returning_columns << pk if suppress_composite_primary_key(pk) returning_columns += ApplicationRecord.custom_returning_columns(table_ref, action) returning_columns.map { |column| quote_column_name(column) } end # No source in original repo def sql_with_returning(sql) table_ref = extract_table_ref_from_update_sql(sql) returning_columns = quote_returning_column_names(table_ref, nil, :update) return sql if returning_columns.blank? "#{sql} RETURNING #{returning_columns.join(', ')}" end # No source in original repo def extract_table_ref_from_update_sql(sql) sql[/update\s("[A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*set/im] Regexp.last_match(1)&.strip end end end end end 

рд╕рдмрд╕реЗ рдорд╣рддреНрд╡рдкреВрд░реНрдг рдмрд╛рдд рдпрд╣ рд╣реИ рдХрд┐ рдпрд╣рд╛рдБ рд╣рдо рдпрд╣ рдЬрд╛рдирдиреЗ рдХреЗ рд▓рд┐рдП ApplicationRecord.custom_returning_columns рдУрд░ рд░реБрдЦ рдХрд░рддреЗ рд╣реИрдВ рдХрд┐ рдЖрдИрдбреА рдХреЗ рдЕрд▓рд╛рд╡рд╛ рдХреМрди рд╕реЗ рдХреЙрд▓рдо рд╣рдорд╛рд░реЗ рд▓рд┐рдП рджрд┐рд▓рдЪрд╕реНрдк рд╣реИрдВред рдФрд░ рдпрд╣ рд╡рд┐рдзрд┐ рдХреБрдЫ рдЗрд╕ рддрд░рд╣ рджрд┐рдЦрддреА рд╣реИ:


  class << self def custom_returning_columns(table_ref, action) return [] if ['"schema_migrations"', '"ar_internal_metadata"'].include?(table_ref) res = [] res << :created_at if action == :create res << :updated_at res += case table_ref when '"user_applications"' [:api_token] when '"users"' [:session_salt, :password_changed_at] # ... else [] end res end end 



рдирд┐рд╖реНрдХрд░реНрд╖ рдХреЗ рдмрдЬрд╛рдп, рд╣рдо рдпрд╣ рдХрд╣ рд╕рдХрддреЗ рд╣реИрдВ рдХрд┐, рд░реЗрд▓ рдХреЗ рдХрд┐рдирд╛рд░реЗ рдХрд╛ рд╕рд┐рд░ рдереЛрдбрд╝рд╛ рдХрдо рд╣реЛ рдЧрдпрд╛ рд╣реИред counter_cache рдФрд░ touch рдЬреИрд╕реА counter_cache рдкреНрд░рдХреНрд░рд┐рдпрд╛рдПрдВ рдЧреБрдордирд╛рдореА рдореЗрдВ рдбреВрдм рдЬрд╛рдПрдВрдЧреА рдФрд░ рдЕрдЧрд▓реЗ рд▓реЗрдЦ рдореЗрдВ рд╣рдо рдХреБрдЫ рдФрд░ рдЧреНрд▓реЛрдмрд▓ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рд╕реЛрдЪ рд╕рдХрддреЗ рд╣реИрдВ, рдЬреИрд╕реЗ рд╣реИрдВрдЧрд┐рдВрдЧ рд╕реНрдкреЗрд╕ рдХреЛ рд╣рдЯрд╛рдирд╛, рдбреЗрдЯрд╛ рд╡реЗрд▓рд┐рдбреЗрд╢рди, рдХреИрд╕реНрдХреЗрдбрд┐рдВрдЧ рдбреЗрдЯрд╛ рдбрд┐рд▓реАрдЯ рдпрд╛ рдкреИрд░рд╛рдиреЙрдпрдб рдбрд┐рд▓реАрдЯред рдЕрдЧрд░ рдЖрдкрдХреЛ рдпрд╣ рд▓реЗрдЦ рдкрд╕рдВрдж рдЖрдпрд╛ рд╣реЛ рддреЛ рдЕрд╡рд╢реНрдп

Source: https://habr.com/ru/post/hi421939/


All Articles