PDA

Ver la Versión Completa : Dudas con esta consulta


Caminante
16-05-2014, 13:44:04
Buenos dias, tardes o noches segun corresponda. Tengo una duda que espero puedan ayudarme a despejar.
Tengo un sistema funcionando en firebird 2.5. En este tengo un listado de resumen. La consulta que lanzo es la siguiente:

SELECT MC.ID_MOV, DC.ID_DETCIL, MC.FECHADOC, DC.CIL,CI.TIPOGASID, TG.TIPOGAS,
CI.DESCRIPCION,CI.CAPACIDAD, MC.SERIE, MC.DOCUMENTO,DC.PLAZO,
DC.FECHADEV, DC.DOCDEV,MC.NOMDESTINO, CP1.NOMBRE,CP2.ID_CLIENTE,
CP2.TIPO, CP2.NOMBRE,DC.LUGAR, DC.OBSERVACION FROM
DETALLECIL DC INNER JOIN MOVCILINDROS MC ON DC.MOVCIL=MC.ID_MOV
INNER JOIN CILINDROS CI on DC.CIL=CI.ID_CILINDRO INNER JOIN
CLIEPROV CP1 ON MC.NOMDESTINO=CP1.ID_CLIENTE INNER JOIN
CLIEPROV CP2 ON CI.PROPIETARIO=CP2.ID_CLIENTE
INNER JOIN TIPOGASES TG ON CI.tipogasid=TG.id
WHERE CP1.id_cliente>0

La consulta en si me da los resultados esperados con una demora maxima de un par de segundos. Ahora revisando en los foros escuche mencionar el IB plan analizer para analizar las consultas. Lo probe y me salio en las tablaS MOVCILINDROS Y DETALLECIL una advertencia. Revisando veo que utiliza los indices que corresponde a sus respectivas claves foraneas.
Este es el plan usado:

PLAN JOIN (CP1 INDEX (RDB$PRIMARY2), MC INDEX (RDB$FOREIGN5), DC INDEX (RDB$FOREIGN12), CI INDEX (RDB$PRIMARY1), CP2 INDEX (RDB$PRIMARY2), TG INDEX (PK_TIPOGASES))

Estas son las tablas usadas:

CREATE TABLE CILINDROS (
ID_CILINDRO INTEGER NOT NULL,
DESCRIPCION VARCHAR(30),
TIPOGASID INTEGER NOT NULL,
CAPACIDAD VARCHAR(7),
PROPIETARIO INTEGER,
OBSCILINDRO VARCHAR(30)
);

CREATE TABLE CLIEPROV (
ID_CLIENTE INTEGER NOT NULL,
RUC VARCHAR(11),
NOMBRE TCADENA NOT NULL COLLATE ES_ES_CI_AI,
EMAIL VARCHAR(30),
WEB VARCHAR(30),
CONTACTO VARCHAR(50),
TIPO INTEGER NOT NULL,
NOMMAYUS TCADENA COLLATE ES_ES_CI_AI,
RUBRO TRUBRO NOT NULL
);

CREATE TABLE DETALLECIL (
ID_DETCIL INTEGER NOT NULL,
MOVCIL INTEGER NOT NULL,
CIL INTEGER NOT NULL,
PLAZO INTEGER DEFAULT 0,
FECHADEV DATE,
OBSERVACION VARCHAR(50),
DOCDEV VARCHAR(15),
LUGAR VARCHAR(15)
);

CREATE TABLE MOVCILINDROS (
ID_MOV INTEGER NOT NULL,
NOMDESTINO INTEGER NOT NULL,
SERIE VARCHAR(3) NOT NULL,
DOCUMENTO VARCHAR(10) NOT NULL,
FECHADOC DATE NOT NULL,
VENDEDOR INTEGER NOT NULL
);

CREATE TABLE TIPOGASES (
ID INTEGER NOT NULL,
TIPOGAS VARCHAR(10) NOT NULL COLLATE ES_ES_CI_AI
);

ALTER TABLE CILINDROS ADD PRIMARY KEY (ID_CILINDRO);
ALTER TABLE CLIEPROV ADD PRIMARY KEY (ID_CLIENTE);
ALTER TABLE DETALLECIL ADD PRIMARY KEY (ID_DETCIL);
ALTER TABLE MOVCILINDROS ADD PRIMARY KEY (ID_MOV);
ALTER TABLE TIPOGASES ADD CONSTRAINT PK_TIPOGASES PRIMARY KEY (ID);

ALTER TABLE CILINDROS ADD CONSTRAINT FK_CILINDROS_1 FOREIGN KEY (TIPOGASID) REFERENCES TIPOGASES (ID) ON UPDATE CASCADE;
ALTER TABLE CILINDROS ADD FOREIGN KEY (PROPIETARIO) REFERENCES CLIEPROV (ID_CLIENTE) ON DELETE NO ACTION ON UPDATE CASCADE;
ALTER TABLE DETALLECIL ADD CONSTRAINT FKDETALLECIL_CIL FOREIGN KEY (CIL) REFERENCES CILINDROS (ID_CILINDRO) ON DELETE NO ACTION ON UPDATE CASCADE;
ALTER TABLE DETALLECIL ADD FOREIGN KEY (MOVCIL) REFERENCES MOVCILINDROS (ID_MOV) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE MOVCILINDROS ADD CONSTRAINT FK_MOVCILINDROS_DEST FOREIGN KEY (NOMDESTINO) REFERENCES CLIEPROV (ID_CLIENTE) ON DELETE NO ACTION ON UPDATE CASCADE;
ALTER TABLE MOVCILINDROS ADD FOREIGN KEY (VENDEDOR) REFERENCES PERSONAL (ID_PERSONAL) ON DELETE NO ACTION ON UPDATE CASCADE;

Si bien la consulta no es lenta (Al menos no mucho). Me preocupa que no este debidamente optimizada. Viendo las estadisticas de los indices se muestra la siguiente selectividad:

RDB$FOREIGN5 24.59
RDB$FOREIGN12 2.48

Me pregunto ¿De que manera se puede mejorar esta consulta?

Gracias por la atención prestada...

duilioisola
16-05-2014, 14:13:38
Dado que el WHERE actúa sobre la tabla "CLIEPROV CP1" yo trataría de empezar por ahí las uniones con las otras tablas.


SELECT MC.ID_MOV, DC.ID_DETCIL, MC.FECHADOC, DC.CIL,CI.TIPOGASID, TG.TIPOGAS,
CI.DESCRIPCION,CI.CAPACIDAD, MC.SERIE, MC.DOCUMENTO,DC.PLAZO,
DC.FECHADEV, DC.DOCDEV,MC.NOMDESTINO, CP1.NOMBRE,CP2.ID_CLIENTE,
CP2.TIPO, CP2.NOMBRE,DC.LUGAR, DC.OBSERVACION
FROM
CLIEPROV CP1
JOIN MOVCILINDROS MC ON MC.NOMDESTINO=CP1.ID_CLIENTE
JOIN DETALLECIL DC ON DC.MOVCIL=MC.ID_MOV
JOIN CILINDROS CI ON DC.CIL=CI.ID_CILINDRO
JOIN CLIEPROV CP2 ON CI.PROPIETARIO=CP2.ID_CLIENTE
JOIN TIPOGASES TG ON CI.tipogasid=TG.id
WHERE
CP1.ID_CLIENTE > 0


Además de esto probaría la consulta utilizando LEFT JOIN.


Los índices que utiliza aparentemente son los correctos y parecen óptimos.

CLIEPROV CP1
JOIN MOVCILINDROS MC ON MC.NOMDESTINO=CP1.ID_CLIENTE
ALTER TABLE MOVCILINDROS ADD CONSTRAINT FK_MOVCILINDROS_DEST FOREIGN KEY (NOMDESTINO) REFERENCES CLIEPROV (ID_CLIENTE) ON DELETE NO ACTION ON UPDATE CASCADE;

y

JOIN DETALLECIL DC ON DC.MOVCIL=MC.ID_MOV
ALTER TABLE DETALLECIL ADD FOREIGN KEY (MOVCIL) REFERENCES MOVCILINDROS (ID_MOV) ON DELETE CASCADE ON UPDATE CASCADE;

Caminante
16-05-2014, 16:57:43
Hola gracias por responder

Además de esto probaría la consulta utilizando LEFT JOIN.


Y Como seria la consulta??

He probado asi:

SELECT MC.ID_MOV, DC.ID_DETCIL, MC.FECHADOC, DC.CIL,CI.TIPOGASID, TG.TIPOGAS,
CI.DESCRIPCION,CI.CAPACIDAD, MC.SERIE, MC.DOCUMENTO,DC.PLAZO,
DC.FECHADEV, DC.DOCDEV,MC.NOMDESTINO, CP1.NOMBRE,CP2.ID_CLIENTE,
CP2.TIPO, CP2.NOMBRE,DC.LUGAR, DC.OBSERVACION FROM
DETALLECIL DC left JOIN MOVCILINDROS MC ON DC.MOVCIL=MC.ID_MOV
INNER JOIN CILINDROS CI on DC.CIL=CI.ID_CILINDRO INNER JOIN
CLIEPROV CP1 ON MC.NOMDESTINO=CP1.ID_CLIENTE INNER JOIN
CLIEPROV CP2 ON CI.PROPIETARIO=CP2.ID_CLIENTE
INNER JOIN TIPOGASES TG ON CI.tipogasid=TG.id
WHERE CP1.id_cliente>0 and dc.id_detcil>0

Y ahora si parece estar todo ok con los indices. Pero la demora es la misma.

Caminante
17-05-2014, 00:19:19
Ahora el nuevo plan es este:

PLAN JOIN (JOIN (JOIN (DC INDEX (RDB$PRIMARY4), MC INDEX (RDB$PRIMARY6)), CI INDEX (RDB$PRIMARY1), CP1 INDEX (RDB$PRIMARY2)), CP2 INDEX (RDB$PRIMARY2), TG INDEX (PK_TIPOGASES))

duilioisola
17-05-2014, 00:51:32
Prueba con la que te propuse, reordenando los joins y agregando left delante de cada uno:

SELECT MC.ID_MOV, DC.ID_DETCIL, MC.FECHADOC, DC.CIL,CI.TIPOGASID, TG.TIPOGAS,
CI.DESCRIPCION,CI.CAPACIDAD, MC.SERIE, MC.DOCUMENTO,DC.PLAZO,
DC.FECHADEV, DC.DOCDEV,MC.NOMDESTINO, CP1.NOMBRE,CP2.ID_CLIENTE,
CP2.TIPO, CP2.NOMBRE,DC.LUGAR, DC.OBSERVACION
FROM
CLIEPROV CP1
LEFT JOIN MOVCILINDROS MC ON MC.NOMDESTINO=CP1.ID_CLIENTE
LEFT JOIN DETALLECIL DC ON DC.MOVCIL=MC.ID_MOV
LEFT JOIN CILINDROS CI ON DC.CIL=CI.ID_CILINDRO
LEFT JOIN CLIEPROV CP2 ON CI.PROPIETARIO=CP2.ID_CLIENTE
LEFT JOIN TIPOGASES TG ON CI.tipogasid=TG.id
WHERE
CP1.ID_CLIENTE > 0

Caminante
17-05-2014, 01:39:38
Hola de nuevo


WHERE
CP1.ID_CLIENTE > 0
[/sql]

Bueno esta linea realmente no es necesaria. Solo la puse siguiendo un consejo de internet para obligar a utilizar un indice en el plan (Ya que daba natural join cp1 cuando no estaba esa linea).


Ya probe como me dijiste pero igual sigue con ese detalle en los mismos indices.