Обновление нескольких столбцов в конфликте postgres - PullRequest
0 голосов
/ 30 апреля 2019

Мне нужно написать запрос на обновление записи, если она существует, иначе вставьте ее.Я делаю это обновление / вставку в базу данных postgres.Я посмотрел примеры upsert, и большинство из них используют максимум два поля для обновления.Однако я хочу обновить несколько столбцов.Пример:

query="""INSERT INTO table (col1,col2,col3,col4,col5,col6,col7,col8,col9,..col20) VALUES(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
ON CONFLICT(col2) DO UPDATE SET (col1,col2,col3,col4,col5,col6,col7,col8,col9,..col20) VALUES(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)"""

В приведенном выше запросе предположим, что col2 - это уникальный ключ, я вставляю и обновляю одинаковое количество столбцов.Я должен выполнить этот запрос, используя pymysql (библиотека Python).В простом операторе вставки я знаю, как динамически передавать кортеж, содержащий параметры.

cursor.execute (insert_query, data_tuple)

Но в этом случае у меня есть оба ввода (вставка и обновление) для динамического ввода.Учитывая вышеупомянутый запрос upsert, способ, которым я передаю параметры курсоруошибка с количеством аргументов в функции execute.Так как мне пройти?Более того, я пытаюсь использовать этот способ для передачи параметров, потому что использование присваивания (=) было бы трудоемким делом для 20 столбцов.

Есть ли другой альтернативный способ сделать это?Как простое выражение «заменить в» в mysql.

Ответы [ 2 ]

2 голосов
/ 30 апреля 2019

Прямой ответ на ваш вопрос: вы делаете tuple + tuple, чтобы удвоить кортеж.

cursor.execute(upsert_query, data_tuple + data_tuple)

Другие параметры:

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

query="""INSERT INTO table (col1,col2,col3,col4,col5,col6,col7,col8,col9,..col20) VALUES(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
ON CONFLICT(col2) DO UPDATE SET col1=%s, col3=%s, col4=%s, ..."""

cur.execute(query, (c1, c2, c3, ... c20, c1, c3, c4, ... c20))

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

Если у вас уже есть кортеж, о чем вы изначально просили, то вы будете использовать + для объединения одного кортежа дважды.

Если у вас есть отдельные значения, а некортеж, вы также можете использовать именованные параметры, такие как словарь.

query="""INSERT INTO table (col1,col2,col3,col4...) VALUES(%(c1)s, %(c2)s, %(c3)s, %(c4)s...) ON CONFLICT(col2) DO UPDATE SET col1=%(c1)s, col3=%(c3)s, col4=%(c4)s, ..."""
cur.execute(query, {'c1': c1val, 'c2': c2val, 'c3': c3val, 'c4': c4val, ...})

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

1 голос
/ 30 апреля 2019

РЕДАКТИРОВАТЬ 2

Итак, после нескольких обменов: похоже, ваша проблема в том, как использовать функцию cursor.execute в pymysql.Это ссылка на соответствующую документацию: https://pymysql.readthedocs.io/en/latest/modules/cursors.html

Я никогда не пишу код на python, но документация кажется довольно точной по использованию метода execute:

execute(query, args=None)

Execute a query
Parameters: 

    query (str) – Query to execute.
    args (tuple, list or dict) – parameters used with query. (optional)

Returns:    

Number of affected rows
Return type:    

int

If args is a list or tuple, %s can be used as a placeholder in the query. If args is a dict, %(name)s can be used as a placeholder in the query.

Так что возможно с 'dict'Возможно, но я не думаю, что это философия этого.

Оригинальный пост

Я не совсем уверен, что именно вы хотите сказать"оба места ввода должны быть динамическими", поэтому я опишу здесь несколько SQL-кодов, и не стесняйтесь, если у вас возникнут вопросы после:)

Сначала небольшая инициализация

CREATE TABLE test 
( 
    id int,
    value_1 varchar,
    value_2 bit
);

ALTER TABLE test
ADD CONSTRAINT ck_test UNIQUE(id, value_1, value_2);


INSERT INTO test
VALUES
    (1, 'test', cast(1 as bit))
    , (2, 'test_2', cast(0 as bit));

Вторая ошибка

INSERT INTO test
VALUES
    (1, 'test', cast(1 as bit));

В-третьих, UPSERT

INSERT INTO test
VALUES 
    (1, 'test', cast(1 as bit))
ON CONFLICT ON CONSTRAINT ck_test
DO
    UPDATE 
        SETid = 3, value_1 = 'error';

Это ответ на ваш вопрос?Или это скорее проблема построения строк?

EDIT Итак, мне не нравятся альтернативные языки, поэтому я положу это в plpgsql:

do language plpgsql $$
declare 
    query varchar;
    id_insert int;
    value_1_insert varchar;
    value_2_insert bit;
    id_update int;
    value_1_update varchar;
    value_2_update bit;
begin
    id_insert := 4;
    value_1_insert := 'test';
    value_2_insert := cast(1 as bit);

    id_update := id_insert;
    value_1_update := 'error';
    value_2_update := cast(0 as bit);

    query := 'INSERT INTO test
                VALUES 
                    (
                        cast('||id_insert||' as int)
                        , '''||value_1_insert||'''
                        , cast('||value_2_insert||' as bit)
                    )
                ON CONFLICT ON CONSTRAINT ck_test
                DO
                    UPDATE 
                        SET 
                            id = cast('||id_update||' as int)
                            , value_1 = '''||value_1_update||'''
                            , value_2 =  cast('||value_2_update||' as bit);';
    execute query;
end;
$$;

Надеюсь, этопомогает;)

...