2011-02-01 12 views
8

Tengo una tabla llamada products en una base de datos MySQL. products se ve un poco de lo que de esta manera:SQL: busque la siguiente fila en una cláusula where con una ID

id name  din  strength active deleted 
1 APA TEST 00246374 25  1  0 
4 APA BOB 00246375 50  1  0 
5 APA TIRE 00246888 50  1  0 
7 APA ROT 00521414 100  1  0 
9 APA APA 01142124 100  1  0 
6 APA CODE 00121212 150  1  0 
8 APA SERV 00011145 600  1  0 

Obviamente He dejado de lado varias columnas no es importante para mi pregunta. Cuando consulto esta tabla, ordenaré por una de varias columnas diferentes (la interfaz de usuario permite a los usuarios individuales cambiar la columna y el orden de clasificación), y puedo tener una cláusula de búsqueda en cuyo caso haré una cláusula LIKE en NAME y DIN.

Lo que quiero saber es, dada la información de ordenación e información de búsqueda, y el ID de un producto específico (digamos que busqué 004, que arrojó 3 resultados, y estoy viendo uno de ellos), ¿cómo podría obtener los productos siguientes y anteriores?

Necesito hacer esto, porque si un usuario hace clic para editar/ver uno de los productos después de buscar y ordenar los resultados, quiere poder recorrer los resultados sin tener que ir a la página anterior.

¿Existe alguna manera buena y eficiente de hacer esto en SQL, o es mejor que use PHP? Cualquier idea también es bienvenida.

Actualmente el uso de esta consulta SQL, que está experimentando problemas si ordenar por la columna de la strength ya que hay valores duplicados

SELECT T.* 
FROM `wp_products` T 
INNER JOIN `wp_products` curr on curr.id = 38 
    AND ((T.strength = curr.strength and T.id < curr.id) 
    OR (T.strength > curr.strength)) 
WHERE T.active = 1 AND T.deleted = 0 AND (T.name LIKE '%%' OR T.din LIKE '%%') 
ORDER BY T.strength ASC, T.id ASC 
LIMIT 1 

Mi código PHP (usando WordPress) (diseñado para obtener el siguiente elemento)

$sql = 'SELECT T.* 
FROM `' . $wpdb->prefix . 'apsi_products` T 
INNER JOIN `' . $wpdb->prefix . 'apsi_products` curr on curr.id = ' . $item->id . ' 
    AND ((T.' . $wpdb->escape($query['orderby']) . ' = curr.' . $wpdb->escape($query['orderby']) . ' and T.id > curr.id) 
    OR (T.' . $wpdb->escape($query['orderby']) . ' > curr.' . $wpdb->escape($query['orderby']) . ')) 
WHERE T.active = 1 AND T.deleted = 0 AND (T.name LIKE \'%' . $query['where'] . '%\' OR T.din LIKE \'%' . $query['where'] . '%\') 
ORDER BY T.' . $wpdb->escape($query['orderby']) . ' ' . $query['order'] . ', T.id ASC 
LIMIT 1;'; 

Respuesta

5

Debe tener una referencia al registro actual, y luego buscar progresivamente el siguiente registro basado en las columnas ordenadas. El ejemplo siguiente se supone que está ordenada en

ORDER BY Active, DIN, NAME 

Primero:

SELECT * 
FROM TABLE 
WHERE NAME LIKE '%X%' AND DIN LIKE '%%' 
ORDER BY Active, DIN, Name 
LIMIT 1; 

siguiente: (asegúrese de separar el CURR.ID = 6 y los Y-ORS con los soportes adecuados!)

SELECT * 
FROM TABLE T 
INNER JOIN TABLE CURR ON CURR.ID = 6 # the current ID being viewed 
    AND ((T.Active = Curr.Active AND T.DIN = Curr.DIN AND T.NAME > Curr.Name) 
    OR (T.Active = Curr.Active AND T.DIN > Curr.DIN) 
    OR T.Active > Curr.Active) 
WHERE T.NAME LIKE '%X%' AND T.DIN LIKE '%%' 
ORDER BY T.Active, T.DIN, T.Name 
LIMIT 1; 

Una muestra de trabajo se presenta a continuación

create table products 
(ID int, SEED int, NAME varchar(20), DIN varchar(10), ACTIVE int, DELETED int); 
insert products values 
(1, 0, 'Product #1', '004812', 1, 0), 
(2, 0, 'Product #2', '004942', 0, 0), 
(3, 0, 'Product #3', '004966', 1, 0), 
(4, 0, 'Product #4', '007437', 1, 1), 
(5, 2, 'Product #2', '004944', 0, 0), 
(6, 2, 'Product #2', '004944', 1, 0); 

SELECT * 
FROM products 
WHERE active = 1 AND deleted = 0 
ORDER BY din DESC, ID desc; 

Output: 
"ID";"SEED";"NAME";"DIN";"ACTIVE";"DELETED" 
"3";"0";"Product #3";"004966";"1";"0" 
"6";"2";"Product #2";"004944";"1";"0" 
"1";"0";"Product #1";"004812";"1";"0" 

Si la corriente es la fila con ID = 6, el siguiente registro se puede recuperar usando

SELECT T.* 
FROM products T 
INNER JOIN products curr on curr.ID = 6 
    AND ((T.din = curr.din and T.ID > curr.ID) 
    OR (T.din < curr.din)) 
WHERE T.active = 1 AND T.deleted = 0 
ORDER BY T.din DESC, T.ID ASC 
LIMIT 1; 
+0

Lo siento, siempre he sido terrible con SQL. ¿Debo dejar referencias a CURR como están, o debería cambiar CURR y TABLE a los nombres de mi tabla? –

+0

TABLE es un marcador de posición para el nombre de su tabla. CURR y T son alias: déjalos en paz. – RichardTheKiwi

+0

No estoy teniendo éxito sin su consulta, más que probable que esté haciendo algo mal. Mi consulta tiene este aspecto: 'SELECT * FROM table_name WHERE active = 1 AND deleted = 0 ORDER BY din DESC' y cuando ejecuto su consulta, obtengo la primera fila devuelta por mi consulta aunque estoy usando la 5ª ID que me fue devuelta . Debería obtener la identificación 38 de la sexta identificación. Ideas? –

0

Pregunta interesante. No creo que puedas hacerlo en una consulta, pero puedes lograrlo quizás con tres.

La primera consulta extrae la fila de respuestas. El truco sería mantener el valor de la columna que ordenaron. Supongamos que esto es "SortedColumn" y el valor es "M". Supongamos que el ID de la fila que obtuvo fue 10.

Su segunda consulta sería la misma que la primera, pero sería "top 1" y se agregaría a la cláusula where "... and sortedColumn> = ' M 'e id <> 10 "Esto te lleva a la siguiente fila. Es posible que desee devolver 10 filas y aferrarse a ellas para evitar hacer esto una y otra vez.

Para obtener el anterior, cambie su orden a descendente, y la cláusula where que agregue es "... y sortColumn < = 'M' e id <> 10". Esto te lleva a la fila anterior. Nuevamente, 10 filas pueden ser más útiles para su usuario.

Espero que esto ayude.

+0

Se puede hacer en una sola consulta. Ver mi respuesta – RichardTheKiwi

+0

Intenté esto rápidamente. No creo que pueda funcionar. Digamos que estoy ordenando por la columna de nombre. La parte 'name'> 'PRODUCT NAME # 3' por ejemplo no parece funcionar –

+0

@cyberwiki: nice. –

1

Sin una capa de caché de persistencia como memcached, donde puede almacenar los resultados y obtenerlos sin volver a emitir la consulta, una solución simple puede habilitar el query cache en mysql. De esta manera, si el caché no se invalida de otros querys, cuando se vuelva a emitir la consulta el costo de sacar el resultado será bajado

+0

se puede hacer de manera eficiente con solo una consulta. Ver mi respuesta – RichardTheKiwi

+0

@cyberwiki por lo que entiendo de la pregunta, no es un problema de consulta única, ya que los elementos se solicitarán en diferentes páginas. Su consulta abordará cómo consultar los diferentes artículos, pero se volverá a evaluar para cada solicitud de página, ¿está de acuerdo? – Ass3mbler

+0

Sí, pero con una indexación adecuada, cada límite 1 recupera solo 1 registro según sea necesario, de manera eficiente. Cada carga de página SO solicita aproximadamente 10 elementos al menos desde el db, por lo que los viajes redondos deberían ser una preocupación menor. – RichardTheKiwi

1

Para cada muestra consecuencia, la configuración de los siguiente y vínculos anteriores con la identificación apropiada.

El orden de clasificación no se puede cambiar desde la vista de detalles.

Cuestiones relacionadas