Обновление вложенного соединения не работает с заказом - PullRequest
1 голос
/ 18 июня 2020

Давний читатель, первый плакат. (Я провел несколько глубоких поисков и не смог найти ничего похожего - заранее спасибо)

Я кодирую проект, используя ruby и активную запись, и я столкнулся с ситуацией, когда я ' Я не могу найти в Google ответ на вопрос, почему происходят две вещи. Насколько я могу судить, первое, что происходит из-за известной ошибки с рельсами. Второе, я не знаю.

Вот имитация кода:

class Object1 < ActiveRecord::Base
  has_many :object2s, foreign_key: :object1_id, :dependent => :delete_all
end

class Object2 < ActiveRecord::Base
  belongs_to :object1, class_name: "Object1"
end

Object2 имеет уникальный индекс для object1_id и date.

У меня есть обновление, которое сбой из-за нарушения индекса:

ActiveRecord :: RecordNotUnique в Object1Controller # datechange

Код обновления:

Object2.joins( :object1 ).where( object1: { :id => id } )**.order( obj2_date: ascdsc )**.update_all(
    "obj2_date = " + direction + "(obj2_date, INTERVAL " + difference.to_s + " month)")

Индекс отключается при обновлении без заказа (** добавлено выше) даты обновляются таким образом, что это вызывает нарушение. В другом месте кода я указал порядок обновления, и он обновит их таким образом, чтобы не нарушать индекс. С этим объектом добавление в соединение вызывает первую проблему:

Вот (макет) SQL сгенерировано:

UPDATE object2 SET obj2_date = date_sub(obj2_date, INTERVAL 1 month) 
WHERE object2.id IN (
    SELECT id FROM (
        SELECT object2.id FROM object2 INNER JOIN object1 ON object1.id = object2.object1_id WHERE <criteria> **ORDER BY object2.obj2_date ASC**
    )  __active_record_temp
)

Если я изменю SQL, я могу запустить его в клиенте SQL, где он будет работать должным образом. [Примечание: я переместил расположение заказа]

UPDATE object2 SET obj2_date = date_sub(obj2_date, INTERVAL 1 month) 
WHERE object2.id IN (
    SELECT id FROM (
        SELECT object2.id FROM object2 INNER JOIN object1 ON object1.id = object2.object1_id WHERE <criteria>
    )  __active_record_temp
) **ORDER BY object2.obj2_date ASC**

Вопрос 1: Заказ добавляется не в то место. Как мне это исправить или как обойтись?

Я считаю, что это связано с этой ошибкой:
https://github.com/rails/rails/issues/6769

Вопрос 2: Почему это происходит? ... выберите идентификатор из (выберите идентификатор из таблицы) __temp_table ...

WHERE object2.id IN (
    SELECT id FROM (
        SELECT object2.id FROM object2 INNER JOIN object1 ON object1.id = object2.object1_id WHERE <criteria> **ORDER BY object2.obj2_date ASC**
    )  __active_record_temp
)

Разве не лучше было бы, чтобы это было так: ... выберите идентификатор из таблицы ...

WHERE object2.id IN (
    SELECT object2.id FROM object2 INNER JOIN object1 ON object1.id = object2.object1_id WHERE <criteria> 
)

Устранение необходимости во временной таблице только для получения идентификатора, когда он уже получает только идентификатор?

Спасибо.

Ответы [ 2 ]

1 голос
/ 18 июня 2020

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

LIMIT 18446744073709551615

позади вашего ЗАКАЗА mBY

UPDATE object2 SET obj2_date = date_sub(obj2_date, INTERVAL 1 month) 
WHERE object2.id IN (
    SELECT DISTINCT id FROM (
        SELECT object2.id,object2.obj2_date FROM object2 INNER JOIN object1 ON object1.id = object2.object1_id WHERE <criteria> 
    )  __active_record_temp
ORDER BY obj2_date ASC LIMIT 18446744073709551615
)

Причина, почему порядок игнорируется без LIMIT это просто, строки по определению неупорядочены, поэтому ORDER BY удаляется без ограничения,

Mysql позволяет ORDER BY нетронутым при особых обстоятельствах

Object2.joins( :object1 ).where( object1: { :id => id } ).order( obj2_date: ascdsc ).limit(18446744073709551615).update_all(
    "obj2_date = " + direction + "(obj2_date, INTERVAL " + difference.to_s + " month)")
0 голосов
/ 19 июня 2020

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

Это решает обе проблемы. Код:

Object2.where( id: __Object1.select( :id ).where( :parent_id => id )__ ).order( obj2_date: ascdsc ).update_all(
    "obj2_date = " + direction + "(obj2_date, INTERVAL " + difference.to_s + " month)")

Авто генерирует это SQL:

UPDATE object2 SET obj2_date = date_add(obj2_date, INTERVAL 1 month) 
WHERE object2.obj1_id IN (
    SELECT object1.id FROM object1 WHERE object1.parent_id = 15
) ORDER BY object2.obj2_date ASC
...