Оптимизация доступа к данным SQL на ходу

Я пытаюсь переключить реализацию API, над которой я работал, с библиотеки GORM ORM на SQLx, чтобы сделать доступ к данным более эффективным. В частности, я пытаюсь избавиться от некоторых проблем с SELECT N + 1. Итак, у меня есть отношения "один ко многим", когда на сайте есть сообщения. Реализуемый мной API возвращает список сайтов в виде объекта JSON, и каждый сайт имеет вложенный список posts. Структура выглядит примерно так

{
    "sites": [
        {
            "id": 1,
            "name": "Site #1",
            "posts" [
                {"title": "Post #1", "published": "1/2/2000", ... },
                {"title": "Post #2", "published": "1/3/2000", ... },
                ... more posts ...
            ]
        },
        {
            "id": 2,
            "name": "Site #2",
            "posts": [
                 ... post list for site #2 ...
            ]
        }
        ... more sites ...
   ]
}

Это было легко реализовать в GORM, но как только я взглянул на SQL GORM, работающий для реализации этого, я понял, что он выполняет SELECT из сообщений для каждого сайта в списке. Поэтому я пытаюсь использовать такой SQL, чтобы избежать проблемы N + 1.

SELECT s.id, s.name, p.title, p.published 
FROM sites as s, posts as p 
WHERE p.site_id = s.id

Это дает мне все необходимые данные одним запросом. Но я несколько застрял в том, как отсканировать все это в список структур сайта. В GORM у меня были определены следующие структуры (упрощенные для краткости)

type struct Post {
    Id        uint      `json:"-"`
    Title     string
    Published time.Time
    SiteId    uint      `json:"-"`
    Site      Site      `json:"-"`
}

type struct Site {
    Id   uint
    Name string
}

А потом я бы сделал что-нибудь вроде

var sites []Site
result := db.Preload('Posts').Find(&sites)
if result.Error != nil {
    ... error handling ...
} else {
   operate on sites here
}

Итак, вопрос в том, как мне сканировать мой новый SQL с помощью SQLx на фрагмент структур таким образом, чтобы в результате получилась структура данных, аналогичная той, что создавала GORM? Я не против написать свой собственный сканер, но все же хочу иметь возможность использовать методы SQLx Select() и Get(). Что мне нужно сделать, чтобы это сработало?

var sites []Site
err := db.Select(query, &sites) // where query is SQL from above

Изменить: кажется, что если я использую точный код, представленный в этом вопросе, GORM на самом деле не выполняет N + 1 выборок, он выполняет два запроса: один простой SELECT для сайтов и один SELECT .. . WHERE ... IN ... для сообщений, а затем сопоставляет два набора результатов. Я все еще хочу знать, как это сделать в SQLx.


person Mad Wombat    schedule 15.03.2017    source источник
comment
Нет никакой магии. Просто сделайте первый запрос к mysql для сайтов, затем извлеките идентификаторы с помощью простого цикла. Затем выполните второй запрос сообщений с WHERE id IN (). Наконец, постройте новые структуры данных, используя два набора данных, используя простые циклы. Просто используйте простой SQL с SQLx API, который довольно хорошо документирован.   -  person Alexander R.    schedule 16.03.2017
comment
Я не жду магии. Я могу написать функцию, которая будет использовать мой единственный оператор SQL и создавать и заполнять структуры сайта и структуры сообщений. И я предполагаю, что мне придется его написать. Но я хочу скрыть это внутри процесса сканирования строк, поэтому на поверхности я могу использовать собственные API-интерфейсы SQLx.   -  person Mad Wombat    schedule 16.03.2017


Ответы (1)


Возможно, это не ответ, но слишком длинный для комментария.

Если вы все еще используете GORM, вы могли бы создать собственный SQL. См. Документацию: http://jinzhu.me/gorm/advanced.html#sql-builder

Для вас это могло быть примерно так:

// Scan

type struct Post {
    Id        uint      `json:"-"`
    Title     string
    SiteId    uint      `json:"-"`
    Site      Site      `json:"-"`
}

var result Post

db.Raw("
SELECT s.id, s.name, p.title, p.published 
FROM sites as s, posts as p 
WHERE p.site_id = s.id").Scan(&result)
person kenfire    schedule 28.09.2017