Este artículo puede sonar más bién a una llamada desesperada. Llevo unas semanas (por no decir un par de meses) liado con un proyecto que me consume más de lo que quisiera pero por respeto a la empresa no puedo desvelar casi nada. Y para colofón me aparece este problema que me está llevando de cabeza.
Resulta que tengo una query... sencilla en concepto, un par de joins cruzados... La construyo en local contra MySQL 4 y funciona perfectamente... y al subirla al servidor de producción con MySQL 5 simplemente me devuelve 0 rows. Estoy literalmente alucinando.
Explicación del escenario
Se trata de tres tablas con relación N:M, es decir, dos tablas principales y una que las une:
variables
---------
id (pk)
name
var_questions
-------------
id (pk)
id_variable (fk)
id_language (fk)
question
languages
----------
id (pk)
name
enabled
La tabla de variables contiene todas las variables registradas en el programa. La tabla languages contiene todos los idiomas disponibles en el programa. La tabla var_questions relaciona variables con languages y presenta el campo question que viene a ser el texto de la variable en el idioma determinado, es decir, para cada variable e idioma debe haber un texto.
Entonces yo necesito una query que me saque las inconsistencias (no las coincidencias), porque se trata de tablas MyISAM y en el proceso de desarrollo se puede haber creado un idioma sin haber generado ningún texto para él y para todas las variables. Mi query debe encontrar "lo que queda suelto".
La query
Usando la lógica (la mía, claro...) pensé: teniendo la lista de variables y la lista de idiomas, lo que debe presentar inconsistencias es la tabla intermedia. Entonces, podría hacer una query enlazando las variables con var_questions con un left join i la última con languages con un right join, para encontrar los nulls en la tabla intermedia var_questions. Luego le digo en el where que sólo quiero los nulls et violà.
SQL:
-
SELECT
-
VARIABLES.name AS var_name,
-
VARIABLES.id AS var_id,
-
var_questions.question AS question,
-
languages.name AS lang
-
FROM
-
VARIABLES
-
LEFT JOIN var_questions ON VARIABLES.id = var_questions.id_variable
-
RIGHT JOIN languages ON var_questions.id_language = languages.id
-
WHERE
-
var_questions.question IS NULL
-
AND languages.enabled = 1
La ejecuto en MySQL 4 y perfecto. Me saca justamente la información que necesito. Subo la query a producción, con un servidor MySQL 5 y me devuelve (con los mismos datos) 0 rows. Y justo ahí empieza mi odisea.
Investigando
La verdad es que poco más puedo investigar a parte de lo que haríamos todos: Googlear mucho, buscar problemas de compatibilidad, buscar documentación de JOINs, preguntar a cracks en el tema, dejar mensajes en foros, ...
Las versiones con las que juego son, en local, un MySQL 4.1.22-community-nt sobre Windows Vista, y en producción un MySQL 5.0.24a sobre Linux 2.6.9-023stab046.2-enterprise (Red Hat 3.4.5-2).
Durante las pruebas he encontrado una query que parece devolver los mismos resultados, pero no me gusta por el simple hecho que usa una CROSS JOIN que no podría explicar exactamente qué hace, pero parece hacer una producto cartesiano de las tablas enlazadas y no acepta un ON:
SQL:
-
SELECT
-
VARIABLES.name AS var_name,
-
var_questions.question AS question,
-
languages.name AS lang
-
FROM
-
languages
-
CROSS JOIN VARIABLES
-
LEFT JOIN var_questions ON var_questions.id_language = languages.id
-
WHERE
-
var_questions.question IS NULL
-
AND languages.enabled = 1
Cómo probar?
Tengo unos datos preparados por si alguien quiere hacer algunas pruebas (que serán agradecidas profundamente):
SQL:
-
CREATE TABLE `variables` (
-
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
-
`name` varchar(100) collate latin1_spanish_ci NOT NULL DEFAULT '',
-
PRIMARY KEY (`id`)
-
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_spanish_ci;
-
-
CREATE TABLE `languages` (
-
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
-
`name` varchar(10) collate latin1_spanish_ci NOT NULL DEFAULT '',
-
`enabled` tinyint(1) NOT NULL DEFAULT '0',
-
PRIMARY KEY (`id`)
-
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_spanish_ci;
-
-
CREATE TABLE `var_questions` (
-
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
-
`id_variable` int(11) UNSIGNED NOT NULL DEFAULT '0',
-
`id_language` int(11) UNSIGNED NOT NULL DEFAULT '0',
-
`question` text collate latin1_spanish_ci,
-
PRIMARY KEY (`id`)
-
) ENGINE=MyISAM AUTO_INCREMENT=163 DEFAULT CHARSET=latin1 COLLATE=latin1_spanish_ci;
-
-
INSERT INTO `languages` (`id`, `name`, `enabled`) VALUES (2, 'es', 1);
-
INSERT INTO `languages` (`id`, `name`, `enabled`) VALUES (4, 'ca', 1);
-
INSERT INTO `languages` (`id`, `name`, `enabled`) VALUES (5, 'en', 0);
-
-
INSERT INTO `variables` (`id`, `name`) VALUES (56, 'var_1');
-
INSERT INTO `variables` (`id`, `name`) VALUES (60, 'var_2');
-
INSERT INTO `variables` (`id`, `name`) VALUES (61, 'var_3');
-
-
INSERT INTO `var_questions` (`id`, `id_variable`, `id_language`, `question`) VALUES (157, 56, 2, 'Variable 1');
-
INSERT INTO `var_questions` (`id`, `id_variable`, `id_language`, `question`) VALUES (158, 60, 2, 'Variable 2');
-
INSERT INTO `var_questions` (`id`, `id_variable`, `id_language`, `question`) VALUES (159, 61, 2, 'Variable 3');
El resultado correcto debería ser el siguiente:
| var_name |
var_id |
question |
lang |
| var_1 |
56 |
null |
ca |
| var_2 |
60 |
null |
ca |
| var_3 |
61 |
null |
ca |
Colofón
Vale. No soy capaz de encontrar el problema. Será un problema de algún bug en la versión que uso para producción? Está mal planteada la query? A alguien se le ocurre algo?
Una cervecita para quien dé con la solución
Salu10
Tag:
Escribe un Comentario