SQLite INSERT - ON DUPLICATE KEY UPDATE (UPSERT)


98

O MySQL tem algo assim:

INSERT INTO visits (ip, hits)
VALUES ('127.0.0.1', 1)
ON DUPLICATE KEY UPDATE hits = hits + 1;

Pelo que eu sei, esse recurso não existe no SQLite, o que eu quero saber é se há alguma maneira de obter o mesmo efeito sem ter que executar duas consultas. Além disso, se isso não for possível, o que você prefere:

  1. SELECT + (INSERT ou UPDATE) ou
  2. UPDATE (+ INSERT se UPDATE falhar )

Respostas:



117
INSERT OR IGNORE INTO visits VALUES ($ip, 0);
UPDATE visits SET hits = hits + 1 WHERE ip LIKE $ip;

Isso requer que a coluna "ip" tenha uma restrição UNIQUE (ou PRIMARY KEY).


EDIT: Outra ótima solução: https://stackoverflow.com/a/4330694/89771 .


2
Apenas para registro, REPLACEnão é uma opção.
Alix Axel

1
Com relação ao link "outra grande solução", eu também consideraria uma resposta diferente para a mesma pergunta: stackoverflow.com/a/418988/3650835
KayakinKoder


7

A resposta atual só funcionará em sqlite OR mysql (dependendo se você usa OR ou não). Então, se você quiser compatibilidade cross dbms, o seguinte servirá ...

REPLACE INTO `visits` (ip, value) VALUES ($ip, 0);

3
A resposta aceita funciona no SQLite (esse era o meu objetivo). REPLACEfuncionará no SQLite também, mas no MySQL sempre zera o contador para 0 - embora a consulta seja portátil, o resultado final será muito diferente.
Alix Axel

Você está certo, pensei que o OP estava procurando por algo que fosse portátil. Sei que REPLACE INTO não funcionará com todos os casos, especialmente onde a preservação de PK é necessária, mas funcionará em muitos casos.
Jacob Thomason

Falha limpa em vez de descartar dados é um recurso, não um bug.
Tobu,

-4

Você deve usar memcached para isso, pois é uma única chave (o endereço IP) que armazena um único valor (o número de visitas). Você pode usar a função de incremento atômico para garantir que não haja condições de "corrida".

É mais rápido que o MySQL e economiza carga para que o MySQL possa se concentrar em outras coisas.


Se os dados não forem tão importantes, sim. No entanto, se isso estiver sendo usado em um site ocupado onde muitos IPs estão acessando o serviço, as instâncias do memcached podem ficar cheias e fazer com que algum conteúdo seja descartado. Fazer backup do conteúdo do memcached também seria interessante (se necessário).
Elliot Foster

@ElliotFoster Memcached pode lidar com tantos dados quanto a RAM que você joga nele (se você quiser persitência, use redis ou membase). Se você estiver recebendo mais de 1 milhão de visitantes por dia, provavelmente poderá dar à sua instância do memcache mais de 30 MB de RAM (que é o padrão, eu acho). No entanto, ele certamente pode lidar com uma carga muito maior do que o SQLite e o MySQL para a quantidade de memória que você fornece - simplesmente não há comparação.
Xeoncross

Não confunda meu comentário com um voto contra o memcache, pois acho que é uma ferramenta fantástica. As is redis (não posso falar em nome do membase, pois não o usei). No entanto, memcache / redis não são os armazenamentos mais confiáveis. Sim, o redis tem persistência, mas os dados são persistidos no disco em um intervalo (última consulta) e o memcache não. Como eu disse, se os dados não forem importantes (ou puderem ser reproduzidos facilmente), o memcache e a empresa são ótimos. A postagem original também perguntava sobre sqlite, que é muito diferente do MySQL e provavelmente significa que eles são limitados de outras maneiras.
Elliot Foster,

Você pode configurar o Redis para persistir os dados tão rápido quanto você deseja (no número X de segundos ou no número Y de alterações).
Buffalo
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.