SQLite - UPSERT * не * Вставить или заменить

Отражение в C ++ очень полезно, в случаях вам нужно запустить какой-то метод для каждого члена (например: сериализация, хеширование, сравнение). Я пришел с общим решением с очень простым синтаксисом:

struct S1
{
    ENUMERATE_MEMBERS(str,i);
    std::string str;
    int i;
};
struct S2
{
    ENUMERATE_MEMBERS(s1,i2);
    S1 s1;
    int i2;
};

Где ENUMERATE_MEMBERS - это макрос, который описан ниже (UPDATE):

Предположим, что мы определили функцию сериализации для int и std :: string:

void EnumerateWith(BinaryWriter & writer, int val)
{
    //store integer
    writer.WriteBuffer(&val, sizeof(int));
}
void EnumerateWith(BinaryWriter & writer, std::string val)
{
    //store string
    writer.WriteBuffer(val.c_str(), val.size());
}

И у нас есть общая функция рядом с «секретным макросом»;)

template<typename TWriter, typename T>
auto EnumerateWith(TWriter && writer, T && val) -> is_enumerable_t<T>
{
    val.EnumerateWith(write); //method generated by ENUMERATE_MEMBERS macro
}

Теперь вы можете написать

S1 s1;
S2 s2;
//....
BinaryWriter writer("serialized.bin");

EnumerateWith(writer, s1); //this will call EnumerateWith for all members of S1
EnumerateWith(writer, s2); //this will call EnumerateWith for all members of S2 and S2::s1 (recursively)

Итак, имея макрос ENUMERATE_MEMBERS в определении структуры, вы можете создавать сериализацию, сравнение, хэширование и другие материалы без касания оригинального типа, единственное требование - внедрять метод «EnumerateWith» для каждого типа, который не перечислим, за каждый перечислитель (например, BinaryWriter). Обычно вам нужно реализовать 10-20 «простых» типов для поддержки любого типа в вашем проекте.

Этот макрос должен иметь нулевые служебные данные для создания / уничтожения структуры во время выполнения, а код T .EnumerateWith () должен генерироваться по требованию, что может быть достигнуто путем создания его встроенной функции шаблона, поэтому единственные накладные расходы во всей истории - это добавить ENUMERATE_MEMBERS (m1, m2, m3 ...) к каждой структуре, тогда как реализация определенного метода для каждого типа элемента является обязательным в любом решении, поэтому я не предполагаю, что это накладные расходы.

UPDATE: существует очень простая реализация макроса ENUMERATE_MEMBERS (однако его можно было бы немного расширить для поддержки наследование из перечислимой структуры)

#define ENUMERATE_MEMBERS(...) \
template<typename TEnumerator> inline void EnumerateWith(TEnumerator & enumerator) const { EnumerateWithHelper(enumerator, __VA_ARGS__ ); }\
template<typename TEnumerator> inline void EnumerateWith(TEnumerator & enumerator) { EnumerateWithHelper(enumerator, __VA_ARGS__); }

// EnumerateWithHelper
template<typename TEnumerator, typename ...T> inline void EnumerateWithHelper(TEnumerator & enumerator, T &...v) 
{ 
    int x[] = { (EnumerateWith(enumerator, v), 1)... }; 
}

// Generic EnumerateWith
template<typename TEnumerator, typename T>
auto EnumerateWith(TEnumerator & enumerator, T & val) -> std::void_t<decltype(val.EnumerateWith(enumerator))>
{
    val.EnumerateWith(enumerator);
}

И вам не нужна сторонняя библиотека для этих 15 строк кода;)

497
задан Samuel Liew 2 May 2018 в 22:46
поделиться

3 ответа

Я думаю, что это может быть тем, что Вы ищете: НА пункте .

КОНФЛИКТА, Если Вы определяете свою таблицу как это:

CREATE TABLE table1( 
    id INTEGER PRIMARY KEY ON CONFLICT REPLACE, 
    field1 TEXT 
); 

Теперь, если Вы делаете ВСТАВКУ с идентификатором, который уже существует, SQLite автоволшебно ОБНОВЛЯЕТ вместо ВСТАВКИ.

Hth...

1
ответ дан kmelvn 2 May 2018 в 22:46
поделиться

Лучший подход, который я знаю, должен сделать обновление, сопровождаемое вставкой. "Наверху выбора" необходимо, но это не ужасная нагрузка, так как Вы ищете на первичном ключе, который быстр.

необходимо быть в состоянии изменить ниже операторов с таблицей & имена полей, чтобы сделать то, что Вы хотите.

--first, update any matches
UPDATE DESTINATION_TABLE DT
SET
  MY_FIELD1 = (
              SELECT MY_FIELD1
              FROM SOURCE_TABLE ST
              WHERE ST.PRIMARY_KEY = DT.PRIMARY_KEY
              )
 ,MY_FIELD2 = (
              SELECT MY_FIELD2
              FROM SOURCE_TABLE ST
              WHERE ST.PRIMARY_KEY = DT.PRIMARY_KEY
              )
WHERE EXISTS(
            SELECT ST2.PRIMARY_KEY
            FROM
              SOURCE_TABLE ST2
             ,DESTINATION_TABLE DT2
            WHERE ST2.PRIMARY_KEY = DT2.PRIMARY_KEY
            );

--second, insert any non-matches
INSERT INTO DESTINATION_TABLE(
  MY_FIELD1
 ,MY_FIELD2
)
SELECT
  ST.MY_FIELD1
 ,NULL AS MY_FIELD2  --insert NULL into this field
FROM
  SOURCE_TABLE ST
WHERE NOT EXISTS(
                SELECT DT2.PRIMARY_KEY
                FROM DESTINATION_TABLE DT2
                WHERE DT2.PRIMARY_KEY = ST.PRIMARY_KEY
                );
3
ответ дан JosephStyons 2 May 2018 в 22:46
поделиться
  • 1
    Было бы замечательно, если можно объяснить, как получить это значение в названном действии? Спасибо. – Varundroid 5 April 2013 в 00:58

Если Вы обычно делаете обновления, я был бы..

  1. Начинаются, транзакция
  2. Делают обновление
  3. Проверка rowcount
  4. , Если это 0, делают вставку
  5. Фиксация

, Если Вы обычно делаете, вставляет, я был бы

  1. , Начинают транзакцию
  2. Попытка вставка
  3. Проверка на ошибку нарушения первичного ключа
  4. , если мы добрались, ошибка делают обновление
  5. Фиксация

Этот способ, которым Вы избегаете выбора, и Вы являетесь транзакционно звуковыми на Sqlite.

80
ответ дан Sam Saffron 2 May 2018 в 22:46
поделиться
Другие вопросы по тегам:

Похожие вопросы: