Pergunta analítica do Oracle
-
06-07-2019 - |
Pergunta
Dada uma função ZipDistance (ZipFrom, Zipto) que calcula a distância (em milhas) entre dois códigos postais e as seguintes tabelas:
create table zips_required(
zip varchar2(5)
);
create table zips_available(
zip varchar2(5),
locations number(100)
);
Como posso construir uma consulta que retorne a mim cada CEP da tabela zips_required e a distância mínima que produziria uma soma (locais)> = n.
Até agora, acabamos de executar uma consulta exaustiva de loop para cada raio até atendermos aos critérios.
--Do this over and over incrementing the radius until the minimum requirement is met
select count(locations)
from zips_required zr
left join zips_available za on (zipdistance(zr.zip,za.zip)< 2) -- Where 2 is the radius
Isso pode demorar um pouco em uma grande lista. Parece que isso pode ser feito com uma consulta analítica do Oracle ao longo das linhas de:
min() over (
partition by zips_required.zip
order by zipdistance( zips_required.zip, zips_available.zip)
--range stuff here?
)
As únicas consultas analíticas que fiz foram "row_number sobre (partição por ordem por)" baseada, e estou passando por áreas desconhecidas com isso. Qualquer orientação sobre isso é muito apreciada.
Solução
Isso é o que eu criei:
SELECT zr, min_distance
FROM (SELECT zr, min_distance, cnt,
row_number() over(PARTITION BY zr ORDER BY min_distance) rnk
FROM (SELECT zr.zip zr, zipdistance(zr.zip, za.zip) min_distance,
COUNT(za.locations) over(
PARTITION BY zr.zip
ORDER BY zipdistance(zr.zip, za.zip)
) cnt
FROM zips_required zr
CROSS JOIN zips_available za)
WHERE cnt >= :N)
WHERE rnk = 1
- Para cada
zip_required
Calcule a distância para ozip_available
e classificá -los à distância - Para cada
zip_required
acount
comrange
permite que você saiba quantoszip_availables
estão no raio dessa distância. - filtro (primeiro onde contagem (locais)> n)
Eu costumava criar dados de amostra:
INSERT INTO zips_required
SELECT to_char(10000 + 100 * ROWNUM) FROM dual CONNECT BY LEVEL <= 5;
INSERT INTO zips_available
(SELECT to_number(zip) + 10 * r, 100 - 10 * r FROM zips_required, (SELECT ROWNUM r FROM dual CONNECT BY LEVEL <= 9));
CREATE OR REPLACE FUNCTION zipdistance(zipfrom VARCHAR2,zipto VARCHAR2) RETURN NUMBER IS
BEGIN
RETURN abs(to_number(zipfrom) - to_number(zipto));
END zipdistance;
/
Observação: Você usou a contagem (locais) e a soma (locais) em sua pergunta, presumi que era contagem (locais)
Outras dicas
SELECT *
FROM (
SELECT zip, zd, ROW_NUMBER() OVER (PARTITION BY zip ORDER BY rn DESC) AS rn2
FROM (
SELECT zip, zd, ROW_NUMBER() OVER (PARTITION BY zip ORDER BY zd DESC) AS rn
FROM (
SELECT zr.zip, zipdistance(zr.zip, za.zip) AS zd
FROM zips_required zr
JOIN zips_available za
)
)
WHERE rn <= n
)
WHERE rn2 = 1
Para cada zip_required
, isso selecionará a distância mínima na qual o ajuste N
zip_available
S, ou distância máxima se o número de zip_available
S é menor que N
.
Resolvi o mesmo problema criando um subconjunto de ZIP dentro de um raio quadrado do zip fornecido (Matemática fácil: <ou> Radius), então iterando cada entrada no subconjunto para ver se estava dentro do raio necessário. Funcionou como um encanto e foi muito rápido.
Eu tinha requisitos parcialmente semelhantes em um dos meus projetos antigos ... para calcular a distância entre 2 códigos ZIPs nos EUA. Para resolver o mesmo, eu fiz um ótimo uso de dados espaciais dos EUA. Basicamente, a abordagem era obter o código ZIPCODE (latitude, longitude) e o ZipCode de destino (latitude, longitude). Agora então eu havia aplicado uma função para obter a distância com base no acima. A fórmula base que ajuda a fazer esse cálculo está disponível no A seguir, localEu também havia validado o resultado referindo -se a esse site...
Nota: No entanto, isso fornecerá distâncias aproximadas, para que se possa usá -lo de acordo. Os benefícios já foram construídos é super rápido para obter os resultados.