Oracle se une (izquierda exterior, derecha, etc.: S)
Pregunta
Sabía que stackoverflow me ayudaría, aparte de saber cuál es el "dibujo animado de programación favorito". : P
Esta fue la respuesta aceptada por: Bill Karwin
Gracias a todos por la ayuda (me gustaría votarlos a todos)
Mi consulta terminó así (esta es la real)
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;
Ahora solo tengo que unirme con las otras tablas para obtener un informe legible para humanos.
PUBLICACIÓN ORIGINAL
Hola.
Estoy luchando durante unas 6 horas. ahora con una consulta de base de datos (mi antiguo enemigo)
Tengo una tabla de datos con algunos campos como:
table performance(
identifier varchar,
activity number,
participant number,
closedate date,
)
Se utiliza para realizar un seguimiento del historial de tickets
Identificador : es una identificación de cliente como (NAF0000001)
actividad : es un lugar donde se encuentra el ticket (nuevo, en progreso, rechazado, cerrado, etc.)
participante : es una persona que está asistiendo en ese momento al boleto
fecha de cierre : es la fecha en que finalizó esa actividad.
EDITAR: debería haber dicho " completedate " en lugar de cerrar la fecha. Esta es la fecha en que se completó la actividad, no es necesario cuando se cerró el ticket.
Por ejemplo, un historial típico puede ser así:
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| -------------------------------------------
Y el participante 1 = jonh, 2 = scott, 3 = mike, 4 = rob
y actividades 1 = nuevo, 2 = en progreso, 3 = espera para aprobación, 4 = cerrado
etc. Y decenas de otra información irrelevante.
Bueno, mi problema es el siguiente.
He logrado crear una consulta donde puedo saber cuándo se abrió y cerró un ticket
es así:
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
Pero no puedo saber qué boletos no están cerrados y quién los atiende.
Hasta ahora tengo algo como esto:
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
Es decir, dame todos los que tienen un comienzo (nuevo = 1) pero no están cerrados (cerrado = 4)
Pero el gran problema aquí es que imprime al participante que abrió el boleto, pero necesito al participante que lo atiende. Entonces agrego el " en progreso " actividad a la 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..
Pero no todas las filas que están en " new " están " en progreso " y con esa consulta los elimino a todos.
Lo que necesito es mostrar todos los " en progreso " participante y si el ticket no está "en progreso", se mostrará como vacío.
Algo así
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|
En este caso
NA002, NA003 y NA004 están en "nuevo", por lo que no se muestra ningún participante
Mientras
NA005 y NA006 están siendo "en progreso (act = 2)" y están siendo atendidos por rob (participante 4)
Así que recuerdo que había una cosa llamada combinación externa izquierda o algo así, pero nunca lo entiendo. Lo que me gustaría saber es cómo puedo obtener los identificadores que están en progreso. y "nuevo" y que no están cerrados.
Probablemente descansar un poco me ayudaría a aclarar mi mente. Si alguien sabe cómo hacerlo, lo agradeceré.
Por cierto, he intentado:
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..
Pero me da el mismo resultado que el anterior (suelte el único en " nuevos " registros)
Solución
Pruebe algo como esto (no lo he probado):
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;
Creo que la gente cree que las uniones externas son más difíciles de lo que realmente son. Por ejemplo:
A LEFT OUTER JOIN B ON (...condition...)
Esto devuelve todas las filas de A, ya sea que haya o no filas coincidentes en B. Si no hay filas en B coincidentes, trate todas las columnas B. * como NULL en el conjunto de resultados para esa fila de A. La condición de unión puede ser una expresión que la fila en B debe satisfacer, o de lo contrario no se incluye en la unión. Entonces, más filas en A serán solo.
Otros consejos
Normalmente, la mejor manera de escribirlos es con EXISTS. El primero sería:
select * from performance p1
where not exists
( select * from performance p2
where p2.identifier = p1.identifier and p2.activity = 4 )
De esta forma, puede realizar una búsqueda con clave en performance.identifier, en lugar de tener que crear una lista masiva de identificadores en (seleccione el identificador de performance donde activity = 4)
.
Creo que esto debería hacerlo.
La primera parte obtiene todos los registros que son nuevos, no están cerrados y no están en progreso. La segunda parte obtiene todos los registros de progreso. Luego los unimos, también podemos ordenar por identificador colocando un 'SELECCIONAR * DESDE' alrededor de esta 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);
Sugeriría que lo que desea es el registro más antiguo (presumiblemente, pero no necesariamente el que tiene actividad = 1) y el registro más reciente (independientemente del número de actividad). Si la actividad del registro más reciente es 4, el ticket está cerrado. de lo contrario, el participante es el titular actual del boleto. Existe un posible error introducido simplemente haciendo coincidir la actividad = 4 si el ticket se puede volver a abrir.
En realidad, según su ejemplo, es posible que ni siquiera necesite el registro más antiguo. ¿Qué tal lo siguiente:
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
)
;
¿Qué tal esto?
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)
La consulta interna proporciona la actividad más reciente para cada ticket y su participante correspondiente. La consulta externa filtra esto a aquellos donde la actividad es " nueva " o "en progreso".
Me encantan las funciones DENSE_RANK.
En primer lugar, puede tener un problema de diseño si puede tener un cliente con varios tickets abiertos al mismo tiempo. Idealmente, debe tener un ticket_id, y luego puede realizar la consulta de Andy utilizando ticket_id en lugar de un identificador.
Qué entradas no están cerradas:
select identifier as closed_identifier
from performance where identifier not exists
(select identifier from performance where activity=4)
Entradas a las que se asiste:
select identifier as inprogress_identifier, participant performance
from performance where activity=2
Entradas no cerradas, con la asistencia de los participantes:
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
Puede ser que pueda usar este tipo de consulta como punto 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
La idea es serializar su tabla de fila en campo y crear una vista basada en ella. Puede crear un informe basado en esta vista.
Saludos,
Solo una idea rápida en la que otros podrían construir (sin probar, pero espero que la idea se presente):
Primero, seleccione todas las actividades aún no cerradas (según lo publicado por otros):
select id
from performance p1 where identifier not exists
(select * from performance p2 where activity=4 and p1.id=p2.id)
Luego, puede agregar a la persona que asiste a la actividad agregando una subconsulta en la cláusula select:
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)
Si no hay ningún registro de actividad 3 para esta identificación, la subconsulta devuelve nulo, que es exactamente lo que necesitamos.
Espero que esto ayude. Expanda si es necesario.