2010-11-17 24 views
7

Me considero bastante competente para comprender y manipular los lenguajes C-ish; no es un problema para mí encontrar un algoritmo e implementarlo en cualquier lenguaje C-ish.¿Qué es una forma estructurada de construir una consulta MySQL?

Tengo una tremenda dificultad para escribir consultas SQL (en mi caso específico, MySQL). Para consultas muy simples, no es un problema, pero para consultas complejas, me siento frustrado sin saber por dónde empezar. Leer la documentación de MySQL es difícil, principalmente porque la descripción de la sintaxis y la explicación no están muy bien organizadas.

Por ejemplo, la documentación SELECT es todo el mapa: se comienza con lo que parece ser pseudo-BNF, pero luego (ya que el texto para las descripciones de agregados se puede hacer clic ... como select_expr) que rápidamente se convirtiera en este frustrante ejercicio de tratar de sintetizar la sintaxis usted mismo al tener abiertas varias ventanas del navegador.

Suficiente lloriqueo.

Me gustaría saber cómo la gente, paso a paso, comienza a construir una consulta compleja de MySQL. Aquí hay un ejemplo específico. Tengo tres tablas a continuación. Quiero SELECT un conjunto de filas con las siguientes características:

De los userInfo y userProgram mesas, quiero seleccionar los userName, isApproved y modifiedTimestamp campos y UNION en un solo conjunto. Desde este conjunto quiero ORDER por modifiedTimestamp tomando el MAX(modifiedTimestamp) por cada usuario (es decir, debería haber solo una fila con un único userName y la marca de tiempo asociada con ese nombre de usuario debería ser lo más alta posible).

De la tabla user, quiero coincidir con el firstName y lastName que se asocia con la userName para que se vea algo como esto:

+-----------+----------+----------+-------------------+ 
| firstName | lastName | userName | modifiedTimestamp | 
+-----------+----------+----------+-------------------+ 
| JJ  | Prof  | jjprofUs |  1289914725 | 
| User  | 2  | user2 |  1289914722 | 
| User  | 1  | user1 |  1289914716 | 
| User  | 3  | user3 |  1289914713 | 
| User  | 4  | user4 |  1289914712 | 
| User  | 5  | user5 |  1289914711 | 
+-----------+----------+----------+-------------------+ 

Lo más cerca que lo que tengo es una consulta que se parece esto:

(SELECT firstName, lastName, user.userName, modifiedTimestamp 
FROM user, userInfo 
WHERE user.userName=userInfo.userName) 

UNION 

(SELECT firstName, lastName, user.userName, modifiedTimestamp 
FROM user, userProgram 
WHERE user.userName=userProgram.userName) 

ORDER BY modifiedTimestamp DESC; 

siento que estoy bastante cerca, pero no sé a dónde ir desde aquí o incluso si estoy pensando en esto de la manera correcta.

> user 
+--------------------+--------------+------+-----+---------+-------+ 
| Field    | Type   | Null | Key | Default | Extra | 
+--------------------+--------------+------+-----+---------+-------+ 
| userName   | char(8)  | NO | PRI | NULL |  | 
| firstName   | varchar(255) | NO |  | NULL |  | 
| lastName   | varchar(255) | NO |  | NULL |  | 
| email    | varchar(255) | NO | UNI | NULL |  | 
| avatar    | varchar(255) | YES |  | ''  |  | 
| password   | varchar(255) | NO |  | NULL |  | 
| passwordHint  | text   | YES |  | NULL |  | 
| access    | int(11)  | NO |  | 1  |  | 
| lastLoginTimestamp | int(11)  | NO |  | -1  |  | 
| isActive   | tinyint(4) | NO |  | 1  |  | 
+--------------------+--------------+------+-----+---------+-------+ 

> userInfo 
+-------------------+------------+------+-----+---------+-------+ 
| Field    | Type  | Null | Key | Default | Extra | 
+-------------------+------------+------+-----+---------+-------+ 
| userName   | char(8) | NO | MUL | NULL |  | 
| isApproved  | tinyint(4) | NO |  | 0  |  | 
| modifiedTimestamp | int(11) | NO |  | NULL |  | 
| field    | char(255) | YES |  | NULL |  | 
| value    | text  | YES |  | NULL |  | 
+-------------------+------------+------+-----+---------+-------+ 

> userProgram 
+-------------------+--------------+------+-----+---------+-------+ 
| Field    | Type   | Null | Key | Default | Extra | 
+-------------------+--------------+------+-----+---------+-------+ 
| userName   | char(8)  | NO | PRI | NULL |  | 
| isApproved  | tinyint(4) | NO | PRI | 0  |  | 
| modifiedTimestamp | int(11)  | NO |  | NULL |  | 
| name    | varchar(255) | YES |  | NULL |  | 
| address1   | varchar(255) | YES |  | NULL |  | 
| address2   | varchar(255) | YES |  | NULL |  | 
| city    | varchar(50) | YES |  | NULL |  | 
| state    | char(2)  | YES | MUL | NULL |  | 
| zip    | char(10)  | YES |  | NULL |  | 
| phone    | varchar(25) | YES |  | NULL |  | 
| fax    | varchar(25) | YES |  | NULL |  | 
| ehsChildren  | int(11)  | YES |  | NULL |  | 
| hsChildren  | int(11)  | YES |  | NULL |  | 
| siteCount   | int(11)  | YES |  | NULL |  | 
| staffCount  | int(11)  | YES |  | NULL |  | 
| grantee   | varchar(255) | YES |  | NULL |  | 
| programType  | varchar(255) | YES |  | NULL |  | 
| additional  | text   | YES |  | NULL |  | 
+-------------------+--------------+------+-----+---------+-------+ 
+1

Esto no tiene ninguna relación con jQuery. Etiqueta eliminada – casablanca

+0

Si desea aprender SQL, la documentación de una implementación de una base de datos que la utiliza no es buena literatura. La documentación está escrita para alguien que ya conoce SQL razonablemente bien. Por lo tanto, debería encontrar algo de literatura para aprender SQL en su lugar. – Guffa

Respuesta

1

Por lo que entiendo de su pregunta, parece que necesita una consulta correlacionada, que se vería así:

(SELECT firstName, lastName, user.userName, modifiedTimestamp 
FROM user, userInfo ui1 
WHERE user.userName=userInfo.userName 
AND modifiedtimestamp=(select max(modifiedtimestamp) from userInfo ui2 where ui1.userName=ui2.userName)) 

UNION 

(SELECT firstName, lastName, user.userName, modifiedTimestamp 
FROM user, userProgram up1 
WHERE user.userName=userProgram.userName 
AND modifiedtimestamp=(select max(modifiedtimestamp) from userProgram up2 where up1.userName=up2.userName)) 
ORDER BY modifiedTimestamp DESC; 

Por lo tanto, debo proceder para llegar a este resultado? La clave es: exprese claramente la información que desea recuperar, sin tomar atajos mentales.

Paso 1: Elija los campos que necesito en las diferentes tablas de mi base de datos. Eso es lo que está entre SELECT y FROM. Parece obvio, pero se vuelve menos obvio cuando se trata de funciones de agregación, como sumas o recuentos. En ese caso, debe decir, por ejemplo, "Necesito el recuento de líneas en userInfo para cada firstName". Vea a continuación en GROUP BY.

Paso 2: Conociendo el campo que necesita, escriba las uniones entre las diferentes tablas correspondientes. Es fácil ...

Paso 3: Exprese sus condiciones.Puede ser fácil, como si desea datos del usuario para userName = "RZEZDFGBH", o más complicado, como en su caso: la forma de formularlo para que pueda hacer el trabajo, si solo desea la marca de tiempo modificada más reciente, es "para que la marca de tiempo modificada sea igual a la última marca de tiempo modificada" (allí es donde puede tomar fácilmente un atajo mental y omitir el punto)

Paso 4: Si tiene agregados, es hora de establecer la instrucción GROUP BY. Por ejemplo, si se cuentan todas las líneas en userInfo para cada primerNombre, podría escribir "GROUP BY Nombre":

SELECT firstName,count(*) FROM userInfo GROUP BY firstName 

Esto le da el número de entradas en la tabla para cada primerNombre diferente.

Paso 5: TENIENDO condiciones. Estas son las condiciones en los agregados. En el ejemplo anterior, si solo deseaba que los datos de FirstName tuvieran más de 5 líneas en la tabla, podría escribir SELECT firstName,count(*) FROM userInfo GROUP BY firstName HAVING count(*)>5

Paso 6: Ordenar con ORDER BY. Bastante fácil ...

Sólo hay un breve resumen. Hay mucho, mucho más por descubrir, pero sería demasiado largo escribir aquí un curso completo de SQL ... ¡Espero que ayude!

+0

Esto realmente no responde mi pregunta. No estoy tan interesado en la respuesta a mi ejemplo. Estoy interesado en _how_ llegaste a esa respuesta. Si pudieras mirar mi descripción y luego mostrarme el proceso de pensamiento para construir la consulta, sería increíble. – Avery

+0

El problema es que no estoy completamente seguro de que mi código haga lo que desea porque su pregunta no está nada clara. Supongamos que lo es y lo que está buscando es, comenzando con el resultado de su consulta, devolver solo la línea correspondiente a la última marca de tiempo modificada para cada usuario. ¿Es esto correcto? Voy a modificar mi respuesta. –

0

No se puede construir SQL sin entender los datos de las tablas y el resultado lógico requerido. No se proporcionan antecedentes sobre los datos a los que se parecerán las tablas y su significado, y la descripción de los resultados que intenta reunir no tiene sentido para mí, así que no voy a arriesgarme a adivinar.

En este último punto ... es raro que desee una unión de valores de marca de tiempo de varias fuentes. En términos generales, cuando se recopilan resultados como ese, generalmente es para algún tipo de auditoría/seguimiento. Sin embargo, cuando descarta toda la información sobre la fuente de la marca de tiempo y solo calcula un máximo, tiene ... ¿qué es exactamente?

De todos modos, uno o más ejemplos de datos y resultados deseados y tal vez algo sobre la aplicación y los por qué es una necesidad para que quede claro.

En la medida en que voy a hacer ninguna predicción acerca de la forma de su eventual declaración, (asumiendo que su tarea aún será conseguir una única marca de tiempo máximo por usuario) es que va a ser algo como esto:

select u.firstname, u.lastname, user_max_time.userName, user_max_time.max_time 
from users u, 
(select (sometable).userName, max((sometable).(timestamp column)) 
from (data of interest) 
group by (sometable).userName) user_max_time 
where u.userName = user_max_time.userName 
order by max_time desc; 

Su tarea aquí sería reemplazar el() s dentro de la subselección user_max_time con algo que tenga sentido y que se corresponda con sus requisitos. En términos de un enfoque general de sql complejo, la sugerencia principal es volver a crear la consulta de las subsecuencias más internas (pruebas en el camino para asegurarse de que el rendimiento sea correcto y no necesite tablas intermedias).

De todos modos, si tiene problemas y puede volver con ejemplos, estaría encantado de ayudarle.

Saludos, Ben

+0

puede formatear el código en SO al resaltarlo y hacer clic en el botón '101'. –

+0

Sí, me doy cuenta de que el significado de las estructuras de tabla que publiqué son opacas. Solo para aportar un poco más de luz: para nuestra aplicación, siempre tendremos userProgram, pero los ítems en userInfo son flexibles. La razón por la que los unimos es que queremos la última actualización que ocurrió, ya sea el agregado de userProgram o un elemento en userInfo. – Avery

1

Como dice f00, es simple (r) si se piensa en los datos en términos de conjuntos.

Uno de los problemas con la pregunta es que el resultado esperado no coincide con los requisitos establecidos; la descripción menciona la columna de Aprobado, pero esto no aparece en ninguna parte ni en la consulta ni en el resultado esperado.

Lo que esto ilustra es que el primer paso para escribir una consulta es tener una idea clara de lo que desea lograr. El problema más grande con la pregunta tal como está es que esto no está claramente descrito, sino que se mueve desde una tabla de muestra de salida esperada (que sería más útil si tuviéramos muestras correspondientes de datos de entrada esperados) directamente en una descripción de cómo tiene la intención de lograrlo.

Como yo lo entiendo, lo que desea ver es una lista de usuarios (por nombre de usuario, con sus nombres y apellidos asociados), junto con la última vez que se modificó cualquier registro asociado en userInfo o userProgram mesas.

(No es claro si desea ver los usuarios que no tienen ninguna actividad asociada a cada una de estas otras mesas - la consulta suministrado no implica, de lo contrario las juntas sería combinaciones externas.)

Por lo tanto, quiero una lista de los usuarios (por nombre de usuario, con sus nombres y apellidos asociados):

SELECT firstName, lastName, userName 
FROM user 

junto con una lista de veces que los registros eran modificada por última vez:

SELECT userName, MAX(modifiedTimestamp) 

...

en cualquiera de las userInfo o userProgram tablas:

...

FROM 
(SELECT userName, modifiedTimestamp FROM userInfo 
UNION ALL 
SELECT userName, modifiedTimestamp FROM userProgram 
) subquery -- <- this is an alias 

...

por nombre de usuario:

...

group by userName 

Estos dos conjuntos de datos deben estar vinculados con su nombre de usuario - por lo que la consulta final se convierte en:

SELECT user.firstName, user.lastName, user.userName, 
     MAX(subquery.modifiedTimestamp) last_modifiedTimestamp 
FROM user 
JOIN 
(SELECT userName, modifiedTimestamp FROM userInfo 
UNION ALL 
SELECT userName, modifiedTimestamp FROM userProgram 
) subquery 
ON user.userName = subquery.userName 
GROUP BY user.userName 

En la mayoría de las versiones de SQL, esta consulta devuelve un error como user.firstName y user.lastName no están incluidos en la cláusula GROUP BY , ni se resumen. MySQL permite esta sintaxis: en otros SQL, ya que esos campos dependen funcionalmente de userName, al agregar un MAX delante de cada campo o al agregarlos a la agrupación se obtendría el mismo resultado.

Un par de puntos adicionales:

  • UNION y UNION ALL no son idénticos - la antigua elimina duplicados mientras que el segundo no lo hace; esto hace que el primero sea más intensivo en el uso del procesador. Como los duplicados serán eliminados por la agrupación, es mejor usar UNION ALL.
  • Mucha gente va a escribir esta consulta como usuario unió a userInfo unioned todas con el user unió a userProgram - esto se debe a que muchos motores de SQL pueden optimizar este tipo de consulta de manera más eficaz. En este punto, esto representa una optimización prematura.
1

Hay un montón de cosas buenas aquí. Gracias a todos los que contribuyeron. Este es un resumen rápido de las cosas que encontré útiles, así como algunos pensamientos adicionales para conectar las funciones de construcción a las consultas de construcción. Me gustaría poder darles a todos los puntos de Mérito, pero creo que solo puede haber uno (respuesta), así que elegiré Traroth en función del punto total y la amabilidad personal.

Una función puede entenderse como tres partes: entrada, proceso, salida. Una consulta se puede entender de manera similar. La mayoría de las consultas tienen el siguiente aspecto:

SELECT stuff FROM data WHERE data is like something 
  • La parte SELECT es la salida. Existen algunas capacidades para formatear la salida aquí (es decir, usando AS)

  • La parte FROM es la entrada. La entrada debe verse como un conjunto de datos; querrá hacer esto tan específico como sea posible, utilizando una variedad de combinaciones y subconsultas que sean apropiadas.

  • La porción WHERE es como el proceso, pero hay mucha superposición con la parte FROM. Tanto las porciones de FROM y WHERE pueden reducir la piscina de datos apropiadamente usando una variedad de condiciones para filtrar los datos no deseados (o sólo incluye datos deseados). La parte WHERE también puede ayudar a formatear la salida.

Así es como me quebré los pasos:

  1. de inicio con pensar en lo que su salida se parece. Esto va a la parte SELECT.

  2. Luego, si desea definir el conjunto de datos que se desea trabajar. Traroth señala: "Conociendo el campo que necesita, escriba las combinaciones entre las diferentes tablas correspondientes. Es fácil ..." Depende de lo que quiera decir con "fácil". Si eres nuevo en la redacción de consultas, es probable que solo quieras escribir uniones internas (como hice yo). Esta no es siempre la mejor manera de hacerlo. http://en.wikipedia.org/wiki/Join_(SQL) es un gran recurso para comprender los diferentes tipos de uniones posibles.

  3. Como parte del paso anterior, piense en las partes más pequeñas de ese conjunto de datos y acumule todo el conjunto de datos que le interesan. Al escribir una función, puede escribir subfunciones para ayudar a expresar su proceso de una manera más clara manera. Similar a eso, puede escribir subconsultas. Un gran consejo de Mark Bannister para crear una subconsulta Y USAR UN ALIAS. Tendrá que volver a configurar su salida para usar este alias, pero esto es bastante clave.

  4. el último, usted puede utilizar varios métodos para recortar el conjunto de datos, eliminación de datos no está interesado en

Una manera de pensar acerca de los datos que se está operando en un gigante de 2- matriz D: JOIN s hacer más grande el aspecto horizontal, UNION s hacer más grande el aspecto vertical.Todos los otros filtros están diseñados para hacer que esta matriz sea más pequeña para que sea apropiada para su salida. No sé si existe una analogía "funcional" para JOIN, pero UNION solo está agregando la salida de dos funciones juntas.

Me doy cuenta, sin embargo, que hay muchas maneras en que la construcción de consultas NO es como escribir una función. Por ejemplo, puede construir y reducir su conjunto de datos en las áreas FROM y WHERE. Lo que fue clave para mí fue comprender las uniones y descubrir cómo crear subconsultas usando alias.

Cuestiones relacionadas