MYSQL Seleciona linha quando a primeira parte da condição é válida (com um grupo de) - mysql, sql

Realmente não sei como encontrar uma resposta para minha pergunta, então estou lhe perguntando.

Aqui está a tabela que tenho:

+----+------------+------------+-------------+
| id | start_date |  end_date  |  id_person  |
+----+------------+------------+-------------+
| 1  | 2017-10-01 | 2017-12-01 |      1      |
| 2  | 2017-07-01 | 2017-09-01 |      1      |
| 3  | 2016-01-01 | 2016-02-01 |      1      |
| 4  | 2016-05-01 | 2016-06-01 |      2      |
| 5  | 2016-01-01 | 2016-02-01 |      2      |
+----+------------+------------+-------------+

E aqui está a consulta que tentei usar:

SELECT * FROM table
WHERE ((start_date < NOW() AND end_date > NOW())
OR start_date > NOW()
OR end_date < NOW())
GROUP BY `id_person`

O resultado que eu esperava era este:

+----+------------+------------+-------------+
| id | start_date |  end_date  |  id_person  |
+----+------------+------------+-------------+
| 2  | 2017-07-01 | 2017-09-01 |      1      | // matches first condition
| 4  | 2016-05-01 | 2016-06-01 |      2      | // matches 3rd condition and has the most recent start_date
+----+------------+------------+-------------+

Se você ainda não entendeu o que fiz de errado, vou lhe contar.

Aqui, eu estava tentando mostrar uma única linha porpessoa, mas eu queria que essa linha correspondesse à primeira condição que encontrar e não às outras, não quero que a linha seja ordenada apenas por data_de_início. É como um pedido personalizado no qual desejo a primeira linha para cada pessoa.

O problema é que essa consulta não funciona, pois a instrução GROUP BY não aplica condições primeiro. (mesmo que sim, não tenho certeza se a condição selecionaria apenas uma linha)

Realmente não sei como conseguir isso e nem sei se é possível, espero que alguém possa me levar a qualquer solução.

Obrigado por ler isso, responderei o mais rápido possível para fornecer mais informações.

Respostas:

0 para resposta № 1

Aqui está uma idéia ...

SELECT m.*
FROM my_table m
JOIN
( SELECT x.id_person
, MAX(x.start_date) start_date
FROM my_table x
JOIN
( SELECT id_person
, MIN(CASE WHEN NOW() BETWEEN start_date AND end_date THEN "A" WHEN start_date > NOW() THEN "B" WHEN end_date < NOW() THEN "C" END) rule
FROM my_table
GROUP
BY id_person
) y
ON y.id_person = x.id_person
AND y.rule = CASE WHEN NOW() BETWEEN start_date AND end_date THEN "A" WHEN start_date > NOW() THEN "B" WHEN end_date < NOW() THEN "C" END
GROUP
BY id_person
) n
ON n.id_person = m.id_person
AND n.start_date = m.start_date;

+----+------------+------------+-----------+
| id | start_date | end_date   | id_person |
+----+------------+------------+-----------+
|  2 | 2017-07-01 | 2017-09-01 |         1 |
|  4 | 2016-05-01 | 2016-06-01 |         2 |
+----+------------+------------+-----------+

0 para resposta № 2

Se você tiver prazer em escrever as regras diretamente no sql, e não como onde as condições, poderá solicitar ao banco de dados o que deseja mais diretamente.

Isso significa dar um passo atrás para ver o que oregras que você deseja são. Parece que você deseja priorizar as entradas pela data mais próxima, mostrando primeiro atual, depois futuro e depois histórico. Também se parece com end_date> = start_date, o que significa que você só precisa olhar end_date para encontrar o que está procurando.

O Mysql pode responder à pergunta que está abusando do grupo por funcionalidade (até versões recentes).

SELECT t.* FROM
(
SELECT t.*
FROM table t
ORDER BY SIGN(t.end_date - NOW()),ABS(t.end_date-NOW())
)
GROUP BY t.id_person

Um método sql padrão que também funcionará melhor com índices seria procurar datas de término antes e depois de hoje separadamente.

SELECT t.*
FROM table t
JOIN (
SELECT t.person_id
,COALESCE(first_not_ended.end_date,t.last_ended.end_date) AS end_date
FROM table t
LEFT JOIN (
SELECT t.*,MIN(end_date) AS end_date
FROM table t
WHERE t.end_date > NOW()
GROUP by t.person_id
) first_not_ended
ON t.person_id=first_not_ended.person_id
AND t.end_date=first_not_ended.end_ate
LEFT JOIN (
SELECT t.person_id,MAX(end_date) AS end_date
FROM table t
WHERE t.end_date < NOW()
GROUP by t.person_id
) last_ended
ON t.person_id=last_ended.person_id
AND t.end_date=last_ended.end_date
) closest
ON t.person_id=closest.person_id
AND t.end_date=closest.end_date

Cardápio