SQLite / Postgres / Heroku: Проблема перевода запроса - PullRequest
0 голосов
/ 03 мая 2011

Heroku выдает ошибку в моем Postgres-Query сообщении:

ActiveRecord :: StatementInvalid (PGError: ОШИБКА: синтаксическая ошибка в или около "date" 2011-05-03T13: 58: 22+00:00 app [web.1]: LINE 1: ... 2011-05-31 ') GROUP BY EXTRACT (ГОД ОТ TIMESTAMP date) || EXT ...

Запрос SQLiteв разработке работает как положено.Вот код:

  def self.calculate(year, month, user_id, partner_id)
    case ActiveRecord::Base.connection.adapter_name  
      when 'SQLite'
        where(':user_id = entries.user_id OR :partner_id = entries.user_id', {    
            :user_id => user_id,
            :partner_id => partner_id
        }).
        where('entries.date <= :last_day', { 
            :last_day => Date.new(year, month, 1).at_end_of_month
        }).
        select('entries.date, ' + 
               'sum(case when joint = "f" then amount_calc else 0 end) as sum_private, ' + 
               'sum(case when joint = "t" and user_id = ' + user_id.to_s + ' then amount_calc else 0 end) as sum_user_joint, ' + 
               'sum(case when joint = "t" and user_id = ' + partner_id.to_s + ' then amount_calc else 0 end) as sum_partner_joint, ' +              
               'sum(case when compensation = "t" and user_id = ' + user_id.to_s + ' then amount_calc else 0 end) as sum_user_compensation, ' +
               'sum(case when compensation = "t" and user_id = ' + partner_id.to_s + ' then amount_calc else 0 end) as sum_partner_compensation '
        ).
        group("strftime('%Y-%m', date)")
      when 'PostgreSQL'
        where(':user_id = entries.user_id OR :partner_id = entries.user_id', {    
            :user_id => user_id,
            :partner_id => partner_id
        }).
        where('entries.date <= :last_day', { 
            :last_day => Date.new(year, month, 1).at_end_of_month
        }).
        select('entries.date, ' + 
               'sum(case when joint = "f" then amount_calc else 0 end) as sum_private, ' + 
               'sum(case when joint = "t" and user_id = ' + user_id.to_s + ' then amount_calc else 0 end) as sum_user_joint, ' + 
               'sum(case when joint = "t" and user_id = ' + partner_id.to_s + ' then amount_calc else 0 end) as sum_partner_joint, ' +              
               'sum(case when compensation = "t" and user_id = ' + user_id.to_s + ' then amount_calc else 0 end) as sum_user_compensation, ' +
               'sum(case when compensation = "t" and user_id = ' + partner_id.to_s + ' then amount_calc else 0 end) as sum_partner_compensation '
        ).
        group("EXTRACT(YEAR FROM TIMESTAMP date)||EXTRACT(MONTH FROM TIMESTAMP date)")
    else
      raise 'Query not implemented for this DB adapter'
    end
  end

Буду очень признателен за любые подсказки.И поскольку я уже задаю здесь вопрос, я не уверен относительно case when joint = "t" в суммах в обоих запросах, есть ли лучший способ сделать это?

ОБНОВЛЕНИЕ

Благодаря как peufeu, так и лошади без имени, код теперь выглядит так:

when 'PostgreSQL'
where(':user_id = entries.user_id OR :partner_id = entries.user_id', {    
    :user_id => user_id,
    :partner_id => partner_id
}).
where('entries.date <= :last_day', { 
    :last_day => Date.new(year, month, 1).at_end_of_month
}).
select('min(entries.date) as date, ' + 
       'sum(case when joint = false then amount_calc else 0 end) as sum_private, ' + 
       'sum(case when joint = true and user_id = ' + user_id.to_s + ' then amount_calc else 0 end) as sum_user_joint, ' + 
       'sum(case when joint = true and user_id = ' + partner_id.to_s + ' then amount_calc else 0 end) as sum_partner_joint, ' +              
       'sum(case when compensation = true and user_id = ' + user_id.to_s + ' then amount_calc else 0 end) as sum_user_compensation, ' +
       'sum(case when compensation = true and user_id = ' + partner_id.to_s + ' then amount_calc else 0 end) as sum_partner_compensation '
).
group('EXTRACT(YEAR FROM "date"), EXTRACT(MONTH FROM "date")')

... и работает, как и ожидалось.

Еще одно мое утверждение сталкивается с проблемамисейчас и я редактирую его здесь, как кажется, связанный с ответом peufeu.Модель / Контроллер:

   def self.all_entries_month(year, month, user_id, partner_id)
    mydate = Date.new(year, month, 1)

    where(':user_id = entries.user_id OR (:partner_id = entries.user_id AND entries.joint = :true)', {
        :user_id => user_id,
        :partner_id => partner_id,
        :true => true
    }).
    where(':first_day <= entries.date AND entries.date <= :last_day', { 
        :first_day => mydate,
        :last_day => mydate.at_end_of_month
    })
  end

  # group by tag and build sum of groups named group_sum
  def self.group_by_tag
    group('tag').
    select('entries.*, sum(amount_calc) as group_sum')
  end

Контроллер:

@income = Entry.all_entries_month(@year, @month, current_user.id, current_partner.id).income
@cost = Entry.all_entries_month(@year, @month, current_user.id, current_partner.id).cost

# group cost by categories
@group_income = @income.group_by_tag.order('group_sum desc')
@group_cost = @cost.group_by_tag.order('group_sum')

Ошибка:

ActionView::Template::Error (PGError: ERROR:  column "entries.id" must appear in the GROUP BY clause or be used in an aggregate function
2011-05-03T18:35:20+00:00 app[web.1]: : SELECT entries.*, sum(amount_calc) as group_sum FROM "entries" WHERE (1 = entries.user_id OR (2 = entries.user_id AND entries.joint = 't')) AND ('2011-04-01' <= entries.date AND entries.date <= '2011-04-30') AND (amount_calc <= 0 AND compensation = 'f') GROUP BY tag ORDER BY group_sum):
2011-05-03T18:35:20+00:00 app[web.1]:     6:    </thead>
2011-05-03T18:35:20+00:00 app[web.1]:     7:    <tbody>
2011-05-03T18:35:20+00:00 app[web.1]:     8:        <% if categories %>
2011-05-03T18:35:20+00:00 app[web.1]:     9:            <% categories.each do |category| %>     
2011-05-03T18:35:20+00:00 app[web.1]:     10:               <tr>
2011-05-03T18:35:20+00:00 app[web.1]:     11:                   <td class="align-left"><%= category.tag %></td>
2011-05-03T18:35:20+00:00 app[web.1]:     12:                   <td class="align-right"><%= my_number_to_percentage (category.group_sum.to_f / total_cost) * 100 %></td>

ОБНОВЛЕНИЕ 2: Я нашел решение

  # group by tag and build sum of groups named group_sum
  def self.group_by_tag
    group('tag').
    select('tag, sum(amount_calc) as group_sum')
  end

Ответы [ 2 ]

3 голосов
/ 03 мая 2011

Проблема есть:

EXTRACT(YEAR FROM TIMESTAMP date)||EXTRACT(MONTH FROM TIMESTAMP date)

Решение:

EXTRACT(YEAR FROM "date"), EXTRACT(MONTH FROM "date")

Слово "TIMESTAMP" там немного неуместно;) ... также:

Поскольку DATE является зарезервированным ключевым словом SQL, очень плохо использовать его в качестве имени столбца.Здесь я процитировал это, используя «так, чтобы postgres не запутался.

И вам не нужно тратить процессорное время на построение строки, объединяющей ваши два ЭКСТРАКТА, просто помните, что GROUP BY может использовать несколько параметров.

Тогда, конечно, запрос не будет выполнен, потому что вы ВЫБИРАЕТЕ "дату", но не указали дату GROUP BY. У postgres нет способа узнать, какую дату вы хотите из всех строк, имеющих один и тот же год-месяц. MySQL будетвозвращать случайное значение из строк, postgres любит корректность, поэтому он выдаст ошибку. Например, вы можете выбрать SELECT min (date), что будет правильным.

Я не уверен в случае, когдаjoint = "t"

Это зависит от того, как вы хотите получить свои результаты, в этом нет ничего плохого.

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

1 голос
/ 03 мая 2011

Я не знаю ruby ​​/ heroku, но выражение

joint = "t"

относится к столбцу t в PostgreSQL, поскольку имена объектов указаны в кавычкахс двойными кавычками.Так что, если Ruby / Heroku не заменит эти двойные кавычки одинарными, это будет недопустимым условием.

Если t должен быть строковым литералом (символ t), тогда вам нужно использовать одинарные кавычки: joint = 't'

Если joint имеет тип boolean, тогда вам следует использоватьjoint = true в PostgreSQL

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...