2009-08-22 17 views
5

Estoy tratando de recuperar registros en MySQL usando un campo enviado enviado. Más precisamente, el usuario ingresa un nombre (nombre o apellido o nombre completo) y el servidor debe devolver las filas coincidentes.¿Usando la cláusula 'OR' entre HAVING y WHERE en MySQL?

Lo que estoy haciendo hasta ahora es algo así como:

SELECT * FROM people 
WHERE 
    firstname LIKE '%user_submitted_data%' OR 
    lastname LIKE '%user_submitted_data%' 

que funciona bien por ahora, pero que (obviamente) no funcionará cuando un usuario envía el nombre completo. ¿Hay alguna manera de agregar un OR entre las "condiciones de tipo WHERE" y las "condiciones de tipo HAVING"? Esta manera de que pudiera hacer algo como:

SELECT [some fields], CONCAT(firstname, ' ', 'lastname') as fullname 
FROM people 
WHERE 
    firstname LIKE '%user_submitted_data%' OR 
    lastname LIKE '%user_submitted_data%' OR 
    HAVING fullname LIKE '%user_submitted_data%' 

sé que sólo podía dividir la cadena original, sino que tiene un impacto negativo ya que se tiene que tratar con nombres que contengan espacios como 'De Gaulle' y cosas por el estilo.

Respuesta

4

hacer una subconsulta:

SELECT [some fields] 
FROM 
    SELECT firstname, lastname, CONCAT(firstname, ' ', lastname) as fullname 
    FROM people) AS tmp 
WHERE firstname LIKE '%user_submitted_data%' 
OR lastname LIKE '%user_submitted_data%' 
OR fullname LIKE '%user_submitted_data%' 
+0

Gracias, lo arregla. – Jimmy

1

Vamos a considerar algunas de las posibles entradas:

John 
Smith 
John Smith 

Su consulta inicial de la muestra es:

SELECT * FROM people 
WHERE 
    firstname LIKE '%user_submitted_data%' OR 
    lastname LIKE '%user_submitted_data%' 

Ahora, cuando el usuario entra en la primera entrada , esta consulta seleccionará a todas las personas cuyo primer nombre contenga 'John'; también seleccionará a todas las personas cuyo apellido contenga 'John' (por ejemplo, todos los Johnsons en la base de datos). Del mismo modo, la segunda entrada elegirá a todas las personas cuyo primer nombre contenga 'Smith'; también seleccionará a todas las personas cuyo apellido contenga 'Smith' (por ejemplo, Smithsons y Smithers). Hasta aquí todo bien; no es perfecto debido a problemas de sensibilidad a mayúsculas y minúsculas (a partir de ahora ignoraré la distinción entre mayúsculas y minúsculas, pero probablemente no deberías ignorarla), pero estará bien.

La tercera entrada solo seleccionará las personas cuyo primer nombre contenga 'John Smith'; también seleccionará a las personas cuyo apellido contenga 'John Smith'. Sin embargo, es bastante probable que haya muy pocas personas que cumplan con esos criterios: las personas llamadas John Smith tendrán solo a John en el primer nombre y solo a Smith en el apellido. Es poco probable que sea lo que tenía en mente.

No está claro si tiene una columna llamada 'nombre completo' en la tabla. Si lo hace, puede hacer coincidir esa columna en lugar de coincidir con el nombre y apellido por separado. Si no lo hace, tal vez pueda fabricar dicha columna y luego ejecutar la consulta en contra de eso.

SELECT * 
    FROM (SELECT firstname || ' ' || lastname AS fullname, ... FROM people) AS t 
WHERE t.fullname LIKE '%user_submitted_data%' 

Esto funciona bastante bien.

Sin embargo, si le preocupan nombres como 'Charles De Gaulle' (o 'Charles de Gaulle') o 'Michael van den Berg'), la correspondencia fallará si alguien ingresa 'Charles Gaulle' o ' Michael Berg, y mucho menos Michael Vandenberg. Probablemente necesites reemplazar cualquier carácter de espacio en la entrada del usuario con un símbolo '%' también. Incluso entonces, enfrenta el problema de que las palabras deben aparecer exactamente en la secuencia dada por el usuario, lo que puede no importar, pero debe decidir conscientemente que no importa. Por ejemplo, si la entrada es 'Adam John Smith', entonces la consulta no captará 'John Adam Smith'; si la entrada es 'Smith, John', entonces no contestará a nadie (lo más probable).

Si desea administrar esto, es probable que deba tokenizar la entrada del usuario y buscar en las palabras separadas.Tenga cuidado de que alguien pregunte por una palabra secundaria (por ejemplo, alguien pregunta sobre 'de' como palabra de nombre): ninguna de las consultas en este momento asegura que las palabras de entrada del usuario coincidan con palabras completas en los valores (John vs Johnson), y hacerlo con el operador LIKE estándar de SQL es casi imposible.

+0

Gracias por la comprensión en profundidad. Por alguna razón, parece que MySQL no distingue entre mayúsculas y minúsculas en mi sistema (OSX), por lo que ha estado funcionando bien hasta el momento. En lo que respecta al orden de las palabras, el operador LIKE se ajusta muy bien al contexto. No necesito (ni quiero) que Johh Smith salga cuando un usuario está buscando a John Adam Smith. La muestra de búsqueda es bastante limitada. – Jimmy

0

Puede hacer referencia a una columna calculada en la cláusula WHERE si se define que la columna en una subconsulta:

SELECT p.* 
FROM (
    SELECT [some fields], CONCAT(firstname, ' ', 'lastname') as fullname 
    FROM people 
) p 
WHERE 
    p.firstname LIKE '%user_submitted_data%' OR 
    p.lastname LIKE '%user_submitted_data%' OR 
    p.fullname LIKE '%user_submitted_data%'; 

Pero, sinceramente, por el tipo de búsqueda que está haciendo, LIKE con comodines es una solución horrible . Usted debe pensar en usar un índice FULLTEXT:

CREATE FULLTEXT INDEX people_names ON people(firstname, lastname); 

SELECT * 
FROM people 
WHERE MATCH(firstname, lastname) AGAINST(?); 

PS: FULLTEXT índices sólo funcionan con el motor de almacenamiento MyISAM. Otra solución, incluso más rápida, es usar Sphinx Search para la indexación de texto completo.

0

Aunque usar una subconsulta funciona bien, tendrá un impacto porque no está golpeando ningún índice.

¿Qué tal agregar una columna calculada (firstname || ' ' || lastname) a la tabla y un índice a la misma? Seguramente sería mucho más rápido.

Si no puede hacer que yo creo que la consulta como

WHERE firstname || ' ' || lastname LIKE '%user_submitted_data%' 

debería funcionar más rápido que dos OR s y una subconsulta.

+0

No creo que exista una forma de hacer una columna de cálculo indexada en MySQL, o así mostrar mis investigaciones. Si eso es factible, me encantaría saberlo. – Jimmy

+0

Puede que tenga razón, ¡creo que no hay forma de usar columnas calculadas !? – Sklivvz

+0

Mientras tanto, MySQL admite columnas calculadas indexadas. Pero las condiciones LIKE no pueden usar un índice si el patrón comienza con un marcador de posición ('%'). –

7

Simplemente ponga todas las condiciones en la cláusula HAVING.

SELECT [some fields], CONCAT(firstname, ' ', 'lastname') as fullname 
FROM people 
HAVING firstname LIKE '%user_submitted_data%' 
OR  lastname LIKE '%user_submitted_data%' 
OR  fullname LIKE '%user_submitted_data%

La cláusula WHERE podría descartar filas temprano, pero ya que no puede desprenderse de ellas hasta después que haya evaluado la condición en la columna calculada, y que tiene que esperar hasta HAVING, se le compra nada para usar WHERE .

+0

Funcionó para mí, parece una solución mucho más elegante que el uso de subconsultas. ¡Gracias! –