2010-06-17 2 views
4

Я хотел оптимизировать запрос к базе данных:SQLAlchemy: выбрать более нескольких таблиц

link_list = select(
    columns=[link_table.c.rating, link_table.c.url, link_table.c.donations_in], 
    whereclause=and_(
     not_(link_table.c.id.in_(
      select(
       columns=[request_table.c.recipient], 
       whereclause=request_table.c.donator==donator.id 
      ).as_scalar() 
     )), 
     link_table.c.id!=donator.id, 
    ), 
    limit=20, 
).execute().fetchall() 

и попытался объединить эти два выбирает в одном запросе:

link_list = select(
    columns=[link_table.c.rating, link_table.c.url, link_table.c.donations_in], 
    whereclause=and_(
     link_table.c.active==True, 
     link_table.c.id!=donator.id, 
     request_table.c.donator==donator.id, 
     link_table.c.id!=request_table.c.recipient, 
    ), 
    limit=20, 
    order_by=[link_table.c.rating.desc()] 
).execute().fetchall() 

база-схема выглядит так:

link_table = Table('links', metadata, 
    Column('id', Integer, primary_key=True, autoincrement=True), 
    Column('url', Unicode(250), index=True, unique=True), 
    Column('registration_date', DateTime), 
    Column('donations_in', Integer), 
    Column('active', Boolean), 
) 
request_table = Table('requests', metadata, 
    Column('id', Integer, primary_key=True, autoincrement=True), 
    Column('recipient', Integer, ForeignKey('links.id')), 
    Column('donator', Integer, ForeignKey('links.id')), 
    Column('date', DateTime), 
) 

В таблице request_table имеется несколько ссылок (донор), указывающих на одну ссылку в link_table. Я хочу иметь ссылки из link_table, которые еще не запрошены.

Но это не работает. Действительно ли это возможно, что я пытаюсь сделать? Если да, как бы вы это сделали?

спасибо, что заблаговременно!

+1

Я решил эту проблему самостоятельно. Мне нужно было левое внешнее соединение, потому что это отношение много-к-одному. Спасибо! –

+8

@ahojnnes: Пожалуйста, разместите свое решение в ответах для целей истории. – nosklo

+0

, честно говоря, мое решение не было решением, потому что в конце я понял, что он сделал что-то другое. Но на данный момент я достиг точки, где, я думаю, это невозможно с объединениями - по крайней мере, не так эффективно, как с двумя выборами. Так что я был бы доволен ответом! –

ответ

0

риффов на masida's answer:

Во-первых, первоначальный запрос:

>>> print select(
...  columns=[link_table.c.url, link_table.c.donations_in], 
...  whereclause=and_(
...   not_(link_table.c.id.in_(
...    select(
...     columns=[request_table.c.recipient], 
...     whereclause=request_table.c.donator==5 
...   ).as_scalar() 
...  )), 
...   link_table.c.id!=5, 
... ), 
...  limit=20, 
...) 
SELECT links.url, links.donations_in 
FROM links 
WHERE links.id NOT IN (SELECT requests.recipient 
FROM requests 
WHERE requests.donator = :donator_1) AND links.id != :id_1 
LIMIT 20 

И переписать в терминах существует():

>>> print select(
...  columns=[link_table.c.url, link_table.c.donations_in], 
...  whereclause=and_(
...  not_(exists().where(request_table.c.donator==5)), 
...  # ^^^^^^^^^^^^^^ 
...   link_table.c.id!=5, 
... ), 
...  limit=20, 
...) 
SELECT links.url, links.donations_in 
FROM links 
WHERE NOT (EXISTS (SELECT * 
FROM requests 
WHERE requests.donator = :donator_1)) AND links.id != :id_1 
LIMIT 20 
+1

этот вид переписки имеет смысл в MySQL, который известен плохой оптимизацией подзадач; Другие СУБД могут вообще не пользоваться; например postgresql всегда будет планировать «не в подзапросе» как анти-соединение, точно так же, как он планирует «не существует», – SingleNegationElimination

Смежные вопросы