{"id":483420,"date":"2026-06-12T11:29:52","date_gmt":"2026-06-12T11:29:52","guid":{"rendered":"https:\/\/savepearlharbor.com\/?p=483420"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=483420","title":{"rendered":"sqlh \u2014 SQL \u0432 Go \u0431\u0435\u0437 boilerplate: \u043f\u0438\u0448\u0435\u043c CRUD \u0437\u0430 50 \u0441\u0442\u0440\u043e\u043a"},"content":{"rendered":"<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>&gt; <em>Zero-boilerplate SQL \u0434\u043b\u044f Go. \u041e\u043f\u0438\u0448\u0438 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0442\u0435\u0433\u0430\u043c\u0438 \u2014 \u0438 \u044d\u0442\u043e \u0432\u0441\u0451.<\/em><\/p>\n<p>\u0415\u0441\u043b\u0438 \u0432\u044b \u043f\u0438\u0448\u0435\u0442\u0435 \u043d\u0430 Go \u0438 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442\u0435 \u0441 SQL-\u0431\u0430\u0437\u0430\u043c\u0438, \u0432\u044b \u0437\u043d\u0430\u0435\u0442\u0435 \u044d\u0442\u0443 \u0431\u043e\u043b\u044c. \u041a\u0430\u0436\u0434\u044b\u0439 CRUD-\u0437\u0430\u043f\u0440\u043e\u0441 \u2014 \u0440\u0443\u0447\u043d\u043e\u0439 SQL-\u0441\u0442\u0440\u043e\u043a\u0430, <code>rows.Scan<\/code> \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043f\u043e\u043b\u044f, <code>Begin\/Commit\/Rollback<\/code> \u0432\u043e\u043a\u0440\u0443\u0433 \u0437\u0430\u043f\u0438\u0441\u0438, \u0438 \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u0430\u044f \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u044f DDL-\u0441\u0445\u0435\u043c\u044b \u0441 \u043a\u043e\u0434\u043e\u043c. \u0428\u0430\u0431\u043b\u043e\u043d\u043d\u044b\u0439 \u043a\u043e\u0434 \u043d\u0435 \u0437\u0430\u043a\u0430\u043d\u0447\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u043d\u0438\u043a\u043e\u0433\u0434\u0430.<\/p>\n<p>\u042d\u0442\u043e \u0440\u0430\u0441\u0441\u043a\u0430\u0437 \u043e <code>sqlh<\/code> \u2014 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0435, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0443\u0431\u0438\u0440\u0430\u0435\u0442 \u0432\u0441\u0451 \u044d\u0442\u043e, \u043e\u0441\u0442\u0430\u0432\u0430\u044f\u0441\u044c \u0432 \u00ab\u0437\u043e\u043b\u043e\u0442\u043e\u0439 \u0441\u0435\u0440\u0435\u0434\u0438\u043d\u0435\u00bb \u043c\u0435\u0436\u0434\u0443 raw SQL (\u0441\u043b\u0438\u0448\u043a\u043e\u043c \u043c\u043d\u043e\u0433\u043e \u0440\u0430\u0431\u043e\u0442\u044b) \u0438 \u0442\u044f\u0436\u0451\u043b\u044b\u043c\u0438 ORM (\u0441\u043b\u0438\u0448\u043a\u043e\u043c \u043c\u043d\u043e\u0433\u043e \u043c\u0430\u0433\u0438\u0438).<\/p>\n<p><strong>## \u00a71. \u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430: Go + SQL = \u0441\u043c\u0435\u0440\u0442\u044c \u043e\u0442 \u0442\u044b\u0441\u044f\u0447\u0438 <\/strong><code><strong>rows.Scan<\/strong><\/code><\/p>\n<p>\u0421\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 <code>database\/sql<\/code> \u0432 Go \u043e\u0442\u043b\u0438\u0447\u0435\u043d. \u041e\u043d \u0434\u0430\u0451\u0442 \u043f\u0440\u043e\u0447\u043d\u044b\u0439, \u043f\u0435\u0440\u0435\u043d\u043e\u0441\u0438\u043c\u044b\u0439 \u0444\u0443\u043d\u0434\u0430\u043c\u0435\u043d\u0442 \u0434\u043b\u044f \u043b\u044e\u0431\u043e\u0439 SQL-\u0431\u0430\u0437\u044b. \u041d\u043e \u043e\u043d \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u043d\u043e \u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0442\u044f\u0436\u0451\u043b\u0443\u044e \u0440\u0430\u0431\u043e\u0442\u0443 \u0437\u0430 \u0432\u0430\u043c\u0438.<\/p>\n<p>\u0412\u043e\u0442 \u043a\u0430\u043a \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u043f\u0440\u043e\u0441\u0442\u043e\u0439 CRUD \u043d\u0430 \u0447\u0438\u0441\u0442\u043e\u043c <code>database\/sql<\/code>:<\/p>\n<p>&#171;`go<\/p>\n<p>\/\/ 1. CREATE TABLE \u2014 raw DDL-\u0441\u0442\u0440\u043e\u043a\u0430<\/p>\n<p>_, err := db.Exec(`CREATE TABLE IF NOT EXISTS user (<\/p>\n<p>\u00a0 \u00a0 id INTEGER PRIMARY KEY AUTOINCREMENT,<\/p>\n<p>\u00a0 \u00a0 name TEXT UNIQUE,<\/p>\n<p>\u00a0 \u00a0 email TEXT,<\/p>\n<p>\u00a0 \u00a0 age INTEGER<\/p>\n<p>)`)<\/p>\n<p>\/\/ 2. INSERT \u2014 \u044f\u0432\u043d\u044b\u0435 placeholder \u0438 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b<\/p>\n<p>_, err = db.Exec(<\/p>\n<p>\u00a0 \u00a0 &#171;INSERT INTO user (name, email, age) VALUES (?, ?, ?)&#187;,<\/p>\n<p>\u00a0 \u00a0 &#171;Alice&#187;, &#171;<a href=\"mailto:alice@example.com\" rel=\"noopener noreferrer nofollow\">alice@example.com<\/a>&#171;, 30,<\/p>\n<p>)<\/p>\n<p>\/\/ 3. GET \u043f\u043e ID \u2014 QueryRow + \u0440\u0443\u0447\u043d\u043e\u0439 Scan<\/p>\n<p>var u User<\/p>\n<p>err = db.QueryRow(&#171;SELECT id, name, email, age FROM user WHERE id = ?&#187;, 1).<\/p>\n<p>\u00a0 \u00a0 Scan(&amp;<a href=\"http:\/\/u.ID\" rel=\"noopener noreferrer nofollow\">u.ID<\/a>, &amp;<a href=\"http:\/\/u.Name\" rel=\"noopener noreferrer nofollow\">u.Name<\/a>, &amp;<a href=\"http:\/\/u.Email\" rel=\"noopener noreferrer nofollow\">u.Email<\/a>, &amp;u.Age)<\/p>\n<p>\/\/ 4. LIST \u0432\u0441\u0435\u0445 \u2014 Query + <a href=\"http:\/\/rows.Next\" rel=\"noopener noreferrer nofollow\">rows.Next<\/a> + rows.Scan \u0432 \u0446\u0438\u043a\u043b\u0435<\/p>\n<p>rows, err := db.Query(&#171;SELECT id, name, email, age FROM user ORDER BY name ASC&#187;)<\/p>\n<p>var users []User<\/p>\n<p>for <a href=\"http:\/\/rows.Next\" rel=\"noopener noreferrer nofollow\">rows.Next<\/a>() {<\/p>\n<p>\u00a0 \u00a0 var u User<\/p>\n<p>\u00a0 \u00a0 if err := rows.Scan(&amp;<a href=\"http:\/\/u.ID\" rel=\"noopener noreferrer nofollow\">u.ID<\/a>, &amp;<a href=\"http:\/\/u.Name\" rel=\"noopener noreferrer nofollow\">u.Name<\/a>, &amp;<a href=\"http:\/\/u.Email\" rel=\"noopener noreferrer nofollow\">u.Email<\/a>, &amp;u.Age); err != nil {<\/p>\n<p>\u00a0 \u00a0 \u00a0 \u00a0 log.Fatal(err)<\/p>\n<p>\u00a0 \u00a0 }<\/p>\n<p>\u00a0 \u00a0 users = append(users, u)<\/p>\n<p>}<\/p>\n<p>rows.Close()<\/p>\n<p>\/\/ 5. UPDATE \u2014 raw SQL \u0441 placeholder<\/p>\n<p>_, err = db.Exec(<\/p>\n<p>\u00a0 \u00a0 &#171;UPDATE user SET email = ?, age = ? WHERE id = ?&#187;,<\/p>\n<p>\u00a0 \u00a0 &#171;<a href=\"mailto:alice.new@example.com\" rel=\"noopener noreferrer nofollow\">alice.new@example.com<\/a>&#171;, 31, 1,<\/p>\n<p>)<\/p>\n<p>\/\/ 6. DELETE \u2014 raw SQL<\/p>\n<p>_, err = db.Exec(&#171;DELETE FROM user WHERE id = ?&#187;, 1)<\/p>\n<p>&#171;`<\/p>\n<p>\u042d\u0442\u043e <strong>~115 \u0441\u0442\u0440\u043e\u043a \u043a\u043e\u0434\u0430<\/strong> \u0434\u043b\u044f \u0448\u0435\u0441\u0442\u0438 \u0431\u0430\u0437\u043e\u0432\u044b\u0445 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439. \u0418 \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437, \u043a\u043e\u0433\u0434\u0430 \u0432\u044b \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442\u0435 \u0441\u0442\u043e\u043b\u0431\u0435\u0446, \u043d\u0443\u0436\u043d\u043e \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u0441\u0442\u0440\u043e\u043a\u0443 <code>CREATE TABLE<\/code>, \u0441\u043f\u0438\u0441\u043e\u043a \u043a\u043e\u043b\u043e\u043d\u043e\u043a \u0432 <code>INSERT<\/code>, \u0441\u043f\u0438\u0441\u043e\u043a \u0432 <code>SELECT<\/code>, \u0438 \u0432\u044b\u0437\u043e\u0432 <code>rows.Scan<\/code>. \u041e\u043f\u0435\u0447\u0430\u0442\u043a\u0430 \u0432 \u043b\u044e\u0431\u043e\u043c \u043c\u0435\u0441\u0442\u0435 \u2014 runtime-\u043e\u0448\u0438\u0431\u043a\u0430, compile-time \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438 \u043d\u0435\u0442.<\/p>\n<p>| \u0411\u043e\u043b\u044c | \u041f\u043e\u0447\u0435\u043c\u0443 \u0431\u043e\u043b\u044c\u043d\u043e |<\/p>\n<p>|&#8212;&#8212;|&#8212;&#8212;&#8212;&#8212;&#8212;|<\/p>\n<p>| \u0420\u0443\u0447\u043d\u043e\u0439 SQL | \u041a\u0430\u0436\u0434\u044b\u0439 CRUD \u2014 raw SQL-\u0441\u0442\u0440\u043e\u043a\u0430, \u043d\u0435\u0442 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043d\u0430 \u044d\u0442\u0430\u043f\u0435 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0446\u0438\u0438 |<\/p>\n<p>| <code>rows.Scan<\/code> | 4\u20135 \u0441\u0442\u0440\u043e\u043a \u043d\u0430 \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u043c\u0430\u043f\u043f\u0438\u043d\u0433\u0430 \u043a\u043e\u043b\u043e\u043d\u043e\u043a \u043d\u0430 \u043f\u043e\u043b\u044f |<\/p>\n<p>| \u0422\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u0438 | <code>db.Begin()<\/code> + <code>defer tx.Rollback()<\/code> + <code>tx.Commit()<\/code> \u2014 \u0432\u0435\u0437\u0434\u0435 |<\/p>\n<p>| \u041d\u0435\u0442 \u0441\u0432\u044f\u0437\u0438 \u0441\u043e \u0441\u0445\u0435\u043c\u043e\u0439 | DDL \u0432 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u044f\u0445, \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u0432 Go \u2014 \u043e\u043d\u0438 \u0440\u0430\u0441\u0445\u043e\u0434\u044f\u0442\u0441\u044f |<\/p>\n<p>| \u041f\u043e\u0440\u044f\u0434\u043e\u043a \u043a\u043e\u043b\u043e\u043d\u043e\u043a | \u041d\u043e\u0432\u044b\u0439 \u0441\u0442\u043e\u043b\u0431\u0435\u0446 \u2192 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c SQL-\u0441\u0442\u0440\u043e\u043a\u0438 <strong>\u0438<\/strong> <code>Scan<\/code>-\u0432\u044b\u0437\u043e\u0432\u044b |<\/p>\n<p><strong>## \u00a72. \u0421\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u044f: sqlx \u0438 GORM<\/strong><\/p>\n<p>\u0412 \u044d\u043a\u043e\u0441\u0438\u0441\u0442\u0435\u043c\u0435 Go \u0435\u0441\u0442\u044c \u0434\u0432\u0430 \u0438\u0437\u0432\u0435\u0441\u0442\u043d\u044b\u0445 \u043f\u0443\u0442\u0438. \u0423 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0441\u0432\u043e\u0438 \u043a\u043e\u043c\u043f\u0440\u043e\u043c\u0438\u0441\u0441\u044b.<\/p>\n<p><strong>### sqlx: \u043b\u0443\u0447\u0448\u0435, \u043d\u043e \u0432\u0441\u0451 \u0435\u0449\u0451 \u0440\u0443\u0447\u043d\u043e\u0439 SQL<\/strong><\/p>\n<p>[sqlx](<a href=\"https:\/\/github.com\/jmoiron\/sqlx\" rel=\"noopener noreferrer nofollow\">https:\/\/github.com\/jmoiron\/sqlx<\/a>) \u2014 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u043e\u0435 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 <code>database\/sql<\/code>. \u041e\u043d \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 <code>StructScan<\/code>, <code>Get<\/code>, <code>Select<\/code>, \u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b. SQL \u043f\u0438\u0448\u0435\u0442\u0435 \u043f\u043e-\u043f\u0440\u0435\u0436\u043d\u0435\u043c\u0443 \u0440\u0443\u043a\u0430\u043c\u0438, \u043d\u043e <code>rows.Scan<\/code> \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d.<\/p>\n<p>&#171;`go<\/p>\n<p>\/\/ sqlx: \u0432\u0441\u0451 \u0435\u0449\u0451 \u0440\u0443\u0447\u043d\u043e\u0439 SQL, \u043d\u043e StructScan \u0443\u0431\u0438\u0440\u0430\u0435\u0442 Scan<\/p>\n<p>var u User<\/p>\n<p>dbx.Get(&amp;u, &#171;SELECT id, name, email, age FROM user WHERE id = ?&#187;, 1)<\/p>\n<p>&#171;`<\/p>\n<p>sqlx \u0441\u044d\u043a\u043e\u043d\u043e\u043c\u0438\u0442 \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e <strong>30% boilerplate<\/strong> (\u0434\u043e ~80 \u0441\u0442\u0440\u043e\u043a). \u041d\u043e <code>CREATE TABLE<\/code>, <code>INSERT<\/code>, <code>SELECT<\/code>, <code>UPDATE<\/code>, <code>DELETE<\/code> \u2014 \u0432\u0441\u0451 \u0435\u0449\u0451 \u043f\u0438\u0448\u0435\u0442\u0435 \u0432\u0440\u0443\u0447\u043d\u0443\u044e. \u0413\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f SQL \u2014 \u043d\u0435 \u0435\u0433\u043e \u0437\u0430\u0434\u0430\u0447\u0430.<\/p>\n<p><strong>### GORM: \u043f\u043e\u043b\u043d\u044b\u0439 ORM, \u043f\u043e\u043b\u043d\u0430\u044f \u043c\u0430\u0433\u0438\u044f<\/strong><\/p>\n<p>[GORM](<a href=\"https:\/\/gorm.io\/\" rel=\"noopener noreferrer nofollow\">https:\/\/gorm.io\/<\/a>) \u2014 \u0442\u044f\u0436\u0435\u043b\u043e\u0432\u0435\u0441. \u0413\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442 \u0432\u0441\u0451 \u2014 \u0441\u0445\u0435\u043c\u0443, \u0437\u0430\u043f\u0440\u043e\u0441\u044b, \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u2014 \u0438 \u0434\u0430\u0451\u0442 \u0431\u043e\u0433\u0430\u0442\u044b\u0439 chainable API. \u041d\u043e \u0446\u0435\u043d\u0430 \u0432\u044b\u0441\u043e\u043a\u0430:<\/p>\n<p>&#8212; <strong>\u0422\u044f\u0436\u0451\u043b\u044b\u0439 reflection<\/strong> \u0432 runtime<\/p>\n<p>&#8212; <strong>\u041a\u0440\u0443\u0442\u0430\u044f \u043a\u0440\u0438\u0432\u0430\u044f \u043e\u0431\u0443\u0447\u0435\u043d\u0438\u044f<\/strong> \u2014 \u0442\u0435\u0433\u0438, \u0445\u0443\u043a\u0438, scopes, \u0430\u0441\u0441\u043e\u0446\u0438\u0430\u0446\u0438\u0438<\/p>\n<p>&#8212; <strong>~4 MB \u0443\u0432\u0435\u043b\u0438\u0447\u0435\u043d\u0438\u0435 \u0431\u0438\u043d\u0430\u0440\u043d\u0438\u043a\u0430<\/strong> \u0442\u043e\u043b\u044c\u043a\u043e \u0437\u0430 ORM<\/p>\n<p>&#8212; <strong>\u041c\u0430\u0433\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0441\u043a\u0440\u044b\u0432\u0430\u0435\u0442 \u0441\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u044c<\/strong> \u2014 \u043f\u043e\u043a\u0430 \u043d\u0435 \u0441\u043b\u043e\u043c\u0430\u0435\u0442\u0441\u044f, \u0438 \u0432\u044b \u0447\u0430\u0441\u0430\u043c\u0438 \u0434\u0435\u0431\u0430\u0436\u0438\u0442\u0435<\/p>\n<p>\u0414\u043b\u044f \u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u043a\u043e\u043c\u0430\u043d\u0434 \u0441 \u0432\u044b\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u043c\u0438 DBA \u0438 \u0441\u043b\u043e\u0436\u043d\u044b\u043c\u0438 \u043c\u043e\u0434\u0435\u043b\u044f\u043c\u0438 GORM \u2014 solid choice. \u0414\u043b\u044f CLI-\u0443\u0442\u0438\u043b\u0438\u0442, \u0441\u0442\u0430\u0440\u0442\u0430\u043f\u043e\u0432 \u0438 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u2014 overkill.<\/p>\n<p><strong>### sqlh: \u0437\u043e\u043b\u043e\u0442\u0430\u044f \u0441\u0435\u0440\u0435\u0434\u0438\u043d\u0430<\/strong><\/p>\n<p>| \u0424\u0438\u0447\u0430 | <code>database\/sql<\/code> | sqlx | GORM | <strong>sqlh<\/strong> |<\/p>\n<p>|&#8212;|&#8212;|&#8212;|&#8212;|&#8212;|<\/p>\n<p>| SQL-\u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f | \u274c \u0420\u0443\u0447\u043d\u0430\u044f | \u274c \u0420\u0443\u0447\u043d\u0430\u044f | \u2705 \u041f\u043e\u043b\u043d\u0430\u044f | \u2705 \u041f\u043e\u043b\u043d\u0430\u044f |<\/p>\n<p>| <code>rows.Scan<\/code> | \u2705 \u041d\u0443\u0436\u0435\u043d | \u274c <code>StructScan<\/code> | \u274c \u0410\u0432\u0442\u043e | \u274c \u0410\u0432\u0442\u043e |<\/p>\n<p>| \u0422\u0438\u043f\u043e\u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u044c (generics) | \u274c | \u274c | \u274c | \u2705 |<\/p>\n<p>| \u0410\u0432\u0442\u043e-\u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u0438 | \u274c | \u274c | \u2705 | \u2705 |<\/p>\n<p>| \u0420\u0435\u0442\u0440\u0430\u0439 \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u043e\u043a | \u274c | \u274c | \u274c | \u2705 |<\/p>\n<p>| \u041a\u0440\u0438\u0432\u0430\u044f \u043e\u0431\u0443\u0447\u0435\u043d\u0438\u044f | \u0421\u0440\u0435\u0434\u043d\u044f\u044f | \u0421\u0440\u0435\u0434\u043d\u044f\u044f | \u0412\u044b\u0441\u043e\u043a\u0430\u044f | <strong>\u041d\u0438\u0437\u043a\u0430\u044f<\/strong> |<\/p>\n<p>| \u041e\u0432\u0435\u0440\u0445\u0435\u0434 \u0431\u0438\u043d\u0430\u0440\u043d\u0438\u043a\u0430 | 0 | ~200 KB | ~4 MB | ~200 KB |<\/p>\n<p>sqlh \u0436\u0438\u0432\u0451\u0442 \u043c\u0435\u0436\u0434\u0443 sqlx \u0438 GORM:<\/p>\n<p>&#8212; <strong>Zero-boilerplate CRUD<\/strong> \u2014 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0435 \u0442\u0435\u0433\u0438 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u044e\u0442 \u0432\u0435\u0441\u044c SQL<\/p>\n<p>&#8212; <strong>\u0422\u0438\u043f\u043e\u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u044c \u0447\u0435\u0440\u0435\u0437 Go generics<\/strong> \u2014 <code>Get[User]()<\/code> \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 <code>*User<\/code>, \u043d\u0435 <code>interface{}<\/code><\/p>\n<p>&#8212; <strong>\u041d\u0438\u043a\u0430\u043a\u043e\u0439 \u043c\u0430\u0433\u0438\u0438<\/strong> \u2014 \u0447\u0442\u043e \u0432\u0438\u0434\u0438\u0442\u0435 \u0432 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0435, \u0442\u043e \u0438 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0435 \u0432 \u0431\u0430\u0437\u0435<\/p>\n<p>&#8212; <strong>\u041b\u0451\u0433\u043a\u0438\u0439<\/strong> \u2014 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u0439 reflection, \u043a\u0435\u0448 \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0445, \u043d\u0438\u043a\u0430\u043a\u043e\u0439 \u0441\u043a\u0440\u044b\u0442\u043e\u0439 \u0441\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u0438<\/p>\n<p><strong>## \u00a73. \u041a\u0430\u043a sqlh \u0440\u0435\u0448\u0430\u0435\u0442 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443: \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0435 \u0442\u0435\u0433\u0438 \u043a\u0430\u043a \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a \u043f\u0440\u0430\u0432\u0434\u044b<\/strong><\/p>\n<p>\u0418\u0434\u0435\u044f \u043f\u0440\u043e\u0441\u0442\u0430: <strong>\u0432\u0430\u0448\u0430 Go-\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u2014 \u044d\u0442\u043e \u0432\u0430\u0448\u0430 \u0441\u0445\u0435\u043c\u0430<\/strong>.<\/p>\n<p>&#171;`go<\/p>\n<p>type User struct {<\/p>\n<p>\u00a0 \u00a0 ID \u00a0 \u00a0int64 \u00a0<code>db:\"id\" db_key:\"not null primary key autoincrement\"<\/code><\/p>\n<p>\u00a0 \u00a0 Name \u00a0string <code>db:\"name\" db_key:\"unique\"<\/code><\/p>\n<p>\u00a0 \u00a0 Email string <code>db:\"email\"<\/code><\/p>\n<p>\u00a0 \u00a0 Age \u00a0 int \u00a0 \u00a0<code>db:\"age\"<\/code><\/p>\n<p>}<\/p>\n<p>&#171;`<\/p>\n<p>\u0422\u0440\u0438 \u0442\u0435\u0433\u0430 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u044e\u0442 \u0432\u0441\u0435\u043c:<\/p>\n<p>| \u0422\u0435\u0433 | \u041d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 | \u041f\u0440\u0438\u043c\u0435\u0440 |<\/p>\n<p>|&#8212;&#8212;|&#8212;&#8212;&#8212;&#8212;|&#8212;&#8212;&#8212;|<\/p>\n<p>| <code>db<\/code> | \u0418\u043c\u044f \u043a\u043e\u043b\u043e\u043d\u043a\u0438 | <code>db:\"user_name\"<\/code> |<\/p>\n<p>| <code>db_key<\/code> | \u041e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f, \u0438\u043d\u0434\u0435\u043a\u0441\u044b | <code>db_key:\"primary key autoincrement\"<\/code> |<\/p>\n<p>| <code>db_type<\/code> | \u041f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0442\u0438\u043f\u0430 SQL | <code>db_type:\"TEXT\"<\/code> |<\/p>\n<p>\u0418\u0437 \u044d\u0442\u043e\u0433\u043e \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0433\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f sqlh \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442:<\/p>\n<p>&#8212; <strong>CREATE TABLE<\/strong> \u2014 <code>sqlh.Create[User](db)<\/code> \u2192 <code>CREATE TABLE IF NOT EXISTS user (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE, email TEXT, age INTEGER)<\/code><\/p>\n<p>&#8212; <strong>INSERT<\/strong> \u2014 <code>sqlh.Insert(db, User{Name: \"Alice\"})<\/code> \u2192 <code>INSERT INTO user (name, email, age) VALUES (?, ?, ?)<\/code><\/p>\n<p>&#8212; <strong>SELECT<\/strong> \u2014 <code>sqlh.Get[User](db, ...)<\/code> \u2192 <code>SELECT id, name, email, age FROM user WHERE ... LIMIT 2<\/code><\/p>\n<p>&#8212; <strong>UPDATE<\/strong> \u2014 <code>sqlh.Update(db, ...)<\/code> \u2192 <code>UPDATE user SET name=?, email=?, age=? WHERE ...<\/code><\/p>\n<p>&#8212; <strong>DELETE<\/strong> \u2014 <code>sqlh.Delete[User](db, ...)<\/code> \u2192 <code>DELETE FROM user WHERE ...<\/code><\/p>\n<p><strong>### \u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430<\/strong><\/p>\n<p>&#171;`<\/p>\n<p>\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510<\/p>\n<p>\u2502 \u00a0sqlh package \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u2502<\/p>\n<p>\u2502 \u00a0Insert, Get, List, Update, Delete, Set, \u00a0 \u00a0\u2502<\/p>\n<p>\u2502 \u00a0Create \u2014 \u0441 \u0430\u0432\u0442\u043e-\u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u044f\u043c\u0438 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u2502<\/p>\n<p>\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524<\/p>\n<p>\u2502 \u00a0query package \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u2502<\/p>\n<p>\u2502 \u00a0SQL-\u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f, \u043a\u0435\u0448 \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0445, JOIN \u00a0 \u00a0 \u00a0 \u00a0\u2502<\/p>\n<p>\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524<\/p>\n<p>\u2502 \u00a0database\/sql (stdlib) \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u2502<\/p>\n<p>\u2502 \u00a0\u041f\u0443\u043b \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0439, \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 raw-\u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u00a0 \u00a0\u2502<\/p>\n<p>\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518<\/p>\n<p>&#171;`<\/p>\n<p><strong>### \u041a\u043b\u044e\u0447\u0435\u0432\u044b\u0435 \u0434\u0438\u0437\u0430\u0439\u043d-\u0440\u0435\u0448\u0435\u043d\u0438\u044f<\/strong><\/p>\n<p>1. <strong>Generics-first (Go 1.25+)<\/strong> \u2014 <code>Get[User]()<\/code> \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 <code>*User<\/code> \u0441 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u043e\u0439 \u0442\u0438\u043f\u043e\u0432 \u043d\u0430 \u044d\u0442\u0430\u043f\u0435 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0446\u0438\u0438. \u041d\u0438\u043a\u0430\u043a\u0438\u0445 <code>interface{}<\/code>, \u043d\u0438\u043a\u0430\u043a\u0438\u0445 \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u0438\u0439 \u0442\u0438\u043f\u043e\u0432.<\/p>\n<p>2. <strong>\u0420\u0435\u0444\u043b\u0435\u043a\u0441\u0438\u044f \u043e\u0434\u0438\u043d \u0440\u0430\u0437<\/strong> \u2014 \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0435 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u043f\u0430\u0440\u0441\u044f\u0442\u0441\u044f \u0438 \u043a\u0435\u0448\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u0432 <a href=\"http:\/\/sync.Map\" rel=\"noopener noreferrer nofollow\"><code>sync.Map<\/code><\/a> \u043f\u043e <code>reflect.Type<\/code>. \u041f\u043e\u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0432\u044b\u0437\u043e\u0432\u044b \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 \u0438\u043c\u0435\u043d\u0430 \u0442\u0430\u0431\u043b\u0438\u0446, \u0441\u043f\u0438\u0441\u043a\u0438 \u043f\u043e\u043b\u0435\u0439, scan-\u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0435.<\/p>\n<p>3. <strong>\u0410\u0432\u0442\u043e-\u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u0438 \u043d\u0430 \u0437\u0430\u043f\u0438\u0441\u044c<\/strong> \u2014 \u043a\u0430\u0436\u0434\u044b\u0439 <code>Insert<\/code>, <code>Update<\/code>, <code>Delete<\/code>, <code>Set<\/code> \u043e\u0431\u0451\u0440\u043d\u0443\u0442 \u0432 <code>BEGIN...COMMIT<\/code> \u0441 <code>ROLLBACK<\/code> \u043f\u0440\u0438 \u043e\u0448\u0438\u0431\u043a\u0435. \u0422\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u0438 \u043d\u0438\u043a\u043e\u0433\u0434\u0430 \u043d\u0435 \u0437\u0430\u0431\u0443\u0434\u0435\u0442\u0435.<\/p>\n<p>4. <strong>\u0420\u0435\u0442\u0440\u0430\u0439 \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u043e\u043a SQLite<\/strong> \u2014 \u043e\u0448\u0438\u0431\u043a\u0438 \u00abdatabase is locked\u00bb \u0440\u0435\u0442\u0440\u0430\u044f\u0442\u0441\u044f \u0434\u043e 20 \u0440\u0430\u0437 \u0441 backoff 100 ms. Production-\u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432\u043e\u0441\u0442\u044c \u0438\u0437 \u043a\u043e\u0440\u043e\u0431\u043a\u0438.<\/p>\n<p>5. <strong>\u041c\u0443\u043b\u044c\u0442\u0438-\u0411\u0414<\/strong> \u2014 SQLite (\u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439), MySQL, PostgreSQL (\u043e\u0431\u0430 \u0432 CI), SQL Server (\u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u0430\u043b\u044c\u043d\u043e).<\/p>\n<p><strong>## \u00a74. CRUD \u0437\u0430 50 \u0441\u0442\u0440\u043e\u043a: \u0431\u044b\u0441\u0442\u0440\u044b\u0439 \u0441\u0442\u0430\u0440\u0442<\/strong><\/p>\n<p>\u0422\u043e\u0442 \u0436\u0435 CRUD, \u0447\u0442\u043e \u0432 \u043d\u0430\u0447\u0430\u043b\u0435 \u2014 \u043d\u043e <strong>~57% \u043a\u043e\u0440\u043e\u0447\u0435<\/strong>:<\/p>\n<p>&#171;`go<\/p>\n<p>package main<\/p>\n<p>import (<\/p>\n<p>\u00a0 \u00a0 &#171;database\/sql&#187;<\/p>\n<p>\u00a0 \u00a0 &#171;fmt&#187;<\/p>\n<p>\u00a0 \u00a0 &#171;github.com\/kirill-scherba\/sqlh&#187;<\/p>\n<p>\u00a0 \u00a0 _ &#171;github.com\/mattn\/go-sqlite3&#187;<\/p>\n<p>)<\/p>\n<p>type User struct {<\/p>\n<p>\u00a0 \u00a0 ID \u00a0 \u00a0int64 \u00a0<code>db:\"id\" db_key:\"not null primary key autoincrement\"<\/code><\/p>\n<p>\u00a0 \u00a0 Name \u00a0string <code>db:\"name\" db_key:\"unique\"<\/code><\/p>\n<p>\u00a0 \u00a0 Email string <code>db:\"email\"<\/code><\/p>\n<p>\u00a0 \u00a0 Age \u00a0 int \u00a0 \u00a0<code>db:\"age\"<\/code><\/p>\n<p>}<\/p>\n<p>func main() {<\/p>\n<p>\u00a0 \u00a0 db, _ := <a href=\"http:\/\/sql.Open\" rel=\"noopener noreferrer nofollow\">sql.Open<\/a>(&#171;sqlite3&#187;, &#171;file::memory:?cache=shared&#187;)<\/p>\n<p>\u00a0 \u00a0 defer db.Close()<\/p>\n<p>\u00a0 \u00a0 \/\/ 1. CREATE TABLE \u0438\u0437 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b<\/p>\n<p>\u00a0 \u00a0 sqlh.Create[User](db)<\/p>\n<p>\u00a0 \u00a0 \/\/ 2. INSERT<\/p>\n<p>\u00a0 \u00a0 sqlh.Insert(db, User{Name: &#171;Alice&#187;, Email: &#171;<a href=\"mailto:alice@example.com\" rel=\"noopener noreferrer nofollow\">alice@example.com<\/a>&#171;, Age: 30})<\/p>\n<p>\u00a0 \u00a0 bobID, _ := sqlh.InsertId(db, User{Name: &#171;Bob&#187;, Email: &#171;<a href=\"mailto:bob@example.com\" rel=\"noopener noreferrer nofollow\">bob@example.com<\/a>&#171;, Age: 25})<\/p>\n<p>\u00a0 \u00a0 \/\/ 3. GET \u043f\u043e ID \u2014 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 *User, \u043d\u0435 interface{}<\/p>\n<p>\u00a0 \u00a0 u, _ := sqlh.Get[User](db, sqlh.Eq(&#171;id&#187;, bobID))<\/p>\n<p>\u00a0 \u00a0 fmt.Println(<a href=\"http:\/\/u.Name\" rel=\"noopener noreferrer nofollow\">u.Name<\/a>) \/\/ &#171;Bob&#187;<\/p>\n<p>\u00a0 \u00a0 \/\/ 4. LIST \u0432\u0441\u0435\u0445 \u2014 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 []User + next offset<\/p>\n<p>\u00a0 \u00a0 users, <em>, <\/em> := sqlh.List[User](db, 0, &#171;&#187;, &#171;name ASC&#187;)<\/p>\n<p>\u00a0 \u00a0 fmt.Println(len(users)) \/\/ 2<\/p>\n<p>\u00a0 \u00a0 \/\/ 5. UPDATE \u2014 \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u043c \u043f\u043e\u043b\u043d\u0443\u044e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0437\u0430\u043d\u0443\u043b\u0438\u0442\u044c \u0434\u0440\u0443\u0433\u0438\u0435 \u043a\u043e\u043b\u043e\u043d\u043a\u0438<\/p>\n<p>\u00a0 \u00a0 sqlh.Update(db, sqlh.UpdateAttr[User]{<\/p>\n<p>\u00a0 \u00a0 \u00a0 \u00a0 Row: \u00a0 \u00a0User{Name: &#171;Alice&#187;, Email: &#171;<a href=\"mailto:alice.new@example.com\" rel=\"noopener noreferrer nofollow\">alice.new@example.com<\/a>&#171;, Age: 31},<\/p>\n<p>\u00a0 \u00a0 \u00a0 \u00a0 Wheres: []sqlh.Where{sqlh.Eq(&#171;id&#187;, 1)},<\/p>\n<p>\u00a0 \u00a0 })<\/p>\n<p>\u00a0 \u00a0 \/\/ 6. DELETE<\/p>\n<p>\u00a0 \u00a0 sqlh.Delete[User](db, sqlh.Eq(&#171;id&#187;, bobID))<\/p>\n<p>}<\/p>\n<p>&#171;`<\/p>\n<p><strong>~50 \u0441\u0442\u0440\u043e\u043a.<\/strong> \u041d\u0438\u043a\u0430\u043a\u043e\u0433\u043e raw SQL. \u041d\u0438 \u043e\u0434\u043d\u043e\u0433\u043e <code>rows.Scan<\/code>. \u041d\u0438 \u043e\u0434\u043d\u043e\u0433\u043e <code>BEGIN\/COMMIT<\/code>. \u041d\u0438 \u043e\u0434\u043d\u043e\u0439 \u043e\u0448\u0438\u0431\u043a\u0438 \u0432 \u043f\u043e\u0440\u044f\u0434\u043a\u0435 \u043a\u043e\u043b\u043e\u043d\u043e\u043a.<\/p>\n<p><strong>### \u0421\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u0435 \u0431\u043e\u043a-\u043e-\u0431\u043e\u043a<\/strong><\/p>\n<p>| \u041e\u043f\u0435\u0440\u0430\u0446\u0438\u044f | Raw <code>database\/sql<\/code> | sqlx | <strong>sqlh<\/strong> |<\/p>\n<p>|&#8212;&#8212;&#8212;-|&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;|&#8212;&#8212;|&#8212;&#8212;&#8212;-|<\/p>\n<p>| CREATE TABLE | Raw SQL-\u0441\u0442\u0440\u043e\u043a\u0430 | Raw SQL-\u0441\u0442\u0440\u043e\u043a\u0430 | <code>sqlh.Create[User](db)<\/code> |<\/p>\n<p>| INSERT | <code>Exec(?,?,?)<\/code> | <code>NamedExec<\/code> | <code>Insert(T)<\/code> |<\/p>\n<p>| GET | <code>QueryRow + Scan<\/code> | <code>Get(&amp;T)<\/code> | <code>Get[T](where)<\/code> |<\/p>\n<p>| LIST | <a href=\"http:\/\/rows.Next\" rel=\"noopener noreferrer nofollow\"><code>rows.Next<\/code><\/a><code> + Scan<\/code> | <code>Select<\/code> | <code>List[T](...)<\/code> |<\/p>\n<p>| UPDATE | <code>Exec(?,?,?,?)<\/code> | <code>NamedExec<\/code> | <code>Update(attr)<\/code> |<\/p>\n<p>| DELETE | <code>Exec(?)<\/code> | <code>Exec(?)<\/code> | <code>Delete[T](where)<\/code> |<\/p>\n<p>| COUNT | <code>QueryRow + Scan<\/code> | <code>Get(&amp;int)<\/code> | <code>Count[T]()<\/code> |<\/p>\n<p>| | \u0421\u0442\u0440\u043e\u043a \u043a\u043e\u0434\u0430 | \u0421\u043e\u043a\u0440\u0430\u0449\u0435\u043d\u0438\u0435 |<\/p>\n<p>|&#8212;|&#8212;|&#8212;|<\/p>\n<p>| Raw <code>database\/sql<\/code> | ~115 | baseline |<\/p>\n<p>| sqlx | ~80 | \u221230% |<\/p>\n<p>| <strong>sqlh<\/strong> | <strong>~50<\/strong> | <strong>\u221257%<\/strong> |<\/p>\n<p><strong>### Table[T]: \u0443\u0434\u043e\u0431\u043d\u044b\u0439 method-based API<\/strong><\/p>\n<p>\u0414\u043b\u044f \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432, \u0433\u0434\u0435 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439 \u043d\u0430\u0434 \u043e\u0434\u043d\u043e\u0439 \u0442\u0430\u0431\u043b\u0438\u0446\u0435\u0439 \u2014 \u043c\u043e\u0436\u043d\u043e \u043e\u0431\u0435\u0440\u043d\u0443\u0442\u044c \u0432 <code>Table[T]<\/code>:<\/p>\n<p>&#171;`go<\/p>\n<p>tbl, _ := sqlh.CreateTable[User](db)<\/p>\n<p>tbl.Insert(User{Name: &#171;Charlie&#187;, Email: &#171;<a href=\"mailto:charlie@example.com\" rel=\"noopener noreferrer nofollow\">charlie@example.com<\/a>&#171;, Age: 28})<\/p>\n<p>c, _ := tbl.Get(sqlh.Eq(&#171;name&#187;, &#171;Charlie&#187;))<\/p>\n<p>fmt.Println(<a href=\"http:\/\/c.Name\" rel=\"noopener noreferrer nofollow\">c.Name<\/a>)<\/p>\n<p>for _, user := range tbl.List(0, &#171;&#187;, &#171;name ASC&#187;, 0) {<\/p>\n<p>\u00a0 \u00a0 fmt.Println(<a href=\"http:\/\/user.Name\" rel=\"noopener noreferrer nofollow\">user.Name<\/a>)<\/p>\n<p>}<\/p>\n<p>&#171;`<\/p>\n<p><code>Table[T]<\/code> \u2014 \u043b\u0451\u0433\u043a\u0438\u0439 wrapper \u043d\u0430\u0434 \u043e\u0431\u0449\u0438\u043c <code>*sql.DB<\/code>. \u041e\u043d <strong>\u043d\u0435 \u0432\u043b\u0430\u0434\u0435\u0435\u0442<\/strong> \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435\u043c, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 <code>Close()<\/code> \u2014 no-op (\u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u0439 \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u043e\u0441\u0442\u0438). \u0420\u0435\u0441\u0443\u0440\u0441\u044b \u043e\u0447\u0438\u0449\u0430\u0435\u0442 \u0432\u044b\u0437\u044b\u0432\u0430\u044e\u0449\u0438\u0439 \u0447\u0435\u0440\u0435\u0437 <code>db.Close()<\/code>.<\/p>\n<p><strong>### Set (upsert): \u043d\u0430\u0442\u0438\u0432\u043d\u044b\u0439 UPSERT<\/strong><\/p>\n<p><code>Set<\/code> \u2014 \u0430\u0442\u043e\u043c\u0430\u0440\u043d\u044b\u0439 upsert. \u0414\u043b\u044f PostgreSQL, SQLite \u0438 MySQL \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u043d\u0430\u0442\u0438\u0432\u043d\u044b\u0439 \u0441\u0438\u043d\u0442\u0430\u043a\u0441\u0438\u0441 \u0431\u0430\u0437\u044b:<\/p>\n<p>&#8212; <strong>PostgreSQL<\/strong>: <code>INSERT ... ON CONFLICT (...) DO UPDATE SET ...<\/code><\/p>\n<p>&#8212; <strong>SQLite<\/strong>: <code>INSERT ... ON CONFLICT (...) DO UPDATE SET ...<\/code><\/p>\n<p>&#8212; <strong>MySQL<\/strong>: <code>INSERT ... ON DUPLICATE KEY UPDATE ...<\/code><\/p>\n<p>\u0414\u043b\u044f \u043d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u044b\u0445 \u0434\u0440\u0430\u0439\u0432\u0435\u0440\u043e\u0432 \u2014 fallback \u043d\u0430 SELECT-then-INSERT\/UPDATE \u0432 \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u0438.<\/p>\n<p>&#171;`go<\/p>\n<p>\/\/ name \u043f\u043e\u043c\u0435\u0447\u0435\u043d db_key:&#187;unique&#187; \u2014 Set \u0441\u0434\u0435\u043b\u0430\u0435\u0442 UPDATE \u043f\u0440\u0438 \u0441\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u0438<\/p>\n<p>err := sqlh.Set(db, User{Name: &#171;Dave&#187;, Email: &#171;<a href=\"mailto:dave@example.com\" rel=\"noopener noreferrer nofollow\">dave@example.com<\/a>&#171;}, sqlh.Eq(&#171;name&#187;, &#171;Dave&#187;))<\/p>\n<p>&#171;`<\/p>\n<p><strong>### ListRange: Go 1.25 iterators<\/strong><\/p>\n<p>\u0412\u043c\u0435\u0441\u0442\u043e <code>List<\/code> \u0441 \u0441\u043b\u0430\u0439\u0441\u043e\u043c \u2014 \u043b\u0435\u043d\u0438\u0432\u044b\u0439 \u0438\u0442\u0435\u0440\u0430\u0442\u043e\u0440 <code>ListRange<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 <code>iter.Seq2[int, T]<\/code>. \u041d\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442 \u0432\u0441\u0451 \u0432 \u043f\u0430\u043c\u044f\u0442\u044c \u2014 \u0438\u0434\u0435\u0430\u043b\u0435\u043d \u0434\u043b\u044f \u0441\u0442\u0440\u0438\u043c\u0438\u043d\u0433\u0430, JOIN \u0438 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u043e\u0432 \u0441 \u0442\u0430\u0439\u043c\u0430\u0443\u0442\u043e\u043c.<\/p>\n<p>&#171;`go<\/p>\n<p>ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)<\/p>\n<p>defer cancel()<\/p>\n<p>var listErr error<\/p>\n<p>for i, user := range sqlh.ListRange[User](db, 0, &#171;&#187;, &#171;name ASC&#187;, 0,<\/p>\n<p>\u00a0 \u00a0 func(err error) { listErr = err },<\/p>\n<p>\u00a0 \u00a0 ctx,<\/p>\n<p>) {<\/p>\n<p>\u00a0 \u00a0 fmt.Printf(&#171;%d: %s\\n&#187;, i, <a href=\"http:\/\/user.Name\" rel=\"noopener noreferrer nofollow\">user.Name<\/a>)<\/p>\n<p>}<\/p>\n<p>&#171;`<\/p>\n<p><strong>### \u0422\u0438\u043f\u043e\u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u044b\u0435 WHERE-\u0445\u0435\u043b\u043f\u0435\u0440\u044b<\/strong><\/p>\n<p>\u0412\u043c\u0435\u0441\u0442\u043e \u0440\u0443\u0447\u043d\u043e\u0433\u043e SQL \u0432 <code>Where.Field<\/code> \u2014 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440\u044b \u0434\u043b\u044f \u0442\u0438\u043f\u043e\u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u044b\u0445 \u0443\u0441\u043b\u043e\u0432\u0438\u0439:<\/p>\n<p>&#171;`go<\/p>\n<p>sqlh.Eq(&#171;name&#187;, &#171;Alice&#187;) \u00a0 \u00a0 \u00a0 \u00a0 \/\/ name = ?<\/p>\n<p><a href=\"http:\/\/sqlh.Ne\" rel=\"noopener noreferrer nofollow\">sqlh.Ne<\/a>(&#171;status&#187;, &#171;deleted&#187;) \u00a0 \u00a0 \/\/ status &lt;&gt; ?<\/p>\n<p><a href=\"http:\/\/sqlh.Gt\" rel=\"noopener noreferrer nofollow\">sqlh.Gt<\/a>(&#171;age&#187;, 18) \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \/\/ age &gt; ?<\/p>\n<p><a href=\"http:\/\/sqlh.Like\" rel=\"noopener noreferrer nofollow\">sqlh.Like<\/a>(&#171;name&#187;, &#171;%Alice%&#187;) \u00a0 \u00a0 \/\/ name LIKE ?<\/p>\n<p><a href=\"http:\/\/sqlh.In\" rel=\"noopener noreferrer nofollow\">sqlh.In<\/a>(&#171;id&#187;, 1, 2, 3) \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \/\/ id IN (?, ?, ?)<\/p>\n<p>sqlh.IsNull(&#171;deleted_at&#187;) \u00a0 \u00a0 \u00a0 \u00a0\/\/ deleted_at IS NULL<\/p>\n<p>&#171;`<\/p>\n<p>\u0417\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u044e\u0442\u0441\u044f \u043a\u0430\u043a bind-\u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b (\u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e). \u041d\u0438\u0437\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u044b\u0439 <code>Where{Field, Value}<\/code> \u043e\u0441\u0442\u0430\u0451\u0442\u0441\u044f \u0434\u043b\u044f \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0445 \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440\u043e\u0432.<\/p>\n<p><strong>### JOIN: composite-\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b<\/strong><\/p>\n<p>&#171;`go<\/p>\n<p>type UserWithOrders struct {<\/p>\n<p>\u00a0 \u00a0 *UserTable \u00a0 \/\/ \u043e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u0442\u0430\u0431\u043b\u0438\u0446\u0430<\/p>\n<p>\u00a0 \u00a0 *OrderTable \u00a0\/\/ JOIN-\u0442\u0430\u0431\u043b\u0438\u0446\u0430<\/p>\n<p>}<\/p>\n<p>join := query.MakeJoin[OrderTable](query.Join{<\/p>\n<p>\u00a0 \u00a0 Join: \u00a0&#171;LEFT&#187;, Alias: &#171;o&#187;, On: &#171;<a href=\"http:\/\/t.id\" rel=\"noopener noreferrer nofollow\">t.id<\/a> = o.user_id&#187;,<\/p>\n<p>})<\/p>\n<p>for _, row := range sqlh.ListRange[UserWithOrders](db, 0, &#171;&#187;, &#171;<a href=\"http:\/\/t.name\" rel=\"noopener noreferrer nofollow\">t.name<\/a> ASC&#187;, 0,<\/p>\n<p>\u00a0 \u00a0 sqlh.SetAlias(&#171;t&#187;), join, func(err error) { log.Fatal(err) },<\/p>\n<p>) {<\/p>\n<p>\u00a0 \u00a0 if row.OrderTable != nil {<\/p>\n<p>\u00a0 \u00a0 \u00a0 \u00a0 fmt.Println(<a href=\"http:\/\/row.UserTable.Name\" rel=\"noopener noreferrer nofollow\">row.UserTable.Name<\/a>, <a href=\"http:\/\/row.OrderTable.Total\" rel=\"noopener noreferrer nofollow\">row.OrderTable.Total<\/a>)<\/p>\n<p>\u00a0 \u00a0 }<\/p>\n<p>}<\/p>\n<p>&#171;`<\/p>\n<p><strong>## \u00a75. \u0411\u0435\u043d\u0447\u043c\u0430\u0440\u043a\u0438: \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0432 \u0446\u0438\u0444\u0440\u0430\u0445<\/strong><\/p>\n<p>\u041d\u0430\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0431\u044b\u0441\u0442\u0440 sqlh \u043d\u0430 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0435? \u0412 \u043c\u043e\u0434\u0443\u043b\u0435 <code>bench\/<\/code> \u2014 \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u043c\u044b\u0435 Go-\u0431\u0435\u043d\u0447\u043c\u0430\u0440\u043a\u0438 \u0441\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u044e\u0442 raw <code>database\/sql<\/code>, <code>sqlx<\/code>, GORM \u0438 sqlh \u043d\u0430 \u043e\u0434\u043d\u043e\u043c \u0438 \u0442\u043e\u043c \u0436\u0435 CRUD-\u0432\u043e\u0440\u043a\u043b\u043e\u0430\u0434\u0435. \u0412\u0441\u0435 \u0442\u0435\u0441\u0442\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 in-memory SQLite \u2014 \u043d\u0438\u043a\u0430\u043a\u043e\u0439 \u0432\u043d\u0435\u0448\u043d\u0435\u0439 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438.<\/p>\n<p>\u0412\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0441\u0442\u0438 \u043d\u0430 \u0441\u0432\u043e\u0435\u0439 \u043c\u0430\u0448\u0438\u043d\u0435:<\/p>\n<p>&#171;`bash<\/p>\n<p>cd bench &amp;&amp; go test -bench=. -benchmem -benchtime=1s<\/p>\n<p>&#171;`<\/p>\n<p><strong>### CRUD Throughput (ops\/sec)<\/strong><\/p>\n<p>| \u041e\u043f\u0435\u0440\u0430\u0446\u0438\u044f | raw sql | sqlx | GORM | <strong>sqlh<\/strong> |<\/p>\n<p>|&#8212;&#8212;&#8212;-|&#8212;&#8212;&#8212;|&#8212;&#8212;|&#8212;&#8212;|&#8212;&#8212;&#8212;-|<\/p>\n<p>| <strong>Insert<\/strong> | 158,041 | 131,596 | 34,971 | <strong>87,085<\/strong> |<\/p>\n<p>| <strong>Get by PK<\/strong> | 169,232 | 152,415 | 78,666 | <strong>68,675<\/strong> |<\/p>\n<p>| <strong>List all<\/strong> | 11,807 | 9,261 | 6,779 | <strong>7,573<\/strong> |<\/p>\n<p>| <strong>List limit 10<\/strong> | 51,500 | 43,691 | 37,821 | <strong>44,142<\/strong> |<\/p>\n<p>| <strong>Update<\/strong> | 228,728 | 180,505 | 65,933 | <strong>85,543<\/strong> |<\/p>\n<p>| <strong>Delete<\/strong> | 172,128 | 166,279 | 41,162 | <strong>60,650<\/strong> |<\/p>\n<p><strong>### Memory Allocations (bytes\/op, allocs\/op)<\/strong><\/p>\n<p>| \u041e\u043f\u0435\u0440\u0430\u0446\u0438\u044f | raw sql | sqlx | GORM | <strong>sqlh<\/strong> |<\/p>\n<p>|&#8212;&#8212;&#8212;-|&#8212;&#8212;&#8212;|&#8212;&#8212;|&#8212;&#8212;|&#8212;&#8212;&#8212;-|<\/p>\n<p>| <strong>Insert<\/strong> | 328 B, 12 | 721 B, 20 | 5,534 B, 82 | <strong>1,274 B, 39<\/strong> |<\/p>\n<p>| <strong>Get by PK<\/strong> | 792 B, 27 | 976 B, 31 | 3,952 B, 66 | <strong>2,592 B, 78<\/strong> |<\/p>\n<p>| <strong>List all<\/strong> | 23,744 B, 528 | 26,376 B, 632 | 27,668 B, 946 | <strong>26,391 B, 745<\/strong> |<\/p>\n<p>| <strong>List limit<\/strong> | 3,120 B, 76 | 3,624 B, 91 | 6,145 B, 141 | <strong>3,958 B, 115<\/strong> |<\/p>\n<p>| <strong>Update<\/strong> | 296 B, 9 | 680 B, 19 | 5,079 B, 68 | <strong>1,393 B, 43<\/strong> |<\/p>\n<p>| <strong>Delete<\/strong> | 216 B, 7 | 216 B, 7 | 5,484 B, 67 | <strong>1,136 B, 37<\/strong> |<\/p>\n<p><strong>### \u0427\u0442\u043e \u0433\u043e\u0432\u043e\u0440\u044f\u0442 \u0446\u0438\u0444\u0440\u044b<\/strong><\/p>\n<p>&#8212; <strong>GORM<\/strong> \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u043d\u0430\u0438\u0431\u043e\u043b\u044c\u0448\u0443\u044e latency \u0438 \u0441\u0430\u043c\u044b\u0439 \u0442\u044f\u0436\u0451\u043b\u044b\u0439 allocation footprint \u2014 \u0441\u043b\u0435\u0434\u0441\u0442\u0432\u0438\u0435 \u0431\u043e\u0433\u0430\u0442\u043e\u0433\u043e feature set \u0438 reflection-\u043e\u0432\u0435\u0440\u0445\u0435\u0434\u0430.<\/p>\n<p>&#8212; <strong>sqlh<\/strong> \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u043c\u0435\u0436\u0434\u0443 raw\/sqlx \u0438 GORM. \u0423\u043c\u0435\u0440\u0435\u043d\u043d\u044b\u0439 \u043e\u0432\u0435\u0440\u0445\u0435\u0434 \u2014 \u043f\u043b\u0430\u0442\u0430 \u0437\u0430 \u0430\u0432\u0442\u043e-\u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044e SQL, \u043f\u0430\u0440\u0441\u0438\u043d\u0433 \u0442\u0435\u0433\u043e\u0432 \u0438 \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0435 \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u0438 \u043d\u0430 \u0437\u0430\u043f\u0438\u0441\u044c.<\/p>\n<p>&#8212; <strong>sqlh \u0442\u043e\u0440\u0433\u0443\u0435\u0442 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c\u044e \u043d\u0430 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0441\u0442\u044c<\/strong>: \u043a\u0430\u0436\u0434\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c \u0430\u0442\u043e\u043c\u0430\u0440\u043d\u0430 (auto-transact \u0441 rollback), \u0447\u0442\u043e \u0443\u0441\u0442\u0440\u0430\u043d\u044f\u0435\u0442 \u0446\u0435\u043b\u044b\u0439 \u043a\u043b\u0430\u0441\u0441 \u0431\u0430\u0433\u043e\u0432 \u0446\u0435\u043d\u043e\u0439 \u043e\u0432\u0435\u0440\u0445\u0435\u0434\u0430 ~2\u20136x vs raw SQL \u0434\u043b\u044f \u043e\u0434\u043d\u043e\u0441\u0442\u0440\u043e\u0447\u043d\u044b\u0445 \u043c\u0443\u0442\u0430\u0446\u0438\u0439.<\/p>\n<p>&#8212; <strong>ListAll<\/strong> \u0434\u043e\u043c\u0438\u043d\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c 100 \u0441\u0442\u0440\u043e\u043a. \u0412\u0441\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u0437\u0434\u0435\u0441\u044c \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u044e\u0442 \u0441\u0445\u043e\u0436\u0443\u044e \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c.<\/p>\n<p>&gt; <strong>\u041e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0435:<\/strong> Linux AMD Ryzen 9 3900, Go 1.26.3, SQLite in-memory.<\/p>\n<p>&gt; \u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 <code>cd bench &amp;&amp; go test -bench=. -benchmem -benchtime=1s<\/code> \u043d\u0430 \u0441\u0432\u043e\u0451\u043c \u0436\u0435\u043b\u0435\u0437\u0435 \u0434\u043b\u044f \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u044f.<\/p>\n<p><strong>## \u00a76. \u041a\u043e\u0433\u0434\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c sqlh<\/strong><\/p>\n<p>sqlh \u2014 \u043d\u0435 \u0441\u0435\u0440\u0435\u0431\u0440\u044f\u043d\u0430\u044f \u043f\u0443\u043b\u044f. \u0412\u043e\u0442 \u0433\u0434\u0435 \u043e\u043d \u0441\u0438\u044f\u0435\u0442, \u0430 \u0433\u0434\u0435 \u043b\u0443\u0447\u0448\u0435 \u0447\u0442\u043e-\u0442\u043e \u0434\u0440\u0443\u0433\u043e\u0435:<\/p>\n<p>| \u0421\u0446\u0435\u043d\u0430\u0440\u0438\u0439 | \u0420\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0430\u0446\u0438\u044f |<\/p>\n<p>|&#8212;&#8212;&#8212;-|&#8212;&#8212;&#8212;&#8212;&#8212;|<\/p>\n<p>| CLI-\u0443\u0442\u0438\u043b\u0438\u0442\u044b | \u2705 \u0418\u0434\u0435\u0430\u043b\u044c\u043d\u043e \u2014 \u043d\u043e\u043b\u044c \u0444\u0430\u0439\u043b\u043e\u0432 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0439, \u043e\u0434\u0438\u043d \u0431\u0438\u043d\u0430\u0440\u043d\u0438\u043a |<\/p>\n<p>| \u0421\u0442\u0430\u0440\u0442\u0430\u043f\u044b \u0438 MVP | \u2705 \u0411\u044b\u0441\u0442\u0440\u0435\u0435 \u043f\u0438\u0448\u0435\u0442\u0435, \u043f\u043e\u0442\u043e\u043c \u0440\u0435\u0444\u0430\u043a\u0442\u043e\u0440\u0438\u0442\u0435 |<\/p>\n<p>| \u041c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u044b \u0441 \u043f\u0440\u043e\u0441\u0442\u044b\u043c\u0438 \u0441\u0445\u0435\u043c\u0430\u043c\u0438 | \u2705 \u041d\u0438\u0437\u043a\u0438\u0439 \u043e\u0432\u0435\u0440\u0445\u0435\u0434, \u0442\u0438\u043f\u043e\u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u044c |<\/p>\n<p>| High-throughput OLTP (&gt;100K writes\/sec) | \u26a0\ufe0f \u0422\u0435\u0441\u0442\u0438\u0440\u0443\u0439\u0442\u0435 \u2014 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e, raw SQL |<\/p>\n<p>| \u0421\u043b\u043e\u0436\u043d\u0430\u044f \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u043a\u0430 | \u26a0\ufe0f \u041f\u0440\u0435\u0434\u043f\u043e\u0447\u0442\u0438\u0442\u0435\u043b\u044c\u043d\u043e raw SQL \u0438\u043b\u0438 query builder |<\/p>\n<p>| \u0411\u043e\u043b\u044c\u0448\u0438\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0441 DBA | \u26a0\ufe0f GORM \u0438\u043b\u0438 sqlx \u043c\u043e\u0433\u0443\u0442 \u043f\u043e\u0434\u043e\u0439\u0442\u0438 \u043b\u0443\u0447\u0448\u0435 |<\/p>\n<p>| \u041e\u0431\u0443\u0447\u0435\u043d\u0438\u0435 Go + SQL | \u2705 \u041e\u0442\u043b\u0438\u0447\u043d\u044b\u0439 \u0443\u0447\u0435\u0431\u043d\u044b\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u2014 \u043d\u0438\u0437\u043a\u0430\u044f \u043a\u043e\u0433\u043d\u0438\u0442\u0438\u0432\u043d\u0430\u044f \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0430 |<\/p>\n<p><strong>## \u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/strong><\/p>\n<p>sqlh \u0430\u043a\u0442\u0438\u0432\u043d\u043e \u0440\u0430\u0437\u0432\u0438\u0432\u0430\u0435\u0442\u0441\u044f. \u041d\u0430 \u043c\u043e\u043c\u0435\u043d\u0442 v0.8.0 (\u0438\u044e\u043d\u044c 2026) \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442:<\/p>\n<p>&#8212; \u2705 \u041f\u043e\u043b\u043d\u044b\u0439 CRUD \u0441 \u0430\u0432\u0442\u043e-\u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u044f\u043c\u0438<\/p>\n<p>&#8212; \u2705 \u041d\u0430\u0442\u0438\u0432\u043d\u044b\u0439 UPSERT (PostgreSQL, SQLite, MySQL)<\/p>\n<p>&#8212; \u2705 JOIN-\u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0441\u043e \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u0432 composite-\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b<\/p>\n<p>&#8212; \u2705 Go 1.25 iterators (<code>ListRange<\/code>) \u0434\u043b\u044f \u043b\u0435\u043d\u0438\u0432\u043e\u0433\u043e \u0441\u0442\u0440\u0438\u043c\u0438\u043d\u0433\u0430<\/p>\n<p>&#8212; \u2705 \u0422\u0438\u043f\u043e\u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u044b\u0435 WHERE-\u0445\u0435\u043b\u043f\u0435\u0440\u044b (<code>Eq<\/code>, <code>Ne<\/code>, <code>Gt<\/code>, <code>Like<\/code>, <code>In<\/code> \u0438 \u0434\u0440.)<\/p>\n<p>&#8212; \u2705 \u0420\u0435\u0442\u0440\u0430\u0439 \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u043e\u043a \u0434\u043b\u044f SQLite<\/p>\n<p>&#8212; \u2705 \u041c\u0443\u043b\u044c\u0442\u0438-\u0411\u0414 (SQLite, MySQL, PostgreSQL)<\/p>\n<p>\u0412 \u043f\u043b\u0430\u043d\u0430\u0445: \u0430\u0433\u0440\u0435\u0433\u0430\u0442\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 (<code>SUM<\/code>, <code>AVG<\/code>), \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u0441\u0445\u0435\u043c\u044b, batch-\u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438. API \u0441\u0442\u0430\u0431\u0438\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u043a v1.0.0.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0432\u044b \u0441\u0442\u0440\u043e\u0438\u0442\u0435 Go-\u043f\u0440\u043e\u0435\u043a\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0431\u0449\u0430\u0435\u0442\u0441\u044f \u0441 SQL, \u0438 \u0443\u0441\u0442\u0430\u043b\u0438 \u043f\u0438\u0441\u0430\u0442\u044c \u043e\u0434\u0438\u043d \u0438 \u0442\u043e\u0442 \u0436\u0435 boilerplate \u0441\u043d\u043e\u0432\u0430 \u0438 \u0441\u043d\u043e\u0432\u0430 \u2014 \u0434\u0430\u0439\u0442\u0435 sqlh \u0448\u0430\u043d\u0441. \u041e\u043f\u0438\u0448\u0438\u0442\u0435 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443. \u042d\u0442\u043e \u0432\u0441\u0451.<\/p>\n<p>&#171;`bash<\/p>\n<p>go get github.com\/kirill-scherba\/sqlh<\/p>\n<p>&#171;`<\/p>\n<p>&#8212; \ud83d\udcd6 [README &amp; Quick Start](<a href=\"https:\/\/github.com\/kirill-scherba\/sqlh\" rel=\"noopener noreferrer nofollow\">https:\/\/github.com\/kirill-scherba\/sqlh<\/a>)<\/p>\n<p>&#8212; \ud83d\udce6 [<a href=\"http:\/\/pkg.go.dev\" rel=\"noopener noreferrer nofollow\">pkg.go.dev<\/a> reference](<a href=\"https:\/\/pkg.go.dev\/github.com\/kirill-scherba\/sqlh\" rel=\"noopener noreferrer nofollow\">https:\/\/pkg.go.dev\/github.com\/kirill-scherba\/sqlh<\/a>)<\/p>\n<p>&#8212; \ud83c\udfd7\ufe0f [\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434](<a href=\"https:\/\/github.com\/kirill-scherba\/sqlh\" rel=\"noopener noreferrer nofollow\">https:\/\/github.com\/kirill-scherba\/sqlh<\/a>)<\/p>\n<p>&#8212; \u2b50 [Awesome Go PR](<a href=\"https:\/\/github.com\/avelino\/awesome-go\/pull\/6401\" rel=\"noopener noreferrer nofollow\">https:\/\/github.com\/avelino\/awesome-go\/pull\/6401<\/a>)<\/p>\n<p>&#8212;<\/p>\n<p><em>\u0410\u0432\u0442\u043e\u0440: [Kirill Scherba](<\/em><a href=\"https:\/\/github.com\/kirill-scherba\" rel=\"noopener noreferrer nofollow\"><em>https:\/\/github.com\/kirill-scherba<\/em><\/a><em>). sqlh \u2014 open source \u043f\u043e\u0434 BSD-\u043b\u0438\u0446\u0435\u043d\u0437\u0438\u0435\u0439. Contributions welcome.<\/em><\/p>\n<\/div>\n<p>\u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/1046836\/\">https:\/\/habr.com\/ru\/articles\/1046836\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>&gt; Zero-boilerplate SQL \u0434\u043b\u044f Go. \u041e\u043f\u0438\u0448\u0438 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0442\u0435\u0433\u0430\u043c\u0438 \u2014 \u0438 \u044d\u0442\u043e \u0432\u0441\u0451.\u0415\u0441\u043b\u0438 \u0432\u044b \u043f\u0438\u0448\u0435\u0442\u0435 \u043d\u0430 Go \u0438 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442\u0435 \u0441 SQL-\u0431\u0430\u0437\u0430\u043c\u0438, \u0432\u044b \u0437\u043d\u0430\u0435\u0442\u0435 \u044d\u0442\u0443 \u0431\u043e\u043b\u044c. \u041a\u0430\u0436\u0434\u044b\u0439 CRUD-\u0437\u0430\u043f\u0440\u043e\u0441 \u2014 \u0440\u0443\u0447\u043d\u043e\u0439 SQL-\u0441\u0442\u0440\u043e\u043a\u0430, rows.Scan \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043f\u043e\u043b\u044f, Begin\/Commit\/Rollback \u0432\u043e\u043a\u0440\u0443\u0433 \u0437\u0430\u043f\u0438\u0441\u0438, \u0438 \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u0430\u044f \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u044f DDL-\u0441\u0445\u0435\u043c\u044b \u0441 \u043a\u043e\u0434\u043e\u043c. \u0428\u0430\u0431\u043b\u043e\u043d\u043d\u044b\u0439 \u043a\u043e\u0434 \u043d\u0435 \u0437\u0430\u043a\u0430\u043d\u0447\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u043d\u0438\u043a\u043e\u0433\u0434\u0430.\u042d\u0442\u043e \u0440\u0430\u0441\u0441\u043a\u0430\u0437 \u043e sqlh \u2014 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0435, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0443\u0431\u0438\u0440\u0430\u0435\u0442 \u0432\u0441\u0451 \u044d\u0442\u043e, \u043e\u0441\u0442\u0430\u0432\u0430\u044f\u0441\u044c \u0432 \u00ab\u0437\u043e\u043b\u043e\u0442\u043e\u0439 \u0441\u0435\u0440\u0435\u0434\u0438\u043d\u0435\u00bb \u043c\u0435\u0436\u0434\u0443 raw SQL (\u0441\u043b\u0438\u0448\u043a\u043e\u043c \u043c\u043d\u043e\u0433\u043e \u0440\u0430\u0431\u043e\u0442\u044b) \u0438 \u0442\u044f\u0436\u0451\u043b\u044b\u043c\u0438 ORM (\u0441\u043b\u0438\u0448\u043a\u043e\u043c \u043c\u043d\u043e\u0433\u043e \u043c\u0430\u0433\u0438\u0438).## \u00a71. \u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430: Go + SQL = \u0441\u043c\u0435\u0440\u0442\u044c \u043e\u0442 \u0442\u044b\u0441\u044f\u0447\u0438 rows.Scan\u0421\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 database\/sql \u0432 Go \u043e\u0442\u043b\u0438\u0447\u0435\u043d. \u041e\u043d \u0434\u0430\u0451\u0442 \u043f\u0440\u043e\u0447\u043d\u044b\u0439, \u043f\u0435\u0440\u0435\u043d\u043e\u0441\u0438\u043c\u044b\u0439 \u0444\u0443\u043d\u0434\u0430\u043c\u0435\u043d\u0442 \u0434\u043b\u044f \u043b\u044e\u0431\u043e\u0439 SQL-\u0431\u0430\u0437\u044b. \u041d\u043e \u043e\u043d \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u043d\u043e \u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0442\u044f\u0436\u0451\u043b\u0443\u044e \u0440\u0430\u0431\u043e\u0442\u0443 \u0437\u0430 \u0432\u0430\u043c\u0438.\u0412\u043e\u0442 \u043a\u0430\u043a \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u043f\u0440\u043e\u0441\u0442\u043e\u0439 CRUD \u043d\u0430 \u0447\u0438\u0441\u0442\u043e\u043c database\/sql:&#171;`go\/\/ 1. CREATE TABLE \u2014 raw DDL-\u0441\u0442\u0440\u043e\u043a\u0430_, err := db.Exec(`CREATE TABLE IF NOT EXISTS user (\u00a0 \u00a0 id INTEGER PRIMARY KEY AUTOINCREMENT,\u00a0 \u00a0 name TEXT UNIQUE,\u00a0 \u00a0 email TEXT,\u00a0 \u00a0 age INTEGER)`)\/\/ 2. INSERT \u2014 \u044f\u0432\u043d\u044b\u0435 placeholder \u0438 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b_, err = db.Exec(\u00a0 \u00a0 &#171;INSERT INTO user (name, email, age) VALUES (?, ?, ?)&#187;,\u00a0 \u00a0 &#171;Alice&#187;, &#171;alice@example.com&#187;, 30,)\/\/ 3. GET \u043f\u043e ID \u2014 QueryRow + \u0440\u0443\u0447\u043d\u043e\u0439 Scanvar u Usererr = db.QueryRow(&#171;SELECT id, name, email, age FROM user WHERE id = ?&#187;, 1).\u00a0 \u00a0 Scan(&amp;u.ID, &amp;u.Name, &amp;u.Email, &amp;u.Age)\/\/ 4. LIST \u0432\u0441\u0435\u0445 \u2014 Query + rows.Next + rows.Scan \u0432 \u0446\u0438\u043a\u043b\u0435rows, err := db.Query(&#171;SELECT id, name, email, age FROM user ORDER BY name ASC&#187;)var users []Userfor rows.Next() {\u00a0 \u00a0 var u User\u00a0 \u00a0 if err := rows.Scan(&amp;u.ID, &amp;u.Name, &amp;u.Email, &amp;u.Age); err != nil {\u00a0 \u00a0 \u00a0 \u00a0 log.Fatal(err)\u00a0 \u00a0 }\u00a0 \u00a0 users = append(users, u)}rows.Close()\/\/ 5. UPDATE \u2014 raw SQL \u0441 placeholder_, err = db.Exec(\u00a0 \u00a0 &#171;UPDATE user SET email = ?, age = ? WHERE id = ?&#187;,\u00a0 \u00a0 &#171;alice.new@example.com&#187;, 31, 1,)\/\/ 6. DELETE \u2014 raw SQL_, err = db.Exec(&#171;DELETE FROM user WHERE id = ?&#187;, 1)&#171;`\u042d\u0442\u043e ~115 \u0441\u0442\u0440\u043e\u043a \u043a\u043e\u0434\u0430 \u0434\u043b\u044f \u0448\u0435\u0441\u0442\u0438 \u0431\u0430\u0437\u043e\u0432\u044b\u0445 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439. \u0418 \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437, \u043a\u043e\u0433\u0434\u0430 \u0432\u044b \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442\u0435 \u0441\u0442\u043e\u043b\u0431\u0435\u0446, \u043d\u0443\u0436\u043d\u043e \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u0441\u0442\u0440\u043e\u043a\u0443 CREATE TABLE, \u0441\u043f\u0438\u0441\u043e\u043a \u043a\u043e\u043b\u043e\u043d\u043e\u043a \u0432 INSERT, \u0441\u043f\u0438\u0441\u043e\u043a \u0432 SELECT, \u0438 \u0432\u044b\u0437\u043e\u0432 rows.Scan. \u041e\u043f\u0435\u0447\u0430\u0442\u043a\u0430 \u0432 \u043b\u044e\u0431\u043e\u043c \u043c\u0435\u0441\u0442\u0435 \u2014 runtime-\u043e\u0448\u0438\u0431\u043a\u0430, compile-time \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438 \u043d\u0435\u0442.| \u0411\u043e\u043b\u044c | \u041f\u043e\u0447\u0435\u043c\u0443 \u0431\u043e\u043b\u044c\u043d\u043e ||&#8212;&#8212;|&#8212;&#8212;&#8212;&#8212;&#8212;|| \u0420\u0443\u0447\u043d\u043e\u0439 SQL | \u041a\u0430\u0436\u0434\u044b\u0439 CRUD \u2014 raw SQL-\u0441\u0442\u0440\u043e\u043a\u0430, \u043d\u0435\u0442 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043d\u0430 \u044d\u0442\u0430\u043f\u0435 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0446\u0438\u0438 || rows.Scan | 4\u20135 \u0441\u0442\u0440\u043e\u043a \u043d\u0430 \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u043c\u0430\u043f\u043f\u0438\u043d\u0433\u0430 \u043a\u043e\u043b\u043e\u043d\u043e\u043a \u043d\u0430 \u043f\u043e\u043b\u044f || \u0422\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u0438 | db.Begin() + defer tx.Rollback() + tx.Commit() \u2014 \u0432\u0435\u0437\u0434\u0435 || \u041d\u0435\u0442 \u0441\u0432\u044f\u0437\u0438 \u0441\u043e \u0441\u0445\u0435\u043c\u043e\u0439 | DDL \u0432 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u044f\u0445, \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u0432 Go \u2014 \u043e\u043d\u0438 \u0440\u0430\u0441\u0445\u043e\u0434\u044f\u0442\u0441\u044f || \u041f\u043e\u0440\u044f\u0434\u043e\u043a \u043a\u043e\u043b\u043e\u043d\u043e\u043a | \u041d\u043e\u0432\u044b\u0439 \u0441\u0442\u043e\u043b\u0431\u0435\u0446 \u2192 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c SQL-\u0441\u0442\u0440\u043e\u043a\u0438 \u0438 Scan-\u0432\u044b\u0437\u043e\u0432\u044b |## \u00a72. \u0421\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u044f: sqlx \u0438 GORM\u0412 \u044d\u043a\u043e\u0441\u0438\u0441\u0442\u0435\u043c\u0435 Go \u0435\u0441\u0442\u044c \u0434\u0432\u0430 \u0438\u0437\u0432\u0435\u0441\u0442\u043d\u044b\u0445 \u043f\u0443\u0442\u0438. \u0423 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0441\u0432\u043e\u0438 \u043a\u043e\u043c\u043f\u0440\u043e\u043c\u0438\u0441\u0441\u044b.### sqlx: \u043b\u0443\u0447\u0448\u0435, \u043d\u043e \u0432\u0441\u0451 \u0435\u0449\u0451 \u0440\u0443\u0447\u043d\u043e\u0439 SQL[sqlx](https:\/\/github.com\/jmoiron\/sqlx) \u2014 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u043e\u0435 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 database\/sql. \u041e\u043d \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 StructScan, Get, Select, \u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b. SQL \u043f\u0438\u0448\u0435\u0442\u0435 \u043f\u043e-\u043f\u0440\u0435\u0436\u043d\u0435\u043c\u0443 \u0440\u0443\u043a\u0430\u043c\u0438, \u043d\u043e rows.Scan \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d.&#171;`go\/\/ sqlx: \u0432\u0441\u0451 \u0435\u0449\u0451 \u0440\u0443\u0447\u043d\u043e\u0439 SQL, \u043d\u043e StructScan \u0443\u0431\u0438\u0440\u0430\u0435\u0442 Scanvar u Userdbx.Get(&amp;u, &#171;SELECT id, name, email, age FROM user WHERE id = ?&#187;, 1)&#171;`sqlx \u0441\u044d\u043a\u043e\u043d\u043e\u043c\u0438\u0442 \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e 30% boilerplate (\u0434\u043e ~80 \u0441\u0442\u0440\u043e\u043a). \u041d\u043e CREATE TABLE, INSERT, SELECT, UPDATE, DELETE \u2014 \u0432\u0441\u0451 \u0435\u0449\u0451 \u043f\u0438\u0448\u0435\u0442\u0435 \u0432\u0440\u0443\u0447\u043d\u0443\u044e. \u0413\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f SQL \u2014 \u043d\u0435 \u0435\u0433\u043e \u0437\u0430\u0434\u0430\u0447\u0430.### GORM: \u043f\u043e\u043b\u043d\u044b\u0439 ORM, \u043f\u043e\u043b\u043d\u0430\u044f \u043c\u0430\u0433\u0438\u044f[GORM](https:\/\/gorm.io\/) \u2014 \u0442\u044f\u0436\u0435\u043b\u043e\u0432\u0435\u0441. \u0413\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442 \u0432\u0441\u0451 \u2014 \u0441\u0445\u0435\u043c\u0443, \u0437\u0430\u043f\u0440\u043e\u0441\u044b, \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u2014 \u0438 \u0434\u0430\u0451\u0442 \u0431\u043e\u0433\u0430\u0442\u044b\u0439 chainable API. \u041d\u043e \u0446\u0435\u043d\u0430 \u0432\u044b\u0441\u043e\u043a\u0430:- \u0422\u044f\u0436\u0451\u043b\u044b\u0439 reflection \u0432 runtime- \u041a\u0440\u0443\u0442\u0430\u044f \u043a\u0440\u0438\u0432\u0430\u044f \u043e\u0431\u0443\u0447\u0435\u043d\u0438\u044f \u2014 \u0442\u0435\u0433\u0438, \u0445\u0443\u043a\u0438, scopes, \u0430\u0441\u0441\u043e\u0446\u0438\u0430\u0446\u0438\u0438- ~4 MB \u0443\u0432\u0435\u043b\u0438\u0447\u0435\u043d\u0438\u0435 \u0431\u0438\u043d\u0430\u0440\u043d\u0438\u043a\u0430 \u0442\u043e\u043b\u044c\u043a\u043e \u0437\u0430 ORM- \u041c\u0430\u0433\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0441\u043a\u0440\u044b\u0432\u0430\u0435\u0442 \u0441\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u2014 \u043f\u043e\u043a\u0430 \u043d\u0435 \u0441\u043b\u043e\u043c\u0430\u0435\u0442\u0441\u044f, \u0438 \u0432\u044b \u0447\u0430\u0441\u0430\u043c\u0438 \u0434\u0435\u0431\u0430\u0436\u0438\u0442\u0435\u0414\u043b\u044f \u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u043a\u043e\u043c\u0430\u043d\u0434 \u0441 \u0432\u044b\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u043c\u0438 DBA \u0438 \u0441\u043b\u043e\u0436\u043d\u044b\u043c\u0438 \u043c\u043e\u0434\u0435\u043b\u044f\u043c\u0438 GORM \u2014 solid choice. \u0414\u043b\u044f CLI-\u0443\u0442\u0438\u043b\u0438\u0442, \u0441\u0442\u0430\u0440\u0442\u0430\u043f\u043e\u0432 \u0438 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u2014 overkill.### sqlh: \u0437\u043e\u043b\u043e\u0442\u0430\u044f \u0441\u0435\u0440\u0435\u0434\u0438\u043d\u0430| \u0424\u0438\u0447\u0430 | database\/sql | sqlx | GORM | sqlh ||&#8212;|&#8212;|&#8212;|&#8212;|&#8212;|| SQL-\u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f | \u274c \u0420\u0443\u0447\u043d\u0430\u044f | \u274c \u0420\u0443\u0447\u043d\u0430\u044f | \u2705 \u041f\u043e\u043b\u043d\u0430\u044f | \u2705 \u041f\u043e\u043b\u043d\u0430\u044f || rows.Scan | \u2705 \u041d\u0443\u0436\u0435\u043d | \u274c StructScan | \u274c \u0410\u0432\u0442\u043e | \u274c \u0410\u0432\u0442\u043e || \u0422\u0438\u043f\u043e\u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u044c (generics) | \u274c | \u274c | \u274c | \u2705 || \u0410\u0432\u0442\u043e-\u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u0438 | \u274c | \u274c | \u2705 | \u2705 || \u0420\u0435\u0442\u0440\u0430\u0439 \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u043e\u043a | \u274c | \u274c | \u274c | \u2705 || \u041a\u0440\u0438\u0432\u0430\u044f \u043e\u0431\u0443\u0447\u0435\u043d\u0438\u044f | \u0421\u0440\u0435\u0434\u043d\u044f\u044f | \u0421\u0440\u0435\u0434\u043d\u044f\u044f | \u0412\u044b\u0441\u043e\u043a\u0430\u044f | \u041d\u0438\u0437\u043a\u0430\u044f || \u041e\u0432\u0435\u0440\u0445\u0435\u0434 \u0431\u0438\u043d\u0430\u0440\u043d\u0438\u043a\u0430 | 0 | ~200 KB | ~4 MB | ~200 KB |sqlh \u0436\u0438\u0432\u0451\u0442 \u043c\u0435\u0436\u0434\u0443 sqlx \u0438 GORM:- Zero-boilerplate CRUD \u2014 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0435 \u0442\u0435\u0433\u0438 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u044e\u0442 \u0432\u0435\u0441\u044c SQL- \u0422\u0438\u043f\u043e\u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u044c \u0447\u0435\u0440\u0435\u0437 Go generics \u2014 Get[User]() \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 *User, \u043d\u0435 interface{}- \u041d\u0438\u043a\u0430\u043a\u043e\u0439 \u043c\u0430\u0433\u0438\u0438 \u2014 \u0447\u0442\u043e \u0432\u0438\u0434\u0438\u0442\u0435 \u0432 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0435, \u0442\u043e \u0438 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0435 \u0432 \u0431\u0430\u0437\u0435- \u041b\u0451\u0433\u043a\u0438\u0439 \u2014 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u0439 reflection, \u043a\u0435\u0448 \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0445, \u043d\u0438\u043a\u0430\u043a\u043e\u0439 \u0441\u043a\u0440\u044b\u0442\u043e\u0439 \u0441\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u0438## \u00a73. \u041a\u0430\u043a sqlh \u0440\u0435\u0448\u0430\u0435\u0442 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443: \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0435 \u0442\u0435\u0433\u0438 \u043a\u0430\u043a \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a \u043f\u0440\u0430\u0432\u0434\u044b\u0418\u0434\u0435\u044f \u043f\u0440\u043e\u0441\u0442\u0430: \u0432\u0430\u0448\u0430 Go-\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u2014 \u044d\u0442\u043e \u0432\u0430\u0448\u0430 \u0441\u0445\u0435\u043c\u0430.&#171;`gotype User struct {\u00a0 \u00a0 ID \u00a0 \u00a0int64 \u00a0db:&#187;id&#187; db_key:&#187;not null primary key autoincrement&#187;\u00a0 \u00a0 Name \u00a0string db:&#187;name&#187; db_key:&#187;unique&#187;\u00a0 \u00a0 Email string db:&#187;email&#187;\u00a0 \u00a0 Age \u00a0 int \u00a0 \u00a0db:&#187;age&#187;}&#171;`\u0422\u0440\u0438 \u0442\u0435\u0433\u0430 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u044e\u0442 \u0432\u0441\u0435\u043c:| \u0422\u0435\u0433 | \u041d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 | \u041f\u0440\u0438\u043c\u0435\u0440 ||&#8212;&#8212;|&#8212;&#8212;&#8212;&#8212;|&#8212;&#8212;&#8212;|| db | \u0418\u043c\u044f \u043a\u043e\u043b\u043e\u043d\u043a\u0438 | db:&#187;user_name&#187; || db_key | \u041e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f, \u0438\u043d\u0434\u0435\u043a\u0441\u044b | db_key:&#187;primary key autoincrement&#187; || db_type | \u041f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0442\u0438\u043f\u0430 SQL | db_type:&#187;TEXT&#187; |\u0418\u0437 \u044d\u0442\u043e\u0433\u043e \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0433\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f sqlh \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442:- CREATE TABLE \u2014 sqlh.Create[User](db) \u2192 CREATE TABLE IF NOT EXISTS user (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE, email TEXT, age INTEGER)- INSERT \u2014 sqlh.Insert(db, User{Name: &#171;Alice&#187;}) \u2192 INSERT INTO user (name, email, age) VALUES (?, ?, ?)- SELECT \u2014 sqlh.Get[User](db, &#8230;) \u2192 SELECT id, name, email, age FROM user WHERE &#8230; LIMIT 2- UPDATE \u2014 sqlh.Update(db, &#8230;) \u2192 UPDATE user SET name=?, email=?, age=? WHERE &#8230;- DELETE \u2014 sqlh.Delete[User](db, &#8230;) \u2192 DELETE FROM user WHERE &#8230;### \u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430&#171;`\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\u2502 \u00a0sqlh package \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u2502\u2502 \u00a0Insert, Get, List, Update, Delete, Set, \u00a0 \u00a0\u2502\u2502 \u00a0Create \u2014 \u0441 \u0430\u0432\u0442\u043e-\u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u044f\u043c\u0438 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u2502\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\u2502 \u00a0query package \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u2502\u2502 \u00a0SQL-\u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f, \u043a\u0435\u0448 \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0445, JOIN \u00a0 \u00a0 \u00a0 \u00a0\u2502\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\u2502 \u00a0database\/sql (stdlib) \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u2502\u2502 \u00a0\u041f\u0443\u043b \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0439, \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 raw-\u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u00a0 \u00a0\u2502\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518&#171;`### \u041a\u043b\u044e\u0447\u0435\u0432\u044b\u0435 \u0434\u0438\u0437\u0430\u0439\u043d-\u0440\u0435\u0448\u0435\u043d\u0438\u044f1. Generics-first (Go 1.25+) \u2014 Get[User]() \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 *User \u0441 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u043e\u0439 \u0442\u0438\u043f\u043e\u0432 \u043d\u0430 \u044d\u0442\u0430\u043f\u0435 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0446\u0438\u0438. \u041d\u0438\u043a\u0430\u043a\u0438\u0445 interface{}, \u043d\u0438\u043a\u0430\u043a\u0438\u0445 \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u0438\u0439 \u0442\u0438\u043f\u043e\u0432.2. \u0420\u0435\u0444\u043b\u0435\u043a\u0441\u0438\u044f \u043e\u0434\u0438\u043d \u0440\u0430\u0437 \u2014 \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0435 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u043f\u0430\u0440\u0441\u044f\u0442\u0441\u044f \u0438 \u043a\u0435\u0448\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u0432 sync.Map \u043f\u043e reflect.Type. \u041f\u043e\u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0432\u044b\u0437\u043e\u0432\u044b \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 \u0438\u043c\u0435\u043d\u0430 \u0442\u0430\u0431\u043b\u0438\u0446, \u0441\u043f\u0438\u0441\u043a\u0438 \u043f\u043e\u043b\u0435\u0439, scan-\u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0435.3. \u0410\u0432\u0442\u043e-\u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u0438 \u043d\u0430 \u0437\u0430\u043f\u0438\u0441\u044c \u2014 \u043a\u0430\u0436\u0434\u044b\u0439 Insert, Update, Delete, Set \u043e\u0431\u0451\u0440\u043d\u0443\u0442 \u0432 BEGIN&#8230;COMMIT \u0441 ROLLBACK \u043f\u0440\u0438 \u043e\u0448\u0438\u0431\u043a\u0435. \u0422\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u0438 \u043d\u0438\u043a\u043e\u0433\u0434\u0430 \u043d\u0435 \u0437\u0430\u0431\u0443\u0434\u0435\u0442\u0435.4. \u0420\u0435\u0442\u0440\u0430\u0439 \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u043e\u043a SQLite \u2014 \u043e\u0448\u0438\u0431\u043a\u0438 \u00abdatabase is locked\u00bb \u0440\u0435\u0442\u0440\u0430\u044f\u0442\u0441\u044f \u0434\u043e 20 \u0440\u0430\u0437 \u0441 backoff 100 ms. Production-\u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432\u043e\u0441\u0442\u044c \u0438\u0437 \u043a\u043e\u0440\u043e\u0431\u043a\u0438.5. \u041c\u0443\u043b\u044c\u0442\u0438-\u0411\u0414 \u2014 SQLite (\u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439), MySQL, PostgreSQL (\u043e\u0431\u0430 \u0432 CI), SQL Server (\u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u0430\u043b\u044c\u043d\u043e).## \u00a74. CRUD \u0437\u0430 50 \u0441\u0442\u0440\u043e\u043a: \u0431\u044b\u0441\u0442\u0440\u044b\u0439 \u0441\u0442\u0430\u0440\u0442\u0422\u043e\u0442 \u0436\u0435 CRUD, \u0447\u0442\u043e \u0432 \u043d\u0430\u0447\u0430\u043b\u0435 \u2014 \u043d\u043e ~57% \u043a\u043e\u0440\u043e\u0447\u0435:&#171;`gopackage mainimport (\u00a0 \u00a0 &#171;database\/sql&#187;\u00a0 \u00a0 &#171;fmt&#187;\u00a0 \u00a0 &#171;github.com\/kirill-scherba\/sqlh&#187;\u00a0 \u00a0 _ &#171;github.com\/mattn\/go-sqlite3&#8243;)type User struct {\u00a0 \u00a0 ID \u00a0 \u00a0int64 \u00a0db:&#187;id&#187; db_key:&#187;not null primary key autoincrement&#187;\u00a0 \u00a0 Name \u00a0string db:&#187;name&#187; db_key:&#187;unique&#187;\u00a0 \u00a0 Email string db:&#187;email&#187;\u00a0 \u00a0 Age \u00a0 int \u00a0 \u00a0db:&#187;age&#187;}func main() {\u00a0 \u00a0 db, _ := sql.Open(&#171;sqlite3&#187;, &#171;file::memory:?cache=shared&#187;)\u00a0 \u00a0 defer db.Close()\u00a0 \u00a0 \/\/ 1. CREATE TABLE \u0438\u0437 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b\u00a0 \u00a0 sqlh.Create[User](db)\u00a0 \u00a0 \/\/ 2. INSERT\u00a0 \u00a0 sqlh.Insert(db, User{Name: &#171;Alice&#187;, Email: &#171;alice@example.com&#187;, Age: 30})\u00a0 \u00a0 bobID, _ := sqlh.InsertId(db, User{Name: &#171;Bob&#187;, Email: &#171;bob@example.com&#187;, Age: 25})\u00a0 \u00a0 \/\/ 3. GET \u043f\u043e ID \u2014 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 *User, \u043d\u0435 interface{}\u00a0 \u00a0 u, _ := sqlh.Get[User](db, sqlh.Eq(&#171;id&#187;, bobID))\u00a0 \u00a0 fmt.Println(u.Name) \/\/ &#171;Bob&#187;\u00a0 \u00a0 \/\/ 4. LIST \u0432\u0441\u0435\u0445 \u2014 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 []User + next offset\u00a0 \u00a0 users, ,  := sqlh.List[User](db, 0, &#171;&#187;, &#171;name ASC&#187;)\u00a0 \u00a0 fmt.Println(len(users)) \/\/ 2\u00a0 \u00a0 \/\/ 5. UPDATE \u2014 \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u043c \u043f\u043e\u043b\u043d\u0443\u044e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0437\u0430\u043d\u0443\u043b\u0438\u0442\u044c \u0434\u0440\u0443\u0433\u0438\u0435 \u043a\u043e\u043b\u043e\u043d\u043a\u0438\u00a0 \u00a0 sqlh.Update(db, sqlh.UpdateAttr[User]{\u00a0 \u00a0 \u00a0 \u00a0 Row: \u00a0 \u00a0User{Name: &#171;Alice&#187;, Email: &#171;alice.new@example.com&#187;, Age: 31},\u00a0 \u00a0 \u00a0 \u00a0 Wheres: []sqlh.Where{sqlh.Eq(&#171;id&#187;, 1)},\u00a0 \u00a0 })\u00a0 \u00a0 \/\/ 6. DELETE\u00a0 \u00a0 sqlh.Delete[User](db, sqlh.Eq(&#171;id&#187;, bobID))}&#171;`~50 \u0441\u0442\u0440\u043e\u043a. \u041d\u0438\u043a\u0430\u043a\u043e\u0433\u043e raw SQL. \u041d\u0438 \u043e\u0434\u043d\u043e\u0433\u043e rows.Scan. \u041d\u0438 \u043e\u0434\u043d\u043e\u0433\u043e BEGIN\/COMMIT. \u041d\u0438 \u043e\u0434\u043d\u043e\u0439 \u043e\u0448\u0438\u0431\u043a\u0438 \u0432 \u043f\u043e\u0440\u044f\u0434\u043a\u0435 \u043a\u043e\u043b\u043e\u043d\u043e\u043a.### \u0421\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u0435 \u0431\u043e\u043a-\u043e-\u0431\u043e\u043a| \u041e\u043f\u0435\u0440\u0430\u0446\u0438\u044f | Raw database\/sql | sqlx | sqlh ||&#8212;&#8212;&#8212;-|&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;|&#8212;&#8212;|&#8212;&#8212;&#8212;-|| CREATE TABLE | Raw SQL-\u0441\u0442\u0440\u043e\u043a\u0430 | Raw SQL-\u0441\u0442\u0440\u043e\u043a\u0430 | sqlh.Create[User](db) || INSERT | Exec(?,?,?) | NamedExec | Insert(T) || GET | QueryRow + Scan | Get(&amp;T) | Get[T](where) || LIST | rows.Next + Scan | Select | List[T](&#8230;) || UPDATE | Exec(?,?,?,?) | NamedExec | Update(attr) || DELETE | Exec(?) | Exec(?) | Delete[T](where) || COUNT | QueryRow + Scan | Get(&amp;int) | Count[T]() || | \u0421\u0442\u0440\u043e\u043a \u043a\u043e\u0434\u0430 | \u0421\u043e\u043a\u0440\u0430\u0449\u0435\u043d\u0438\u0435 ||&#8212;|&#8212;|&#8212;|| Raw database\/sql | ~115 | baseline || sqlx | ~80 | \u221230% || sqlh | ~50 | \u221257% |### Table[T]: \u0443\u0434\u043e\u0431\u043d\u044b\u0439 method-based API\u0414\u043b\u044f \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432, \u0433\u0434\u0435 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439 \u043d\u0430\u0434 \u043e\u0434\u043d\u043e\u0439 \u0442\u0430\u0431\u043b\u0438\u0446\u0435\u0439 \u2014 \u043c\u043e\u0436\u043d\u043e \u043e\u0431\u0435\u0440\u043d\u0443\u0442\u044c \u0432 Table[T]:&#171;`gotbl, _ := sqlh.CreateTable[User](db)tbl.Insert(User{Name: &#171;Charlie&#187;, Email: &#171;charlie@example.com&#187;, Age: 28})c, _ := tbl.Get(sqlh.Eq(&#171;name&#187;, &#171;Charlie&#187;))fmt.Println(c.Name)for _, user := range tbl.List(0, &#171;&#187;, &#171;name ASC&#187;, 0) {\u00a0 \u00a0 fmt.Println(user.Name)}&#171;`Table[T] \u2014 \u043b\u0451\u0433\u043a\u0438\u0439 wrapper \u043d\u0430\u0434 \u043e\u0431\u0449\u0438\u043c *sql.DB. \u041e\u043d \u043d\u0435 \u0432\u043b\u0430\u0434\u0435\u0435\u0442 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435\u043c, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 Close() \u2014 no-op (\u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u0439 \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u043e\u0441\u0442\u0438). \u0420\u0435\u0441\u0443\u0440\u0441\u044b \u043e\u0447\u0438\u0449\u0430\u0435\u0442 \u0432\u044b\u0437\u044b\u0432\u0430\u044e\u0449\u0438\u0439 \u0447\u0435\u0440\u0435\u0437 db.Close().### Set (upsert): \u043d\u0430\u0442\u0438\u0432\u043d\u044b\u0439 UPSERTSet \u2014 \u0430\u0442\u043e\u043c\u0430\u0440\u043d\u044b\u0439 upsert. \u0414\u043b\u044f PostgreSQL, SQLite \u0438 MySQL \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u043d\u0430\u0442\u0438\u0432\u043d\u044b\u0439 \u0441\u0438\u043d\u0442\u0430\u043a\u0441\u0438\u0441 \u0431\u0430\u0437\u044b:- PostgreSQL: INSERT &#8230; ON CONFLICT (&#8230;) DO UPDATE SET &#8230;- SQLite: INSERT &#8230; ON CONFLICT (&#8230;) DO UPDATE SET &#8230;- MySQL: INSERT &#8230; ON DUPLICATE KEY UPDATE &#8230;\u0414\u043b\u044f \u043d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u044b\u0445 \u0434\u0440\u0430\u0439\u0432\u0435\u0440\u043e\u0432 \u2014 fallback \u043d\u0430 SELECT-then-INSERT\/UPDATE \u0432 \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u0438.&#171;`go\/\/ name \u043f\u043e\u043c\u0435\u0447\u0435\u043d db_key:&#187;unique&#187; \u2014 Set \u0441\u0434\u0435\u043b\u0430\u0435\u0442 UPDATE \u043f\u0440\u0438 \u0441\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u0438err := sqlh.Set(db, User{Name: &#171;Dave&#187;, Email: &#171;dave@example.com&#187;}, sqlh.Eq(&#171;name&#187;, &#171;Dave&#187;))&#171;`### ListRange: Go 1.25 iterators\u0412\u043c\u0435\u0441\u0442\u043e List \u0441 \u0441\u043b\u0430\u0439\u0441\u043e\u043c \u2014 \u043b\u0435\u043d\u0438\u0432\u044b\u0439 \u0438\u0442\u0435\u0440\u0430\u0442\u043e\u0440 ListRange, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 &#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-483420","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/483420","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=483420"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/483420\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=483420"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=483420"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=483420"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}