Oracle Analytic Question
-
06-07-2019 - |
Domanda
Data una funzione zipdistance (zipfrom, zipto) che calcola la distanza (in miglia) tra due codici postali e le seguenti tabelle:
create table zips_required(
zip varchar2(5)
);
create table zips_available(
zip varchar2(5),
locations number(100)
);
Come posso costruire una query che mi restituirà ogni codice postale dalla tabella zips_required e la distanza minima che produrrebbe una somma (posizioni) > = n.
Fino ad ora abbiamo appena eseguito un ciclo esaustivo di query per ciascun raggio fino a quando non abbiamo soddisfatto i criteri.
--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
Questo può richiedere del tempo in un ampio elenco. Sembra che questo potrebbe essere fatto con una query analitica Oracle come:
min() over (
partition by zips_required.zip
order by zipdistance( zips_required.zip, zips_available.zip)
--range stuff here?
)
Le uniche query analitiche che ho fatto sono state " row_number over (partizione per ordine di) " basato, e sto calpestando aree sconosciute con questo. Qualsiasi consiglio in merito è molto apprezzato.
Soluzione
Questo è quello che mi è venuto in mente:
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
- Per ogni
zip_required
calcola la distanza dazip_available
e ordinali per distanza - Per ogni
count
ilrange
conzip_availables
ti consente di sapere quanti <=> sono nel raggio di quella distanza. - filtro (prima dove COUNT (posizioni) > N)
Ho usato per creare dati di esempio:
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;
/
Nota: hai usato COUNT (posizioni) e SUM (posizioni) nella tua domanda, supponevo che fosse COUNT (posizioni)
Altri suggerimenti
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
Per ogni zip_required
, verrà selezionata la distanza minima in cui si adattano N
zip_available
o la distanza massima se il numero di <=> è inferiore a <=>.
Ho risolto lo stesso problema creando un sottoinsieme di ZIP all'interno di un raggio quadrato da quello specificato (semplice matematica: < o > NSWE radius), quindi ripetendo ogni voce del sottoinsieme per vedere se era nel raggio necessario. Ha funzionato come un fascino ed è stato molto veloce.
Avevo requisiti parzialmente simili in uno dei miei vecchi progetti ... per calcolare la distanza tra 2 codici postali negli Stati Uniti. Per risolvere lo stesso avevo fatto un grande uso dei dati spaziali statunitensi. Fondamentalmente l'approccio era quello di ottenere il codice postale di origine (latitudine, longitudine) e il codice postale di destinazione (latitudine, longitudine). Ora avevo applicato una funzione per ottenere la distanza in base a quanto sopra. La formula di base che aiuta a fare questo calcolo è disponibile nel seguente sito Ho anche validato il risultato facendo riferimento a questo sito ...
Nota: tuttavia ciò fornirà distanze approssimative, quindi è possibile utilizzarlo di conseguenza. I vantaggi vengono costruiti una volta superveloci per ottenere i risultati.