SELECT N строк до и после строки, соответствующей условию?

Поведение, которое я хочу реплицировать, похоже на grep с флагами -A и -B. например, grep -A 2 -B 2 "hello" myfile.txt даст мне все строки, которые имеют "привет" в них, но также 2 строки до и 2 строки после него. Предположим, что эта схема таблицы:

+--------+-------------------------+
| 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           |
+------------+---------------------+

Теперь запрос вроде: Select * from theTable where message like '%hello%' приведет к:

5 | hello world

Как я могу поместить другой параметр "N", который выбирает N строк раньше, и N строк после согласованной записи, т.е. для N = 2, результат должен быть:

    | 3      | My testing message      |
    | 4      | php module test         |
    | 5      | hello world             |
    | 6      | team spirit             |
    | 7      | puzzle game             |
  • Для простоты предположим, что "как% TERM%" соответствует только 1 строке.
  • Здесь результат должен быть отсортирован в поле auto-increment id.

Ответы

Ответ 1

Правильно, это работает для меня:

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;

Ответ 2

Не знаю, действительно ли это MySQL, но как насчет

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

Ответ 3

Попробуйте этот простой (отредактированный) -

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        |
+------+--------------------+

Значение N может быть указано как пользовательская переменная; в настоящее время это - "2".

Этот запрос работает с номерами строк, и это гарантирует возврат ближайших записей.

Ответ 4

Try

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

Ответ 5

(только MS SQL Server)

Самый надежный способ - использовать функцию row_number, так что не имеет значения, есть ли пробелы в id. Это также будет работать, если есть несколько случаев результата поиска и правильно вернуть два выше и ниже каждого результата.

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

Ответ 6

Добавление ответа с использованием даты вместо идентификатора. В данном случае это таблица поворота по вызову с одной записью в неделю. Из-за изменений идентификатор может быть неработоспособным для намеченной цели. Разумеется, любой прецедент, имеющий несколько записей pr week, pr date или другой, должен быть исправлен.

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

Запрос:

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)