Это не проблема Go, как таковая, скорее вопрос о том, как оптимально структурировать ваш SQL в вашем коде. Вы берете набор результатов, выполняя запрос для 2 000 000 строк:
rows, err := db.Query("SELECT id FROM chapters where title = 'custom_type'")
, а затем выполняете другой запрос для каждой строки в этом наборе результатов:
row, err := db.Query(fmt.Sprintf("SELECT id FROM chapters where parent_id = %v", id))
и затем выполнить еще немного кода для каждого из этих , по-видимому, один за другим:
for rows.Next() {
// ignore update code for simplify
}
Это фактически два уровня вложенности операторов, что является очень неэффективным способом загрузки всехэти результаты попадают в программную память, а затем выполняются независимые операторы UPDATE:
SELECT
+---->SELECT
+---->UPDATE
Вместо этого вы могли бы выполнять всю работу в самой базе данных, что было бы гораздо более эффективным. Вы не показываете, что такое оператор UPDATE
, но это ключевая часть. Допустим, вы хотите установить флаг publish
. Вы можете сделать что-то вроде этого:
UPDATE chapters
SET publish=true
WHERE parent_id in
(SELECT id FROM chapters
WHERE title='custom_type')
RETURNING id;
Используя вложенный запрос, вы можете объединить все из трех отдельных запросов в один запрос. База данных содержит всю информацию, необходимую для оптимизации операции и построения наиболее эффективного плана запросов, и вы только выполняете операцию single db.Query
. Предложение RETURNING
позволяет получить список идентификаторов, которые были обновлены в операции. Таким образом, код будет таким простым:
func main(){
rows, err := db.Query("UPDATE chapters SET publish=true WHERE parent_id in" +
"(SELECT id FROM chapters WHERE title='custom_type')" +
"RETURNING id;")
if err != nil {
panic(err)
}
for rows.Next() {
var id int
_ = rows.Scan(&id)
fmt.Println(id)
}
}