В попытке научить себя программированию я создаю небольшое веб-приложение (Flask, SQLAlchemy, Jijna), чтобы отобразить все книги, которые я когда-либо заказывал от Амазонки.Улучшение скорости запроса во взаимоотношениях «многие ко многим»
В «самых красивых костях», я пытаюсь научиться реплицировать http://pinboard.in -that's my paragon; Macie Cegłowski - это прямая G ... Я понятия не имею, как его сайт работает так быстро: я могу загрузить 160 закладок - все со связанными тегами -in, я не знаю, 500 мс? ... вот почему я знаю, что я делаю что-то ужасно, ужасно неправильно, как обсуждается ниже. (Если бы я мог, я бы просто заплатить ему обучать меня. Lulz.)
В любом случае, я создал многие-ко-многим между моим books
класса и моего tag
класса, так что пользователь может (1) щелкните по book
и просмотрите все его tags
, а также (2) щелкните по tag
и просмотрите все похожие книги. Вот моя таблица архитектура:
Вот код для отношений между двумя классами:
assoc = db.Table('assoc',
db.Column('book_id', db.Integer, db.ForeignKey('books.book_id')),
db.Column('tag_id', db.Integer, db.ForeignKey('tags.tag_id'))
)
class Book(db.Model):
__tablename__ = 'books'
book_id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(120), unique=True)
auth = db.Column(db.String(120), unique=True)
comment = db.Column(db.String(120), unique=True)
date_read = db.Column(db.DateTime)
era = db.Column(db.String(36))
url = db.Column(db.String(120))
notable = db.Column(db.String(1))
tagged = db.relationship('Tag', secondary=assoc, backref=db.backref('thebooks',lazy='dynamic'))
def __init__(self, title, auth, comment, date_read, url, notable):
self.title = title
self.auth = auth
self.comment = comment
self.date_read = date_read
self.era = era
self.url = url
self.notable = notable
class Tag(db.Model):
__tablename__ = 'tags'
tag_id = db.Column(db.Integer, primary_key=True)
tag_name = db.Column(db.String(120))
проблема
Если я перебирать books
таблицы только (~ 400 строк), запрос запускается и отображается в браузере молниеносной скоростью. Нет проблем.
{% for i in book_query %}
<li>
{{i.notable}}{{i.notable}}
<a href="{{i.url}}">{{i.title}}</a>, {{i.auth}}
<a href="/era/{{i.era}}">{{i.era}}</a> {{i.date_read}}
{% if i.comment %}
<p>{{i.comment}}</p>
{% else %}
<!-- print nothing -->
{% endif %}
</li>
{% endfor %}
Если, однако, я хочу, чтобы показать любые и все теги, связанные с книгой, я изменить код, вкладывая в for loop
следующим образом:
{% for i in book_query %}
<li>
{{i.notable}}{{i.notable}}
<a href="{{i.url}}">{{i.title}}</a>, {{i.auth}}
<a href="/era/{{i.era}}">{{i.era}}</a>
{% for ii in i.tagged %}
<a href="/tag/{{ii.tag_name}}">{{ii.tag_name}}</a>
{% endfor %}
{{i.date_read}}
{% if i.comment %}
<p>{{i.comment}}</p>
{% else %}
<!-- print nothing -->
{% endif %}
</li>
{% endfor %}
Запрос значительно замедляется (занимает около 20 секунд). Я понимаю, что это происходит потому, что для каждой строки в таблице book
мой код выполняет итерацию через всю таблицуassoc
(т. Е. «Полное сканирование таблицы»).
обсуждение (или «то, что я думаю, что происходит»)
Очевидно, что я полный нуб-I've программировали в течение ~ 3 месяцев. Это мотивирует только заставить все работать, но я понимаю, что у меня большие пробелы в моей базе знаний, которые я пытаюсь заполнить, когда я иду.
Сразу, что летучая мышь, я могу оценить, что это невероятно неэффективно, что с каждой новой книгой, код перебором таблицы всей ассоциации (если это действительно то, что происходит, что я считаю, что это). Я думаю, мне нужно класть (?) Или сортировать (?) Таблицу assoc
таким образом, что как только я получаю все теги для book with book_id == 1
, я больше никогда не «проверю» строки с book_id == 1
в таблице assoc
.
Другими словами, то, что я думаю, что происходит это (в computerspeak):
- О, он хочет знать, как книга с
book_id == 1
вbooks
стол был помечен - Хорошо, позвольте мне перейдите на страницу
assoc
Таблица - Строка № 1 ...
book_id
вassoc
Таблица равна1
? - Хорошо, это так; то что такое
tag_id
для строки №1? ... [затем компьютер переходит вtag
стол, чтобы получитьtag_name
, и возвращает его в браузер] - Row # 2 ... в
assoc
таблицеbook_id
равна1
? - О, нет, это не ... хорошо, перейдите к строке # 3
- Хмммм, потому что мой программист глуп и не сделал эту таблицу сортированной или проиндексированной каким-то образом, я собираюсь должно пройти через все
assoc
стола ищетbook_id == 1
когда, возможно, не больше и ...
Тогда, как только мы получаем book_id == 2
в books table
компьютере становится действительно безумно:
- Хорошо, он хочет знать все теги, которые идут с
book_id == 2
- Хорошо, позвольте мне перейти к
assoc
таблице - Row # 1 ... секундочку ... я не проверить это один уже ?? Holy sh # t, я должен сделать это снова и снова ??
- Dammit ... okay ... Row # 1 ... is
book_id == 2
? (Я знаю, что это не так! Но я должен проверить в любом случае, потому что мой программист является дум-дум ...)
Таким образом, вопрос, могу ли я (1) сортировать (?) или кластер (?) таблицу assoc
в некотором роде, которая обеспечивает более «интеллектуальный» обход таблицы assoc
, или, как мне показалось, я (2) «научиться писать хорошие SQL-запросы»? (Обратите внимание, я никогда не узнал SQL, так как я все обращения с SQLAlchemy ... проклятые Алхимики ... окутывающих их в магии секрет и этажерки.)
финальные слова
Спасибо за любой вклад. Если у вас есть предложения, которые помогут мне улучшить, как я задаю вопросы о stackoverflow (это мой первый пост!), Пожалуйста, дайте мне знать.