2011-05-12 28 views
9

Tengo una tabla de códigos postales y quiero actualizar cada código postal con sus 3 vecinos más cercanos. Es decir, para llenar los espacios en blanco en esta tabla:mySQL - actualizar varias columnas con una selección que devuelve varias filas

postcode nearestPostcode1 nearestPostcode2 nearestPostcode3 
_______________________________________________________________ 

KY6 1DA  -    -     - 
KY6 1DG  -    -     - 
KY6 2DT  -    -     - 
KY6 1RG  -    -     - 
.... 

que he descubierto una consulta SELECT para encontrar los códigos postales más cercanos y aquí es una manera torpe la primera fila se podría actualizar:

update table1 set 
nearestPostcode1 = (select query for returning the first nearest postcode), 
nearestPostcode2 = (select query for returning the second nearest postcode), 
nearestPostcode3 = (select query for returning the third nearest postcode) 
where postcode = 'KY6 1DA'; 

Sin embargo, esto generará 3 consultas de selección para cada actualización de fila. Sería más eficaz si había alguna manera de hacer lo que se expresa por esta pseudo código:

update table1 set 
(nearestPostcode1, nearestPostcode2, nearestPostcode3) = 
(select query to return the 3 nearest postcodes) 
where postcode = 'KY6 1DA'; 

La 'consulta de selección' en las miradas por encima de la siguiente manera:

select postcode from postcodeTable 
order by <equation to calculate distance> ASC 
limit 3 

¿Hay alguna forma de las filas devueltas desde la selección para ponerlas en un formulario que se pueden usar para actualizar varios campos? Gracias.

+0

¿Cómo se determina el "código postal más cercano"? – Thomas

+0

@Thomas latitud y longitud también se almacenan en la tabla de códigos postales y los convierto en metros y uso pythageros – spiderplant0

Respuesta

10
Update Table1 
    Cross Join (
       Select Min(Case When Z1.Num = 1 Then Z1.postcode End) As PostCode1 
        , Min(Case When Z1.Num = 2 Then Z1.postcode End) As PostCode2 
        , Min(Case When Z1.Num = 3 Then Z1.postcode End) As PostCode3 
       From (
         Select postcode 
          , @num := @num + 1 As Num 
         From postcodeTable 
         Where postcode = 'KY6 IDA' 
         Order By <equation to calculate distance> ASC 
         Limit 3 
         ) As Z1 
       ) As Z 
Set nearestPostCode1 = Z.PostCode1 
    , nearestPostCode2 = Z.PostCode2 
    , nearestPostCode3 = Z.PostCode3 
Where Table1.postcode = 'KY6 IDA' 
+0

Gracias @Thomas. Esto funcionó. Sin embargo, necesito expandir el código y no puedo entender el significado de "Min (Caso cuando Z1.Num = 1 Then Z1.postcode End) Como PostCode1". Pensé que "Min" era para usar con "group by". Traté de reemplazarlos por "IF (@ rownum = 1, código postal, '')) AS PostCode1", etc. pero no puedo hacer que esto funcione. ¿Puede explicar cómo funciona el "Min (Case ..." – spiderplant0

+0

@ spiderplant0 - Por lo general, Min se usa con Group By sin embargo, si cada columna está envuelta en una función agregada (como la tabla derivada "Z"), entonces solo se devuelve una fila. – Thomas

+0

@ spiderplant0 - Debería usar 'Case' en lugar de' If' ya que 'Case' es compatible con ANSI. Tenga en cuenta que tengo que calcular el orden en su propia tabla derivada (Z1) y luego evaluar mis expresiones como Z1.Num = 1. – Thomas

0

creo que se podría hacer esto con el pseudo-código:

REPLACE INTO table1 (postcode, nearestPostcode1, nearestPostcode2, nearestPostcode3) 
    SELECT "KY6 1DA", col1, col2, col3 FROM myTable WHERE ...; 

que sería más fácil para especificar que ver el SQL real.

Tenga en cuenta que la primera columna se especifica como una constante entre comillas. Para que esto funcione, postcode debe ser un índice UNIQUE o PRIMARY.

+0

Gracias @ james-c Debería haber explicado que mi consulta de selección devuelve 3 filas y no 3 columnas. Es decir. devuelve 3 códigos postales que son los más cercanos. He agregado un poco más a mi pregunta original para explicar esto un poco más. – spiderplant0

+0

el SQL real para la consulta de selección para encontrar los 3 códigos postales más cercanos es \t código postal SELECT postcode_table \t donde \t lat>. ". (Lat $ - $ deltaLat)" y \t lat < "($ lat + $ deltaLat) "y \t lng>" ($ lng -..... $ deltaLng) "y \t lng <" ($ lng + $ deltaLng) "y \t = activos '1' \t ORDER BY \t POW (lat - $ lat, 2) + \t POW ((lng - $ lng) * $ lat2LngAdjust, 2) \t ASC \t límite 3 – spiderplant0

+0

Sospecho que podría hacer esto con los procedimientos almacenados, pero me temo que no tengo los conocimientos. Probablemente manejaría esto con una lógica de ajuste simple en (por ejemplo, PHP) que ejecuta la primera consulta, extrae los valores y luego crea la declaración de actualización. En teoría, podría ejecutar las consultas 'SELECT' para múltiples códigos postales, preparar los resultados por lotes y luego crear una gran instrucción' REPLACE' de múltiples filas. –

1

Usted puede hacer algo similar a esto:

UPDATE table1 
SET 
nearestPostcode1 = pc1, 
nearestPostcode2 = pc2, 
nearestPostcode3 = pc3 
FROM 
(SELECT pc1, pc2, pc3 FROM ....) t 
WHERE 
postcode = 'KY6 1DA'; 

me encontré con esta pregunta relacionada en Stackoverflow sobre cómo transformar columnas a filas:

MySQL - Rows to Columns

En su caso , puede hacer algo como

SELECT 
IF(@rownum=1,postcode,'')) AS pc1, 
IF(@rownum=2,postcode,'')) AS pc2, 
IF(@rownum=3,postcode,'')) AS pc2, 
FROM 
(SELECT postcode 
FROM postcodeTable 
ORDER BY <equation to calculate distance> ASC 
LIMIT 3) 

Aquí es un truco para simular la funcionalidad ROW_NUMBER() en MySQL [1]:

SELECT @rownum:[email protected]+1 rownum, t.* 
FROM (SELECT @rownum:=0) r, mytable t; 
+0

Gracias @peter Sin embargo, ¿son las columnas pc1, pc2 y pc3? Mi consulta de selección devuelve 3 filas, no 3 columnas. Es decir. devuelve 3 códigos postales que son los más cercanos. He agregado un poco más a mi pregunta original para explicar esto un poco más. – spiderplant0

+0

Quiero decir gracias Ravi-Gummadi :) – spiderplant0

+0

@ spiderplant0: Modificó la respuesta. No he ejecutado realmente la consulta, así que simplemente considérelo como un código psuedo. – rkg

-1

Cada vez que veo una tabla con columnas que tienen 1-up contadores después de sus nombres, me sale que se trate.

En general, es una mala idea (TM) almacenar datos que se pueden calcular a partir de datos que ya están almacenados. ¿Qué sucede si su aplicación de repente necesita los códigos postal más cercanos ? ¿Qué ocurre si cambian los límites del código postal?

Suponiendo que el cálculo de la distancia no es muy complejo, lo mejor será que a la larga no guarde estos datos explícitamente.

+0

@ chris-morgan El cálculo de los 3 códigos postales más cercanos tomará mucho tiempo para ejecutarse. Pero esto está bien, ya que solo debe hacerse una vez, y las consultas posteriores de la base de datos que necesitan esta información lo necesitan rápidamente. La decisión de si algo es o no una mala idea, casi siempre depende del contexto. – spiderplant0

+0

@ spiderplant0 Definitivamente valoro que el contexto importa, siempre me gusta equivocarme por el lado de la flexibilidad, ya que el cambio generalmente está garantizado. –

Cuestiones relacionadas