1

У меня есть запрос, который выглядит следующим образом:РЕЛЬСЫ оптимального качества SQL подзапрос

@inventory = Pack.find_by_sql("SELECT Packs.id, "+ 
" (SELECT COUNT(*) FROM Stocks WHERE (Stocks.pack_id = Packs.id AND Stocks.status = 'online'  AND Stocks.user_id = #{current_user.id})) AS online,"+ 
" (SELECT COUNT(*) FROM Stocks WHERE (Stocks.pack_id = Packs.id AND Stocks.status = 'offline'  AND Stocks.user_id = #{current_user.id})) AS offline,"+ 
" (SELECT COUNT(*) FROM Stocks WHERE (Stocks.pack_id = Packs.id AND Stocks.status = 'depositing' AND Stocks.user_id = #{current_user.id})) AS depositing,"+ 
" (SELECT COUNT(*) FROM Stocks WHERE (Stocks.pack_id = Packs.id AND Stocks.status = 'withdrawing' AND Stocks.user_id = #{current_user.id})) AS withdrawing,"+ 
" (SELECT COUNT(*) FROM Stocks WHERE (Stocks.pack_id = Packs.id AND Stocks.status = 'selling'  AND Stocks.user_id = #{current_user.id})) AS selling,"+ 
" (SELECT COUNT(*) FROM Transactions WHERE (Transactions.pack_id = Packs.id AND Transactions.status = 'buying' AND Transactions.buyer_id = #{current_user.id})) AS buying"+ 
" FROM Packs WHERE disabled = false") 

Я имею в виду, что есть способ сделать новый суб-запрос таким образом, что вместо

SELECT FROM Stocks 

выбирает запроса из хранимой таблицы

SELECT FROM (Stocks WHERE (Stocks.pack_id = Packs.id AND Stocks.user_id = #{current_user.id})) 

который будет запрошен только раз. Затем материал WHERE Stocks.status = ? будет применен к этой сохраненной таблице.

Любые помощь ребята?

+0

Пожалуйста, всегда обеспечивают вашу версию Postgres. –

+0

9.1 на данный момент. В какой-то момент я, вероятно, буду обновляться до 9.4. –

+0

[Итак, у вас есть ваш ответ?] (Http://meta.stackexchange.com/a/5235/169168) –

ответ

0

Если то, что вы после того, как это подсчет различных типов, то вроде следующего будет гораздо меньше кода и проще для чтения/сохранения, IMO ...

Вы можете разделить их на различные таблицы, поэтому, для stocks, что-то вроде этого:

@inventory = Pack.find_by_sql("SELECT status, count(*) 
           FROM stocks 
           WHERE user_id = ? 
           GROUP BY status 
           ORDER BY status", current_user.id) 

Обратите внимание на важность использования ? для предотвращения инъекций SQL. Кроме того, Ruby поддерживает многострочные строки, поэтому нет необходимости цитировать и конкатенировать каждую строку.

Вы можете сделать что-то подобное для других таблиц.

+0

К сожалению, этого не произойдет и не получится. –

1

Лучший запрос зависит от распределения данных и других деталей.

Это очень эффективно, пока большинство pack_id из подзапросов фактически используются в соединении с packs (наиболее packs являются NOT disabled):

SELECT p.id 
    , s.online, s.offline, s.depositing, s.withdrawing, s.selling, t.buying 
FROM packs p 
LEFT JOIN (
    SELECT pack_id 
     , count(status = 'online'  OR NULL) AS online 
     , count(status = 'offline'  OR NULL) AS offline 
     , count(status = 'depositing' OR NULL) AS depositing 
     , count(status = 'withdrawing' OR NULL) AS withdrawing 
     , count(status = 'selling'  OR NULL) AS selling 
    FROM stocks 
    WHERE user_id = #{current_user.id} 
    AND status = ANY('{online,offline,depositing,withdrawing,selling}'::text[]) 
    GROUP BY 1 
    ) s ON s.pack_id = p.id 
LEFT JOIN (
    SELECT pack_id, count(*) AS buying 
    FROM transactions 
    WHERE status = 'buying' 
    AND buyer_id = #{current_user.id} 
    ) t ON t.pack_id = p.id 
WHERE NOT p.disabled; 

В стр 9,4 вы можете использовать совокупный ФИЛЬТР оговорка:

SELECT pack_id 
    , count(*) FILTER (WHERE status = 'online')  AS online 
    , count(*) FILTER (WHERE status = 'offline')  AS offline 
    , count(*) FILTER (WHERE status = 'depositing') AS depositing 
    , count(*) FILTER (WHERE status = 'withdrawing') AS withdrawing 
    , count(*) FILTER (WHERE status = 'selling')  AS selling 
FROM stocks 
WHERE ... 

Детали:

Использование crosstab() для сводной таблицы, чтобы сделать это быстрее, но:

SELECT p.id 
    , s.online, s.offline, s.depositing, s.withdrawing, s.selling, t.buying 
FROM packs p 
LEFT JOIN crosstab( $$ SELECT pack_id, status, count(*)::int AS ct FROM stocks WHERE user_id = $$ || #{current_user.id} || $$ AND status = ANY('{online,offline,depositing,withdrawing,selling}'::text[]) GROUP BY 1, 2 ORDER BY 1, 2 $$ ,$$SELECT unnest('{online,offline,depositing,withdrawing,selling}'::text[])$$ ) s (pack_id int , online int , offline int , depositing int , withdrawing int , selling int ) USING (pack_id) 
LEFT JOIN (
    SELECT pack_id, count(*) AS buying 
    FROM transactions 
    WHERE status = 'buying' 
    AND buyer_id = #{current_user.id} 
    ) t ON t.pack_id = p.id 
WHERE NOT p.disabled;

Подробности здесь:

Если большинство packs: disabled, LATERAL Соединения будут быстрее (требуется pg 9.3 или более поздней версии):

SELECT p.id 
    , s.online, s.offline, s.depositing, s.withdrawing, s.selling, t.buying 
FROM packs p 
LEFT JOIN LATERAL (
    SELECT pack_id 
     , count(status = 'online'  OR NULL) AS online 
     , count(status = 'offline'  OR NULL) AS offline 
     , count(status = 'depositing' OR NULL) AS depositing 
     , count(status = 'withdrawing' OR NULL) AS withdrawing 
     , count(status = 'selling'  OR NULL) AS selling 
    FROM stocks 
    WHERE user_id = #{current_user.id} 
    AND status = ANY('{online,offline,depositing,withdrawing,selling}'::text[]) 
    AND pack_id = p.id 
    GROUP BY 1 
    ) s ON TRUE 
LEFT JOIN LATERAL (
    SELECT pack_id, count(*) AS buying 
    FROM transactions 
    WHERE status = 'buying' 
    AND buyer_id = #{current_user.id} 
    AND pack_id = p.id 
    ) t ON TRUE 
WHERE NOT p.disabled;

Почему LATERAL? Существуют ли альтернативы в pg 9.1?

+0

Этот код работает, и это то, что я обычно пытаюсь сделать, но примерно в 15 раз медленнее. Однако я не использую достаточное количество тестовых данных, поэтому мне придется добавить больше и сравнить. –

+0

@ user1694853: для оптимизации производительности нам потребуется больше данных. В частности, определения таблиц, мощности и значения частот (какой процент от 'pack'' отключен', сколько разных значений в 'stocks.status'? Freuquecies? И т.д. Я добавил еще несколько опций. –

+0

Святое дерьмо у вас есть безумное знание sql. Мне нужно будет кое-что прочитать, чтобы понять, что происходит здесь, lol. Спасибо за помощь. Я вернусь к вам, когда я проверил против более крупного пула объектов. –