Я пытаюсь обновить значения в таблице по результатам запроса на выборку. Проблема в том, что мне не разрешено ссылаться на самую внешнюю таблицу (обновляемую) в предложении Order By самой внутренней таблицы (таблица для выбора нового значения).

Предположим, у меня есть следующая таблица:

MustMatch   PreferredMatch  Old         New       
----------  --------------  ----------  ----------
0           Blue            Old blue              
1           Blue            Wrong matc            
0           Red             Unpreferre            
0           Blue            Preferred   

Я хочу заполнить столбец «Новый» на «Старый синий». Новое значение должно отличаться от старого, но соответствовать столбцу MustMatch. Следующий запрос сделает это:

UPDATE t 
SET New = (
    SELECT innerTable.Old 
    FROM t innerTable 
    WHERE innerTable.Old != t.Old 
        AND innerTable.MustMatch = t.MustMatch 
    LIMIT 1
) WHERE Old = "Old blue";

MustMatch   PreferredMatch  Old         New              
----------  --------------  ----------  -----------------
0           Blue            Old blue    Unpreferred match
1           Blue            Wrong matc                   
0           Red             Unpreferre                   
0           Blue            Preferred    

Теперь я хочу добавить предпочтение: я хочу добавить ORDER BY innerTable.PreferredMatch = t.PreferredMatch во внутренний запрос:

UPDATE t 
SET New = (
    SELECT innerTable.Old 
    FROM t innerTable 
    WHERE innerTable.Old != t.Old 
        AND innerTable.MustMatch = t.MustMatch 
    ORDER BY innerTable.PreferredMatch = t.PreferredMatch DESC 
    LIMIT 1
) WHERE Old = "Old blue";

Это вызывает ошибку Error: no such column: t.PreferredMatch.

Проблема заключается в ссылке на t. Предложение Order By работает так, как задумано, когда я

UPDATE t 
SET New = (
    SELECT innerTable.Old 
    FROM t innerTable 
    WHERE innerTable.Old != t.Old 
        AND innerTable.MustMatch = t.MustMatch 
    ORDER BY innerTable.PreferredMatch = 'Blue' DESC 
    LIMIT 1
) WHERE Old = "Old blue";

MustMatch   PreferredMatch  Old         New            
----------  --------------  ----------  ---------------
0           Blue            Old blue    Preferred match
1           Blue            Wrong matc                 
0           Red             Unpreferre                 
0           Blue            Preferred     

Почему мне не разрешено использовать таблицу t в предложении Order By, хотя я могу использовать его в предложении Where? Есть ли другой способ добиться этого?

0
Kevin van der Pol 6 Сен 2016 в 16:42

3 ответа

Лучший ответ

Если вместо

UPDATE test 
SET New = (
    SELECT innerTable.Old 
    FROM test innerTable 
    WHERE innerTable.Old != test.Old 
        AND innerTable.MustMatch = test.MustMatch 
    ORDER BY innerTable.PreferredMatch = test.PreferredMatch DESC 
    LIMIT 1
) WHERE Old = "Old blue";

Вы используете еще один уровень

UPDATE test 
SET New = (SELECT newValue FROM
    (SELECT innerTable.Old as newValue,
            innerTable.PreferredMatch = test.PreferredMatch as pick
    FROM test innerTable 
    WHERE innerTable.Old != test.Old 
        AND innerTable.MustMatch = test.MustMatch 
    ORDER BY pick DESC 
    LIMIT 1) foo
) WHERE Old = "Old blue";

Затем он работает (вы используете по порядку имя поля в select, а в select вы можете использовать поле из внешней таблицы, которая обновляется).

IMHO, это ошибка в sqlite - нет причин, по которым поле из внешней таблицы может использоваться в предложениях where и select, но не по порядку.

0
Marek Wieckowski 6 Сен 2016 в 14:40

Я удивлен, что ваша версия не работает. Вот решение грубой силы:

UPDATE t 
    SET New = COALESCE((SELECT it.Old 
                        FROM t it 
                        WHERE it.Old <> t.Old AND
                              it.MustMatch = t.MustMatch AND
                              it.PreferredMatch = t.PreferredMatch
                        LIMIT 1
                       ),
                       (SELECT it.Old 
                        FROM t it 
                        WHERE it.Old <> t.Old AND
                              it.MustMatch = t.MustMatch AND
                              it.PreferredMatch <> t.PreferredMatch
                        LIMIT 1
                       ))
WHERE Old = 'Old blue';

Или это может сработать:

UPDATE t 
    SET New = (SELECT COALESCE(MAX(CASE WHEN it.PreferredMatch = t.PreferredMatchit.Old 
                                        THEN t.Old
                                   END),
                               MAX(t.Old))
               FROM t it 
               WHERE it.Old <> t.Old AND
                     it.MustMatch = t.MustMatch AND
              );
0
Gordon Linoff 6 Сен 2016 в 14:31

Обратите внимание, что точно такой же запуск в postgresql просто работает:

create table test (MustMatch integer,
            PreferredMatch varchar,
            Old varchar,
            New varchar);
-- CREATE TABLE

insert into test (MustMatch, PreferredMatch, Old, New)
values
    (0           ,'Blue'            ,'Old blue'         ,null),
    (1           ,'Blue'            ,'Wrong matc'       ,null),
    (0           ,'Red'             ,'Unpreferre'       ,null),
    (0           ,'Blue'            ,'Preferred'        ,null);
-- INSERT 0 4

UPDATE test 
SET New = (
    SELECT innerTable.Old 
    FROM test innerTable 
    WHERE innerTable.Old != test.Old 
        AND innerTable.MustMatch = test.MustMatch 
    ORDER BY innerTable.PreferredMatch = test.PreferredMatch DESC 
    LIMIT 1
) WHERE Old = 'Old blue';
-- UPDATE 1

Это действительно похоже на ошибку sqlite - не должно быть разницы, где вы будете использовать внешнее поле в подзапросе.

0
Marek Wieckowski 6 Сен 2016 в 14:54