2011-11-15 11 views
15

Il comportamento che voglio replicare è come grep con le bandiere -A e -B. ad esempio grep -A 2 -B 2 "hello" myfile.txt mi darà tutte le linee che hanno "Ciao" in esse, ma anche 2 righe prima e 2 righe dopo di essa. Assumiamo questo schema di tabella:SELEZIONA N righe prima e dopo la riga che corrisponde alla condizione?

+--------+-------------------------+ 
| id  | message    | 
+--------+-------------------------+ 
| 1  | One tow three   | 
| 2  | No error in this  | 
| 3  | My testing message  | 
| 4  | php module test   | 
| 5  | hello world    | 
| 6  | team spirit    | 
| 7  | puzzle game    | 
| 8  | social game    | 
| 9  | stackoverflow   | 
|10  | stackexchange   | 
+------------+---------------------+ 

Ora una query come: Select * from theTable where message like '%hello%' si tradurrà in:

5 | hello world

Come posso mettere un altro parametro "N" che seleziona N righe prima, e N righe dopo il record corrispondente, ovvero per N = 2, il risultato dovrebbe essere:

| 3  | My testing message  | 
    | 4  | php module test   | 
    | 5  | hello world    | 
    | 6  | team spirit    | 
    | 7  | puzzle game    | 
  • Per semplicità, supponiamo che "come% TERM%" corrisponda solo a 1 riga.
  • Qui il risultato deve essere ordinato sul campo di incremento automatico id.
+0

almeno specificare un ordinamento .. –

+0

hi yi_H, la sua specificato come ultimo punto nella questione – DhruvPathak

+0

sarà la tua prima dichiarazione/interrogazione sempre restituire solo una riga? o può restituire anche più righe? –

risposta

11

destro, questo funziona per me:

SELECT child.* 
FROM stack as child, 
(SELECT idstack FROM stack WHERE message LIKE '%hello%') as parent 
WHERE child.idstack BETWEEN parent.idstack-2 AND parent.idstack+2; 
+8

Questo potrebbe non funzionare se ci sono degli spazi tra gli ID, altrimenti sarebbe una soluzione semplice. –

+0

Supponendo che l'ID sia incrementale, questo funziona in parte, non restituisce i valori ID corretti: http://sqlize.com/i3UzLmm9u8 – mellamokb

+0

occorrono 2 join aggiuntivi con limite di N questo. Il mio in effetti non presuppone lacune –

6

Non so se questo è affatto MySQL valido, ma come circa

SELECT t.* 
FROM theTable t 
     INNER JOIN (
      SELECT id FROM theTable where message like '%hello%' 
     ) id ON id.id <= t.id 
ORDER BY 
     ID DESC 
LIMIT 3      
UNION ALL 
SELECT t.* 
FROM theTable t 
     INNER JOIN (
      SELECT id FROM theTable where message like '%hello%' 
     ) id ON id.id > t.id 
ORDER BY 
     ID 
LIMIT 2 
+0

Che dovrebbe essere id 'id ON t.id <= id.id 'e' id ON t.id> id.id' (dato che gli alias sono chiamati 'id', altrimenti stai confrontando' t.id' su se stesso), quindi funziona: http://sqlize.com/fIR8gPZ1CO – mellamokb

+0

@mellamokb - Grazie, ho corretto l'errore –

2

Prova

Select * from theTable 
Where id >= 
(Select id - variableHere from theTable where message like '%hello%') 
Order by id 
Limit (variableHere * 2) + 1 
+0

Non credo tu possa [usare le variabili nella clausola 'limite'] (http://bugs.mysql.com/bug.php?id=11918): http: //sqlize.com/3Q9z45906z – mellamokb

+0

Non so se MySql è in grado di consentire la variabile, io uso semplicemente la variabile wordHere lassamente lì.Ancora se non lo è, usa la query parametrica della libreria di accesso ai dati del tuo livello medio per facilitare passa la tua variabile lì. Non utilizzare la concatenazione –

+0

se stai eseguendo la query in una lingua tuttavia e la variabile limite è di quel linguaggio, mysql non avrà problemi con esso. ad esempio: '$ limit = 10; $ query =" Seleziona id da limite LIMIT LIMIT temporaneo ";' –

5

Try questo semplice (modificato) -

CREATE TABLE messages(
    id INT(11) DEFAULT NULL, 
    message VARCHAR(255) DEFAULT NULL 
); 

INSERT INTO messages VALUES 
    (1, 'One tow three'), 
    (2, 'No error in this'), 
    (3, 'My testing message'), 
    (4, 'php module test'), 
    (5, 'hello world'), 
    (6, 'team spirit'), 
    (7, 'puzzle game'), 
    (8, 'social game'), 
    (9, 'stackoverflow'), 
    (10, 'stackexchange'); 

SET @text = 'hello world'; 

SELECT id, message FROM (
    SELECT m.*, @n1:[email protected] + 1 num, @n2:=IF(message = @text, @n1, @n2) pos 
    FROM messages m, (SELECT @n1:=0, @n2:=0) n ORDER BY m.id 
) t 
WHERE @n2 >= num - 2 AND @n2 <= num + 2; 

+------+--------------------+ 
| id | message   | 
+------+--------------------+ 
| 3 | My testing message | 
| 4 | php module test | 
| 5 | hello world  | 
| 6 | team spirit  | 
| 7 | puzzle game  | 
+------+--------------------+ 

Il valore N può essere specificato come variabile utente; attualmente è - '2'.

Questa query funziona con i numeri di riga e ciò garantisce che verranno restituiti i record più vicini.

+0

Ciao, credo questo è quello che cerco, ma non capisco ancora con la funzione di @ n2. Puoi spiegare di più? La colonna num è per il numero di riga giusto? ma qual è la colonna pos? – dieend

+0

@ n2 è una variabile, la funzione di controllo IF imposta il valore @ n1 quando il campo 'message' è uguale a @text, altrimenti non fa nulla - imposta solo il suo valore. – Devart

+0

Quando lo sto provando,: n2 setta su 0 per tutte le righe prima che la riga con messaggio di campo sia uguale a: testo, quindi WHERE non funzionerà correttamente per ottenere una riga prima della riga con: testo. Ad esempio la riga con: testo alla 4a riga. : n2 nella prima e terza riga avrà valore 0. Quindi la riga 3 non verrà selezionata come riga precedente, poiché pos> = num-2 -> 0> = 3-2 è false, mentre in realtà è una riga prima della 4a riga. CMIIW – dieend

1

(MS SQL Server)

Il modo più affidabile sarebbe quello di utilizzare la funzione di row_number in questo modo non importa se ci sono lacune nella id. Ciò funzionerà anche se ci sono più occorrenze del risultato della ricerca e correttamente restituire due sopra e sotto ogni risultato.

WITH 

srt AS (
    SELECT ROW_NUMBER() OVER (ORDER BY id) AS int_row, [id] 
    FROM theTable 
), 

result AS (
    SELECT int_row - 2 AS int_bottom, int_row + 2 AS int_top 
    FROM theTable 
     INNER JOIN srt 
      ON theTable.id = srt.id 
    WHERE ([message] like '%hello%') 
) 

SELECT theTable.[id], theTable.[message] 
FROM theTable 
    INNER JOIN srt 
     ON theTable.id = srt.id 
    INNER JOIN result 
     ON srt.int_row >= result.int_bottom 
     AND srt.int_row <= result.int_top 
ORDER BY srt.int_row 
+0

oops ha appena capito che la tua domanda è stata taggata mysql. Questo metodo è per SQL Server. Scusate. – David

0

Aggiunta di una risposta utilizzando la data anziché un ID. Il caso d'uso qui è una tabella di rotazione a chiamata con una settimana di registrazione. A causa di modifiche, l'ID potrebbe essere fuori servizio per lo scopo previsto. Qualsiasi caso d'uso con più registrazioni pr settimana, data pr o altro dovrà naturalmente essere riparato.

+----------+--------------+------+-----+---------+----------------+ 
| Field | Type   | Null | Key | Default | Extra   | 
+----------+--------------+------+-----+---------+----------------+ 
| id  | int(11)  | NO | PRI | NULL | auto_increment | 
| startdate| datetime  | NO |  | NULL |    | 
| person | int(11)  | YES | MUL | NULL |    | 
+----------+--------------+------+-----+---------+----------------+ 

La query:

SELECT child.* 
FROM rota-table as child, 
(SELECT startdate 
    FROM rota-table 
    WHERE YEARWEEK(startdate, 3) = YEARWEEK(now(), 3)) as parent 
WHERE 
    YEARWEEK(child.startdate, 3) >= YEARWEEK(NOW() - INTERVAL 25 WEEK, 3) 
    AND YEARWEEK(child.startdate, 3) <= YEARWEEK(NOW() + INTERVAL 25 WEEK, 3) 
Problemi correlati