Eu tenho algumas tabelas com números como este (status é FREE ou ASSIGNED)
status do número id_set ----------------------- 1 000001 ATRIBUÍDO 1 000002 GRÁTIS 1 000003 ATRIBUÍDO 1 000004 GRÁTIS 1 000005 GRÁTIS 1 000006 ATRIBUÍDO 1 000007 ATRIBUÍDO 1 000008 GRÁTIS 1 000009 GRÁTIS 1 000010 GRÁTIS 1 000011 ATRIBUÍDO 1 000012 ATRIBUÍDO 1 000013 ATRIBUÍDO 1 000014 GRÁTIS 1 000015 ATRIBUÍDO
e eu preciso encontrar "n" números consecutivos, então para n = 3, a consulta retornaria
1 000008 GRÁTIS 1 000009 GRÁTIS 1 000010 GRÁTIS
Deve retornar apenas o primeiro grupo possível de cada id_set (na verdade, seria executado apenas para id_set por consulta)
Eu estava verificando as funções do WINDOW, tentei algumas consultas como COUNT(id_number) OVER (PARTITION BY id_set ROWS UNBOUNDED PRECEDING)
, mas foi tudo o que consegui :) Não consegui pensar em lógica, como fazer isso no Postgres.
Eu estava pensando em criar uma coluna virtual usando funções WINDOW contando linhas anteriores para cada número em que status = 'FREE' e, em seguida, selecione o primeiro número, onde contagem é igual ao meu número "n".
Ou talvez agrupe números por status, mas apenas de um ASSIGNED para outro ASSIGNED e selecione apenas grupos contendo pelo menos "n" números
EDITAR
Eu encontrei esta consulta (e mudei um pouco)
WITH q AS
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY id_set, status ORDER BY number) AS rnd,
ROW_NUMBER() OVER (PARTITION BY id_set ORDER BY number) AS rn
FROM numbers
)
SELECT id_set,
MIN(number) AS first_number,
MAX(number) AS last_number,
status,
COUNT(number) AS numbers_count
FROM q
GROUP BY id_set,
rnd - rn,
status
ORDER BY
first_number
que produz grupos de números FREE/ASSIGNED, mas gostaria de ter todos os números apenas do primeiro grupo que atende à condição
Este é um problema de lacunas e ilhas . Supondo que não haja lacunas ou duplicatas no mesmo
id_set
conjunto:Aqui está um link de demonstração do SQL Fiddle * para esta consulta: http://sqlfiddle.com/#!1/a2633/1 .
ATUALIZAR
Para retornar apenas um conjunto, você pode adicionar mais uma rodada de classificação:
Aqui está uma demonstração para este também: http://sqlfiddle.com/#!1/a2633/2 .
Se você precisar fazer um conjunto por
id_set
, altere aRANK()
chamada assim:Além disso, você pode fazer com que a consulta retorne o menor conjunto correspondente (ou seja, primeiro tente retornar o primeiro conjunto de exatamente três números consecutivos, se existir, caso contrário, quatro, cinco etc.), assim:
ou assim (um por
id_set
):* As demos do SQL Fiddle vinculadas nesta resposta usam a instância 9.1.8, pois a 9.2.1 não parece estar funcionando no momento.
Uma variante simples e rápida :
Requer uma sequência sem intervalos de números
number
(conforme fornecido na pergunta).Funciona para qualquer número de valores possíveis em
status
além'FREE'
, mesmo comNULL
.A principal característica é subtrair depois
row_number()
denumber
eliminar as linhas não qualificadas. Números consecutivos terminam no mesmogrp
– egrp
também é garantido que estejam em ordem crescente .Então você pode
GROUP BY grp
e contar os membros. Como você parece querer a primeira ocorrênciaORDER BY grp LIMIT 1
e obtém a posição inicial e o comprimento da sequência (pode ser >= n ).Conjunto de linhas
Para obter um conjunto real de números, não procure a tabela outra vez. Muito mais barato com
generate_series()
:Se você realmente deseja uma string com zeros à esquerda como você exibe em seus valores de exemplo, use
to_char()
com oFM
modificador (modo de preenchimento):SQL Fiddle com caso de teste estendido e ambas as consultas.
Resposta intimamente relacionada:
Esta é uma maneira bastante genérica de fazer isso.
Tenha em mente que depende de sua
number
coluna ser consecutiva. Se não for uma função de janela e/ou solução de tipo CTE provavelmente será necessária:Isso retornará apenas o primeiro dos 3 números. Não requer que os valores de
number
sejam consecutivos. Testado no SQL-Fiddle :E isso mostrará todos os números (onde houver 3 ou mais
'FREE'
posições consecutivas):Neste caso 5 números consecutivos - portanto a diferença deve ser 4 ou em outras palavras
count(r3.number) = n
er2.number = r1.number + n - 1
.Com junções: