2009-02-18 3 views
17

Ho una tabella di compiti MySQL da eseguire, ogni riga contiene i parametri per una singola attività.
Ci sono molte app di lavoro (possibilmente su macchine diverse), che eseguono compiti in un ciclo.
Le app accedono al database utilizzando le API C native di MySQL.MySQL UPDATE e SELECT in un passaggio

Al fine di possedere un compito, un app fa qualcosa di simile:

  • genera un ID univoco globale (per semplicità, diciamo che è un numero)

  • UPDATE tasks
    SET guid = %d
    WHERE guid = 0 LIMIT 1

  • SELECT params
    FROM tasks
    WHERE guid = %d

  • Se l'ultima query restituisce una riga, abbiamo possederlo e hanno i parametri per eseguire

C'è un modo per ottenere lo stesso effetto (vale a dire 'possedere' una riga e ottenere i suoi parametri) in una singola chiamata al server?

risposta

0

Non so la parte chiamata singola, ma quello che stai descrivendo è una serratura. I blocchi sono un elemento essenziale dei database relazionali.

Non conosco le specifiche del blocco di una riga, la lettura e l'aggiornamento in MySQL, ma con un po 'di lettura dello mysql lock documentation è possibile eseguire tutti i tipi di manipolazioni basate su lock.

La documentazione postumi di locks ha un ottimo esempio che descrive esattamente cosa si vuole fare: bloccare la tabella, leggere la tabella, modificare la tabella.

+0

Le serrature sono un eccesso in questo caso. La soluzione sopra utilizza il blocco interno MySQL, quindi è molto più veloce. –

+0

Vedo il tuo punto .... non hai bisogno di serrature qui ... Non sono a conoscenza di una singola chiamata. – Daniel

6

È possibile creare una procedura che lo fa:

CREATE PROCEDURE prc_get_task (in_guid BINARY(16), OUT out_params VARCHAR(200)) 
BEGIN 

    DECLARE task_id INT; 

    SELECT id, out_params 
    INTO task_id, out_params 
    FROM tasks 
    WHERE guid = 0 
    LIMIT 1 
    FOR UPDATE; 

    UPDATE task 
    SET guid = in_guid 
    WHERE id = task_id; 

END; 

BEGIN TRANSACTION; 

CALL prc_get_task(@guid, @params); 

COMMIT; 
+1

Ho provato qualcosa di simile. In conclusione: le query SQL native sono molto più veloci delle stored procedure. –

+3

In MySQL non c'è molta differenza, ad esempio, in Oracle. Non è possibile farlo comunque in una singola query, ma la procedura è una chiamata al server, proprio come richiesto. Con un piccolo sforzo è possibile inserire BEGIN e COMMIT nella procedura, ma è meglio farlo dall'applicazione, per ogni evenienza. – Quassnoi

+0

Quindi in pratica stai dicendo che non è fattibile in una singola chiamata. Speravo che una qualche forma di INSERT INTO _tmp SELECT .. sarebbe stata abbastanza re da consentire una chiamata a mysql_store_results() in quanto consente mysql_affected_rows() –

1

Se siete alla ricerca di una singola query allora non può accadere. La funzione UPDATE restituisce specificamente solo il numero di elementi che sono stati aggiornati. Allo stesso modo, la funzione SELECT non altera una tabella, restituisce solo i valori.

L'utilizzo di una procedura lo trasforma in una singola funzione e può essere utile se il blocco è un problema. Se la tua più grande preoccupazione è il traffico di rete (ad esempio, passando troppe query), usa la procedura. Se il problema riguarda il sovraccarico del server (ad esempio: il DB sta lavorando troppo), il sovraccarico di una procedura potrebbe peggiorare le cose.

+0

La preoccupazione è davvero il sovraccarico del server. La mia speranza era che, dal momento che l'aggiornamento già 'seek() ed' alla riga, un SELECT sarebbe più facile per il server all'interno della stessa istruzione. –

+2

Sto anche cercando un singolo SELECT/UPDATE e ho trovato questa domanda. Non voglio però bloccarlo, ma per modificare la colonna LastAccessed nella riga su NOW() quando viene recuperata la riga – CashCow

+1

Ciao Paulo, puoi spiegare (o dare un collegamento) perché le procedure renderebbero le cose più lente per DB? Ho pensato che SP fosse più veloce: S – confiq

8

provare come questo

UPDATE `lastid` SET `idnum` = (SELECT `id` FROM `history` ORDER BY `id` DESC LIMIT 1); 

sopra il codice ha funzionato per me

+0

ha funzionato anche per me. Preferisco questo metodo. – Darius

+0

Grazie !!!!!!!!! –

+0

Questa condizione di gara è sicura –

0
UPDATE tasks 
SET guid = %d, params = @params := params 
WHERE guid = 0 LIMIT 1; 

Si tornerà 1 o 0, a seconda che i valori sono stati effettivamente modificati.

SELECT @params AS params; 

Questo semplicemente seleziona la variabile dalla connessione.

Da: here

1

Ho lo stesso problema esatto. Abbiamo finito per usare PostreSQL invece, e UPDATE ... RETURNING:

La clausola RETURNING opzionale provoca UPDATE per il calcolo e il valore di ritorno (s) sulla base di ogni riga in realtà aggiornato. È possibile calcolare qualsiasi espressione utilizzando le colonne della tabella e/o le colonne di altre tabelle citate in FROM. Vengono utilizzati i nuovi valori (post-aggiornamento) delle colonne della tabella. La sintassi dell'elenco RETURNING è identica a quella dell'elenco di output di SELECT.

Esempio: UPDATE 'my_table' SET 'status' = 1 WHERE 'status' = 0 LIMIT 1 RETURNING *;

Oppure, nel tuo caso: UPDATE 'tasks' SET 'guid' = %d WHERE 'guid' = 0 LIMIT 1 RETURNING 'params';

dispiace, so che questo non risponde alla domanda con MySQL, e potrebbe non essere facile passare solo a PostgreSQL, ma è il modo migliore che abbiamo trovato per farlo. Anche 6 anni dopo, MySQL continua a non supportare UPDATE ... RETURNING. Potrebbe essere aggiunto ad un certo punto in futuro, ma per ora MariaDB only has it for DELETE statements.

Edit: V'è un compito (bassa priorità) per add UPDATE ... RETURNING support to MariaDB.