SQLite Вмъкване и Замяна с условие

Не мога да разбера как да заявя SQLite. необходимо: 1) Заменете записа (първичния ключ), ако условието (сравнение на нови и стари записи в полета) 2) Вмъкнете запис, ако такъв запис не съществува в базата данни на първичния ключ.

Важното е, че трябва да работи много бързо! Не мога да изляза с ефективно запитване.

Редактиране.

MyInsertRequest - желаният израз.

Скрипт:

CREATE TABLE testtable (a INT PRIMARY KEY, b INT, c INT)

INSERT INTO testtable VALUES (1, 2, 3)

select * from testtable
1|2|3

-- Adds an entry, because the primary key is not
++ MyInsertRequest VALUES (2, 2, 3) {if c>4 then replace}

select * from testtable
1|2|3
2|2|3

-- Adds
++ MyInsertRequest VALUES (3, 8, 3) {if c>4 then replace}

select * from testtable
1|2|3
2|2|3
3|8|3

-- Does nothing, because such a record (from primary key field 'a') 
-- is in the database and none c>4
++ MyInsertRequest VALUES (1, 2, 3) {if c>4 then replace}

select * from testtable
1|2|3
2|2|3
3|8|3

-- Does nothing
++ MyInsertRequest VALUES (3, 34, 3) {if c>4 then replace}

select * from testtable
1|2|3
2|2|3
3|8|3

-- replace, because such a record (from primary key field 'a') 
-- is in the database and c>2
++ MyInsertRequest VALUES (3, 34, 1) {if c>2 then replace}

select * from testtable
1|2|3
2|2|3
3|34|1

person Ivan    schedule 29.08.2013    source източник
comment
Ами ако накрая искам да добавя VALUES (1, 2, 1)? Не мога да разбера критерии! Това ли е: добавете нов a, заменете, когато a съвпада и new.c е по-малко от old.c, не правете нищо друго?   -  person LS_ᴅᴇᴠ    schedule 29.08.2013


Отговори (3)


Не е ли ВМЪКНЕТЕ ИЛИ ЗАМЕНЕТЕ това, от което се нуждаете? напр. :

INSERT OR REPLACE INTO table (cola, colb) values (valuea, valueb)

Когато възникне УНИКАЛНО нарушение на ограничението, алгоритъмът REPLACE изтрива съществуващите редове, които причиняват нарушението на ограничението, преди да вмъкне или актуализира текущия ред и командата продължава да се изпълнява нормално.

Трябва да поставите условието в уникално ограничение на таблицата. Той автоматично ще създаде индекс, за да направи проверката ефективна.

e.g.

-- here the condition is on columnA, columnB
CREATE TABLE sometable (columnPK INT PRIMARY KEY,
    columnA INT,
    columnB INT,
    columnC INT,
    CONSTRAINT constname UNIQUE (columnA, columnB)
)

INSERT INTO sometable VALUES (1, 1, 1, 0);
INSERT INTO sometable VALUES (2, 1, 2, 0);

select * from sometable
1|1|1|0
2|1|2|0

-- insert a line with a new PK, but with existing values for (columnA, columnB)
-- the line with PK 2 will be replaced
INSERT OR REPLACE INTO sometable VALUES  (12, 1, 2, 6)

select * from sometable
1|1|1|0
12|1|2|6
person bwt    schedule 29.08.2013
comment
Необходимо условие за замяна, клаузата WHERE не работи с INSERT OR REPLACE, може да е някак различно зададено условие ... Гледах, че никога не е намерено, нямам много опит с SQL. - person Ivan; 29.08.2013
comment
Добавен е пример, условието е в таблицата, а не в заявката. Това е единственото решение, за което се сещам за SQLite. - person bwt; 29.08.2013
comment
Добре, мисля, че това, от което се нуждаете, е правилно MERGE изявление. Доколкото знам, не съществува за SQLite. Вероятно трябва да изтрия отговора, тъй като не е подходящ, съжалявам - person bwt; 29.08.2013

Ако приемем, че вашите изисквания са:

  • Вмъкване на нов ред, когато a не съществува;
  • Замяна на ред, когато a съществува и съществуващ c е по-голям от новия c;
  • Не правете нищо, когато a съществува и съществуващото c е по-малко или равно на ново c;

INSERT OR REPLACE отговаря на първите две изисквания.

За последното изискване единственият начин, който знам, за да направя INSERT неефективен, е предоставянето на празен набор от редове.

SQLite команда като следната, която ще свърши работата:

INSERT OR REPLACE INTO sometable SELECT newdata.* FROM
    (SELECT 3 AS a, 2 AS b, 1 AS c) AS newdata
    LEFT JOIN sometable ON newdata.a=sometable.a
    WHERE newdata.c<sometable.c OR sometable.a IS NULL;

Новите данни (3,2,1 в този пример) са LEFT JOINen с текущите данни в таблицата.

Тогава WHERE ще "деселектира" реда, когато новият c не е по-малък от съществуващия c, запазвайки го, когато редът е нов, т.е. sometable.* IS NULL.

person LS_ᴅᴇᴠ    schedule 29.08.2013
comment
Любопитно е, че това изглежда е около 40% по-бързо от прав INSERT OR REPLACE... - някаква идея защо може да е така? - person Cocowalla; 29.04.2017

Опитах другите отговори, защото също страдах от решение на този проблем.

Това трябва да работи, но не съм сигурен за последиците от производителността. Вярвам, че може да се наложи първата колона да бъде уникална като първичен ключ, иначе просто ще вмъква нов запис всеки път.

INSERT OR REPLACE INTO sometable
    SELECT columnA, columnB, columnC FROM (
    SELECT columnA, columnB, columnC, 1 AS tmp FROM sometable
        WHERE sometable.columnA = 1 AND
              sometable.columnB > 9
    UNION
        SELECT 1 AS columnA, 1 As columnB, 404 as columnC, 0 AS tmp)
    ORDER BY tmp DESC
    LIMIT 1

В този случай една фиктивна заявка се изпълнява и обединява към втора заявка, което би имало въздействие върху производителността в зависимост от това как е написана и как е индексирана таблицата. Следващият проблем с производителността има потенциал, когато резултатите са подредени и ограничени. Въпреки това очаквам, че втората заявка трябва да върне само един запис и следователно не трябва да бъде твърде голям удар в производителността.

Можете също така да пропуснете ORDER BY tmp LIMIT 1 и той работи с моята версия на sqlite, но може да повлияе на производителността, тъй като в крайна сметка може да актуализира записа два пъти (записвайки първоначалната стойност, след това новата стойност, ако е приложимо).

Другият проблем е, че в крайна сметка получавате запис в таблицата, дори ако условието гласи, че тя не трябва да се актуализира.

person kmcguire    schedule 27.07.2015