Oracle se junta (externo esquerdo, à direita, etc.: s)
Pergunta
Eu sabia que o Stackoverflow me ajudaria além de saber qual é o "desenho de programação favorito": P
Esta foi a resposta aceita por:Bill Karwin
Obrigado a todos pela ajuda (eu gostaria de votar em dobro de todos vocês)
Minha consulta acabou assim (esta é a verdadeira)
SELECT
accepted.folio,
COALESCE( inprog.activityin, accepted.activityin ) as activityin,
inprog.participantin,
accepted.completiondate
FROM performance accepted
LEFT OUTER JOIN performance inprog
ON( accepted.folio = inprog.folio
AND inprog.ACTIVITYIN
IN ( 4, 435 ) -- both are ids for inprogress
AND inprog.PARTICIPANTIN != 1 ) -- Ignore the "bot" participant
LEFT OUTER JOIN performance closed
ON( accepted.folio = closed.folio
AND closed.ACTIVITYIN IN ( 10,436, 4, 430 ) ) -- all these are closed or cancelled
WHERE accepted.ACTIVITYIN IN ( 3, 429 ) --- both are id for new
AND accepted.folio IS NOT NULL
AND closed.folio IS NULL;
Agora eu só tenho que me juntar às outras tabelas para um relatório legível por humanos.
Postagem original
Olá.
Estou lutando por cerca de 6 horas. Agora com uma consulta de banco de dados (meu inimigo de longa data)
Eu tenho uma tabela de dados com alguns campos como:
table performance(
identifier varchar,
activity number,
participant number,
closedate date,
)
É usado para acompanhar a história do tíquete
Identificador: é um ID de cliente como (NAF0000001)
atividade: é um FK de onde o bilhete está (novo, in_progress, rejeitado, fechado, etc)
participante: é um FK de quem está participando naquele momento o bilhete
data de encerramento: é a data em que essa atividade terminou.
EDITAR: Eu deveria ter dito "conclusão" em vez de fechado. Esta é a data em que a atividade foi concluída, não é necessária quando o bilhete foi fechado.
Por exemplo, uma história típica pode ser assim:
identifier|activity|participant|closedate ------------------------------------------- NA00000001| 1| 1|2008/10/08 15:00| ------------------------------------------- NA00000001| 2| 2|2008/10/08 15:20| ------------------------------------------- NA00000001| 3| 2|2008/10/08 15:40| ------------------------------------------- NA00000001| 4| 4|2008/10/08 17:05| -------------------------------------------
E participante 1 = Jonh, 2 = Scott, 3 = Mike, 4 = Rob
e Activties 1 = novo, 2 = inprogress, 3 = WaitingForAproval, 4 = fechado
etc. e dezenas de outras informações irrelevantes.
Bem, meu problema é o seguinte.
Consegui criar uma consulta onde posso saber quando um ingresso foi aberto e fechado
é assim:
select
a.identifier,
a.participant,
a.closedate as start,
b.closedate as finish
from
performance a,
performance b
where
a.activity = 1 -- new
and b.activity = 4 -- closed
and a.identifier = b.identifier
Mas não sei saber quais são os ingressos não Fechado e quem os está atendendo.
Até agora eu tenho algo assim:
select
a.identifier,
a.participant,
a.closedate as start
from
performance a
where
a.activity = 1 -- new
and a.identifier not in ( select identifier from performance where activity = 4 ) --closed
Isso é me dê todos os que têm um começo (novo = 1), mas não estão fechados (fechados = 4)
Mas o grande problema aqui é que ele imprime o participante que abriu o bilhete, mas preciso do participante que o está participando. Então, eu adiciono a atividade "inprogressista" à consulta.
select
a.identifier,
a.participant,
a.closedate as start
from
performance a,
performance b
where
a.activity = 1 -- new
and a.identifier not in ( select identifier from performance where activity = 4 ) --closed
and b.identifier = a.identifier
and b.activity = 2 -- inprogress..
Mas nem todas as linhas que estão em "novos" estão "emprogressos" e, com essa consulta, deixo todos eles.
O que eu preciso é mostrar todo o participante "inprogressista" e, se o ingresso não estiver "inpropreente", será exibido como vazio.
Algo como
identifier|activity|participant|closedate ------------------------------------------- NA00000002| 1| |2008/10/08 15:00| ------------------------------------------- NA00000003| 1| |2008/10/08 15:20| ------------------------------------------- NA00000004| 1| |2008/10/08 15:40| ------------------------------------------- NA00000005| 2| 4|2008/10/08 15:40| ------------------------------------------- NA00000006| 2| 4|2008/10/08 15:40|
Nesse caso
NA002, NA003 e NA004 estão em "novo", então nenhum participante é mostrado
Enquanto
NA005 e NA006 estão sendo "inprogressistas (ACT = 2)" e estão sendo atendidos por Rob (participante 4)
Então, lembro que havia essa coisa chamada de junta externa esquerda ou algo assim, mas eu nunca a entendo. O que eu gostaria de saber é como posso buscar os identificadores que estão "emprogressos" e "novos" e que não estão fechados.
Provavelmente descansar um pouco me ajudaria a esclarecer minha mente. Se alguém souber fazer isso, eu apreciarei.
A propósito, eu tentei:
select
a.identifier,
a.participant,
a.closedate as start
from
performance a
left outer join
performance b
on
b.identifier = a.identifier
where
a.activity = 1 -- new
and a.identifier not in ( select identifier from performance where activity = 4 ) --closed
and b.activity = 2 -- inprogress..
Mas me dá o mesmo resultado que o anterior (solte o único nos registros "novos")
Solução
Tente algo assim (eu não testei):
SELECT p_new.identifier, COALESCE(p_inprog.activity, p_new.activity) AS activity,
p_inprog.participant, COALESCE(p_inprog.closedate, p_new.closedate) AS closedate
FROM performance p_new
LEFT OUTER JOIN performance p_inprog
ON (p_new.identifier = p_inprog.identifier AND p_inprog.activity = 2)
LEFT OUTER JOIN performance p_closed
ON (p_new.identifier = p_closed.identifier AND p_closed.activity = 4)
WHERE p_new.activity = 1
AND p_closed.identifier IS NULL;
Eu acho que as pessoas acreditam que as juntas externas são mais difíceis do que realmente são. Por exemplo:
A LEFT OUTER JOIN B ON (...condition...)
Isso retorna todas as linhas de A, se existem linhas correspondentes ou não em B. Se não houver linhas na partida B, trate todas as colunas B.* como nulo no conjunto de resultados para essa linha de A. A condição de junção pode ser uma expressão que a linha em B deve satisfazer, ou então não está incluída na junção. Portanto, mais linhas em um será solo.
Outras dicas
Normalmente, a melhor maneira de escrever isso é existe. O primeiro seria:
select * from performance p1
where not exists
( select * from performance p2
where p2.identifier = p1.identifier and p2.activity = 4 )
Dessa forma, você permite fazer uma pesquisa com chave no desempenho. (select identifier from performance where activity=4)
.
Eu acho que isso deveria fazer isso.
A primeira parte obtém todos os registros que são novos, não fechados e não em andamento. A segunda parte coloca todos os registros em andamento. Em seguida, juntamos eles, também podemos classificar por identificador envolvendo um 'select * de' em torno desta consulta.
select
a.identifier,
a.participant,
a.closedate as start
from
performance a
where
a.activity = 1
and not exists ( select identifier
from performance b
where b.activity = 4
and b.identifier = a.identifier)
and not exists ( select identifier
from performance c
where c.activity = 2
and c.identifier = a.identifier)
UNION ALL
select
a.identifier,
a.participant,
a.closedate as start
from
performance a
where
a.activity = 2
and not exists ( select identifier
from performance b
where b.activity = 4
and b.identifier = a.identifier);
Eu sugeriria que o que você deseja é o registro mais antigo (presumivelmente, mas não necessariamente aquele com atividade = 1) e o registro mais recente (independentemente do número da atividade). Se a atividade do registro mais recente for 4, o ingresso será fechado. Caso contrário, o participante é o atual titular do ticket. Existe um bug potencial introduzido apenas correspondendo à atividade = 4 se o ticket puder ser reaberto.
Na verdade, com base no seu exemplo, você pode nem precisar do registro mais antigo. Que tal o seguinte:
SELECT
identifier,
activity,
participant,
closedate
FROM
performance a
WHERE
(a.identifier, a.closedate) in
(select b.identifier, max(b.closedate)
from performance b
group by b.identifier
)
;
Que tal agora:
SELECT * FROM (
SELECT identifier,
MAX(activity) activity,
MAX(participant) KEEP (DENSE_RANK LAST ORDER BY activity)
FROM performance
GROUP BY identifier
)
WHERE activity in (1,2)
A consulta interna fornece a atividade mais recente para cada ingresso e seu participante correspondente. A consulta externa o filtra para aqueles em que a atividade é "nova" ou "em andamento".
Eu amo as funções dense_rank.
Em primeiro lugar, você pode ter um problema de design se puder ter um cliente com vários ingressos abertos ao mesmo tempo. Idealmente, você deve ter um ticket_id e, em seguida, pode executar a consulta de Andy usando o ticket_id em vez do identificador.
Quais ingressos não estão fechados:
select identifier as closed_identifier
from performance where identifier not exists
(select identifier from performance where activity=4)
Ingressos que estão sendo atendidos:
select identifier as inprogress_identifier, participant performance
from performance where activity=2
Os ingressos não foram atendidos, com o participante disso está sendo atendido:
select * from
(select identifier as notclosed_identifier
from performance where identifier not exists
(select identifier from performance where activity=4)) closed
left join
(select identifier as inprogress_identifier, participant performance
from performance where activity=2) attended
on notclosed_identifier=inprogress_identifier
Pode ser que você possa usar esse tipo de consulta como ponto de partida.
select x.identifier,
max(x.p_1) as new_participant, max(x.c_1) as new_date,
max(x.p_2) as inprogress_participant, max(x.c_2) as inprogress_date,
max(x.p_3) as approval_participant, max(x.c_3) as approval_date,
max(x.p_4) as closing_participant, max(x.c_4) as closing_date
from (
select a.identifier,
decode (activity, 1, participant, null) as p_1, decode (activity, 1, closedate, null) as c_1,
decode (activity, 2, participant, null) as p_2, decode (activity, 2, closedate, null) as c_2,
decode (activity, 3, participant, null) as p_3, decode (activity, 3, closedate, null) as c_3,
decode (activity, 4, participant, null) as p_4, decode (activity, 4, closedate, null) as c_4
from performance a
) x
group by x.identifier
A idéia é serializar sua tabela da linha para o campo e criar uma visualização com base nela. Você pode criar relatório com base nessa visualização.
Cumprimentos,
Apenas uma ideia rápida que outros possam desenvolver (não testados, mas espero que a ideia apareça):
Primeiro, selecione todas as atividades ainda não fechadas (conforme publicado por outras pessoas):
select id
from performance p1 where identifier not exists
(select * from performance p2 where activity=4 and p1.id=p2.id)
Em seguida, você pode adicionar a pessoa que compareceu à atividade adicionando uma subconsulta na cláusula selecionada:
select id,
(select participant
from performance p3
where p3.activity=3 and p1.id=p2.id)
from performance p1 where identifier not exists
(select * from performance p2 where activity=4 and p1.id=p2.id)
Se não houver registro da Atividade 3 para este ID, o subconstorial retorna nulo, o que é exatamente o que precisamos.
Espero que isso ajude - por favor, expanda se necessário.