Club Builder - Foros de discusión Club Builder
Foro de C++Builder en español
 
 FAQFAQ   BuscarBuscar   MiembrosMiembros   Grupos de UsuariosGrupos de Usuarios   RegistrarseRegistrarse 
 PerfilPerfil   Entra para ver tus mensajes privadosEntra para ver tus mensajes privados   EntrarEntrar 

Consulta "pesada"
Ir a página 1, 2  Siguiente
 
Publicar nuevo tema   Responder al tema    Foros de discusión -> Bases de Datos
Ver tema anterior :: Ver tema siguiente  
Autor Mensaje
Mensaje    Asunto: Consulta "pesada" Responder citando

Estoy teniendo un problema que me está resultado más complejo de resolver de lo que yo pensaba. Vamos por partes. La base de datos está en Firebird-2.0.0.12748

Tengo definida la siguiente tabla:
Código:
CREATE TABLE Recibos (C_refer CHAR(12), Fecha DATE, RefInt CHAR(10), Cuenta CHAR(20), Texto CHAR(70), Importe NUMERIC(10, 2), Situacion CHAR(1), JLocal CHAR(5), PRIMARY KEY (C_refer, Fecha, RefInt))

Y estoy haciendo la siguiente consulta:
Código:
SELECT C_refer FROM Personas WHERE Situacion = 'A' AND Cuota > 0 AND TRIM(C_refer) NOT IN (SELECT DISTINCT TRIM(C_refer) FROM Recibos)

La cláusula TRIM la he puesto tras comprobar que si hago directamente SELECT DISTINCT me saca todos los registros de la tabla Recibos.

El problema que estoy teniendo es que si lanzo la consulta completa, la máquina se muere; llega a tardar 15 minutos en dar algún resultado; vaya por delante: la tabla de personas tiene 17.000 registros y la de recibos unos 87.000. Esta misma consulta, en Paradox, tardaba unos 2 minutos en ejecutarse.

Me había planteado crear una clave externa (FOREING KEY) en la tabla de recibos, pero al tratar de definirla con IBManager me da el siguiente error:
Cita:
Unsuccessful metadata update.
Could not find UNIQUE INDEX with specified columns.

con esta instrucción:
Código:
ALTER TABLE RECIBOS ADD CONSTRAINT FK_RECIBOS FOREIGN KEY (C_REFER) REFERENCES PERSONAS(C_REFER);

No sé si la idea de una clave externa es correcta, pero estoy abierto a cualquier sugerencia.
_________________
Un saludo

Necesitamos tu ayuda: Envía AI al 27595 (Coste 1,20 euros + IVA)
Volver arriba
Ver perfil de usuario Enviar mensaje privado Enviar email
Mensaje    Asunto: Responder citando

Hola

Cita:

Me había planteado crear una clave externa (FOREING KEY) en la tabla de recibos, pero al tratar de definirla con IBManager me da el siguiente error:


El error es porque estas intentado crear una campo como clave foranea(FOREING KEY) que no es clave primaria en su tabla. C_REFER debería ser clave primara en Personas.

Que significa C_REFER, que contiene?
Porque creas una clave primaria con los tres campos?
En general y casi siempre utilizo claves primaria enteras, y si hay restricciones que indiquen que los tres campos deben ser unicos lo hago con

Código:
alter table MiTabla add constraint unq_MiRestriccion unique (Campo1,Campo2,...);


Saludos
TJose
Volver arriba
Ver perfil de usuario Enviar mensaje privado
Mensaje    Asunto: Responder citando

La estructura que es de una tabla de recibos; los campos de la clave son:
    1. C_refer » Código de referencia: Cada persona a la que se factura un recibo tiene un código único.
    2. Fecha: La de emisión del recibo. No se pueden emitir dos recibos para la misma persona en la misma fecha.
    3. RefInt: Un referencia asociada al recibo que identifica el tipo de emisión.

Sobre las restricciones he de decir que estoy empezando a manejar las bases de datos con Firebird y me quedan muchas cosas por aprender: restricciones, triggers, etc.

Si te puedo anticipar que no me interesa hacer de ese campo una clave primaria en la tabla de personas; como es lógico, el índice primario en esa tabla es el alfabético.
_________________
Un saludo

Necesitamos tu ayuda: Envía AI al 27595 (Coste 1,20 euros + IVA)
Volver arriba
Ver perfil de usuario Enviar mensaje privado Enviar email
Mensaje    Asunto: Responder citando

Angel quiero entender lo siguiente, tienes 2 tablas, la tabla recibos, es un detalle de la
tabla personas, correcto?, si es asi entonces, la declaracion de las tablas deben estar de la
siguiente manera.

Código:
CREATE TABLE Personas (
       C_refer CHAR(12) not null, /* identificador de la persona, aun que me gusta mas el ID, jeje */
       /* los demas campos */
       PRIMARY KEY (C_refer) /* llaver primaria para enlazar con otras tablas */
);



CREATE TABLE Recibos (
       id integer not null, /* identificador unico del recibo */
       C_refer CHAR(12) not null,
       Fecha DATE,
       RefInt CHAR(10),
       Cuenta CHAR(20),
       Texto CHAR(70),
       Importe NUMERIC(10, 2),
       Situacion CHAR(1),
       JLocal CHAR(5),
       PRIMARY KEY (id), /* Llave primaria */
       FOREIGN KEY (C_refer) REFERENCES Personas (C_Refer)  /* llave Foranea  que se enlaza con personas*/
);


ahora bien, imagino que usas el componente IBDataSet para cada una de las tablas,
en el componente IBDataSet para la tabla personas y en la instruccion select,
podemos poner algo como esto "Select * from Personas", jalamos a todas las personas,
se puede modificar es solo para el ejemplo, asi mismo imagino que tienes un DataSource1 para
el IBDDataSet1, que son orientados a la tabla personas.

en el otro componente, que llamare IBDataSet2 y DataSource2
en el IBDataSet2 que nos servirá para la tabla recibos, hacemos la siguientes operaciones

IBDataSet2
DataSource: DataSource1

propiedad DataSource: aqui debes apuntar al DataSource1, para enlazarlo con la tabla
personas.

posteriormente generas la sentencia SQL dentro del SelectSQL de la siguiente manera
"Select * from recibos where C_Refer = :C_Refer", esto hace que cuando te muevas en la
tabla personas, automaticamente en la tabla recibos te muestre los registros
asignados a la persona donde te encuentras posicionado en ese momento.
pero en el componente que
te sirve para los recibos, imagino que estas queriendo jalar los recibos, de cada
una de las personas, bien pudes usar la siguiente instruccion.
_________________
Cool
Volver arriba
Ver perfil de usuario Enviar mensaje privado Enviar email MSN Messenger
Mensaje    Asunto: Responder citando

Para qeu el motor de sql funcione rapido necesita laves unicas de preferencia integer (nada con char) entonces crea una campo autonumerico id tipo integer y listo (aun que no lo incluyas en la consulta ) pero le sirve al motor para hacer sus arboles-b y demas menjurges matematicos..(por lo tanto) acelera la consulta

saludos
_________________
Ing. Gabriel Góngora Navarrete
TouchSoftwares Inc

http://www.touchsoftwares.com
Volver arriba
Ver perfil de usuario Enviar mensaje privado Visitar sitio web del autor MSN Messenger
Mensaje    Asunto: Responder citando

Efectivamente, usar campos IDentificadores del tipo entero para las llaves primarias son la mejor opción, pero como no conozco la estructura de la tabla personas, e indicando que es una manera mas legible para que el sepa como generar el maestro detalle, puse el ejemplo asumiendo que la tabla personas debe contener un campo que haga referencia, lo ideal seria establecer las tablas algo asi

Código:
Personas
    ID integer not null,
    /* los demas campos */
   primary key (id)

Recibos
    ID integer not null,       /*identificador del recibo*/
    ID_personsa integer not null   /*identificador de la persona */
    primary key(id), /*llave primaria de los recibos*/
    foreign key (id_persona) references Personas(ID), /*llave foranea a personas*/


pero comento, que al no conocer las estructuras de las tablas que maneja Angel, he utilizado los campos que me ha dado.

Saludos
_________________
Cool
Volver arriba
Ver perfil de usuario Enviar mensaje privado Enviar email MSN Messenger
Mensaje    Asunto: Responder citando

Bueno, gracias por las sugerencias. Veo que sigo con la mala costumbre de no explicar bien las cosas. Vamos a ver:

Efectivamente hay dos tablas: Personas contiene los datos personales (actualmente unas 17.000) y la Recibos que contiene un histórico de los recibos que se han cobrado a las personas. El campo que relaciona una con otra es es campo C_refer, que se ha llamado así proque en el sistema bancario español el campo se llama Código de referencia.

La tabla personas tiene la siguiente definición
Código:
CREATE TABLE Personas (Apellidos CHAR(40), Nombre CHAR(15), Registro INTEGER, Nif CHAR(8), Sexo CHAR(1), F_nacim DATE, Domicilio CHAR(50), Cpostal CHAR(5), Municipio CHAR(5), Provincia CHAR(2), Profesion CHAR(5), C_electronico CHAR(60), Tlf_part CHAR(9), Tlf_prof CHAR(9), Tlf_movil CHAR(9), Situacion CHAR(1), Correo CHAR(1), F_alta DATE, F_baja DATE, M_baja CHAR(25), N_afiliado CHAR(12), Afiliacion CHAR(2), JLocal CHAR(5), C_refer CHAR(12), Periodo CHAR(1), F_pago CHAR(1), Cuota NUMERIC(10, 2), Entidad CHAR(4), Sucursal CHAR(4), Digitos CHAR(2), Numero CHAR(10), PRIMARY KEY (Apellidos, Nombre, Registro))

Tened en cuenta que esta tabla, que es la maestra, se usa en consultas alfabéticas sobre todo; el campo Registro es un entero que podemos decir es un autoincremetal, aunque no esté definido como tal.

En situaciones normales el campo C_refer debería estar en la clave primaria, pero no todas las fichas tienen esa dato porque no todas las fichas tiene que pagar un recibo y ese dato sólo está en las que si tienen que pagarlo, no sé si me explico. Dado que no todas las fichas tienen esa información, C_refer no puede formar parte de la clave primaria: Puedo encontrarme con dos fichas de personas que se llamen JOSÉ PÉREZ PÉREZ y sin código de referencia. Dicho de otra forma: todos los registros de la tabal Recibos tienen correspondencia en la de personas, pero no al revés; podría añadirlo a la clave actual (aunque C_refer PUEDE ser NULL, cosa que no admiten las claves primarias), de forma que fuera:
Código:
PRIMARY KEY (C_refer, Apellidos, Nombre, Registro)
pero necesito que apellidos y nombre se mantengan en esa clave.
_________________
Un saludo

Necesitamos tu ayuda: Envía AI al 27595 (Coste 1,20 euros + IVA)
Volver arriba
Ver perfil de usuario Enviar mensaje privado Enviar email
Mensaje    Asunto: Responder citando

bien, aun es un poco confuso, pero veamos el ejemplo que has puesto.
tienes dos personas con el nombre JOSÉ PÉREZ PÉREZ
pero estas personas deben encontrarse de la siguiente manera

id - nombre
20 - JOSÉ PÉREZ PÉREZ
1300 - JOSÉ PÉREZ PÉREZ

como vez, se ha implementado un campo llamado ID, que es el identificador unico de cada registro, un buen consejo es usar los identificadores integer como claves primarias y no generar, claves primarias complejas, por cuestiones de Integridad referencial, a veces es un poco confuso, pero es una de las mejores maneras de usar la IR (integridad referencial),
cuando te muevas al registro 1300 de JOSÉ PÉREZ PÉREZ , y usando el metodo del post mas arriba, para declarar la IR dentro de los componentes de BCB, automaticamente, la tabla de recibos, contendra o no contendra los recibos de esta persona; Igual sucede cuando te muevas al registro 20 de JOSÉ PÉREZ PÉREZ, esto hace lo siguiente, te mueves en personas y recibos SIEMPRE contiene los datos que han sido metidos a los recibos.

Esto proceso, permite que no sea necesario los campos Apellido y nombre en la clave primaria.

y tu consulta para los recibos, puede quedar de la siguiente manera

Código:
SELECT C_refer FROM Personas WHERE Situacion = 'A' AND Cuota > 0 AND ID_PERSONA = :ID


Donde :ID es el campo que automaticamente te genera el filtro.

espero que esto te sea mas claro.

1.- Pregunta, por que necesitas los campos Apellido y nombre en la clave primaria?
_________________
Cool
Volver arriba
Ver perfil de usuario Enviar mensaje privado Enviar email MSN Messenger
Mensaje    Asunto: Responder citando

Es que tendría que haber explicado desde el principio las cosas más claritas. ¡En fin! La necesidad de que nombre y apellidos estén en la clave primaria viene dada única y exclusivamente por funcionalidad del programa ya que una de las opciones que tiene es avanzar o retroceder un registro alfabéticamente... Claro que tal como tengo esas funciones ahora me da igual que estén o no en el índice, por que hago una consulta SQL.

Entonces lo que me sugerís es crear la clave primaria sólo con dos campos: código de referencia, para poder relacionar las tablas de personas y recibos, y ese identificador único. Algo así:
Código:
PRIMARY KEY (C_refer, Registro)

y entonces sí podría establecer una FOREING KEY en la tabla de recibos ¿O lo he entendido mal?
_________________
Un saludo

Necesitamos tu ayuda: Envía AI al 27595 (Coste 1,20 euros + IVA)
Volver arriba
Ver perfil de usuario Enviar mensaje privado Enviar email
Mensaje    Asunto: Responder citando

Código:
PRIMARY KEY (Registro)


creo que solo asi te debe funcionar perfectamente. ya que registro es entero y es unico, podemos indicar que
registro = ID

cual quier cosa, podemos estar aqui en contacto. asi que puede te vea mañana por la madrugada del Miercoles
_________________
Cool
Volver arriba
Ver perfil de usuario Enviar mensaje privado Enviar email MSN Messenger
Mensaje    Asunto: Responder citando

Gracias César. Efectivamente Registro sería ID. Una cosa que no me ha quedado clara: si en una tabla defino una clave externa (FOREING KEY), ¿ha de estar declarada en la tabla a la que me refiero, no? Por lo tanto si quiero usar ese campo C_refer como enlace entre la tabla de personas y recibos, habré de declararlo en el índice primario de personas.
_________________
Un saludo

Necesitamos tu ayuda: Envía AI al 27595 (Coste 1,20 euros + IVA)
Volver arriba
Ver perfil de usuario Enviar mensaje privado Enviar email
Mensaje    Asunto: Responder citando

Efectivamente Angel, una de las "reglas" lo pongo entre comillas ya que muchas veces se pasa por algo, es lo siguiente, dentro del Interbas o Firebird, vamos en todos los DB que usen sql, como lenguaje debe existir siempre un campo que sirve como IDentificador, tambien dicen que este debe ser numerico y de tipo entero, aun que puede ser cualquier otro tipo menos blob ni fechas.

en este caso tienes una tabla personas y existe una tabla recibos, la cual esta ultima depende del registro donde te encuentres en la tabla personas.

por lo tanto personas debe tener una clave primaria (Primary Key), que es el campo con el cual se hace referencia en la llave foranea para que "otras" tablas tenga relacion con ella, en este caso recibos, bien puede tener un campo como el siguiente ID_PERSONA que debe ser del mismo tipo de datos que el campo a donde haces la referencia, jeje ya me estoy confundiendo...

mas facil esto debe estar asi, si te das cuenta en unos posts mas arriba, espesifico correctamente la relacion

en esta tabla estan declarados los campos de las personas, voy a cambiar algunas cosas, para que sea mas entendible

Código:
Personas
    Registro integer not null,
    /* los demas campos */
   primary key (registro)

si te das cuenta, "registro" es un campo numerico no nullo, ya que es el campo que va a ser la llave primaria

ahora bien en la siguiente tabla, debes crear un campo que sea del mismo tipo de datos, como opcion puede o no ser NULL

Código:

Recibos
    ID integer not null,       /*identificador del recibo*/
    registro integer,   /*identificador de la persona */     
    primary key(id), /*llave primaria de los recibos*/
    foreign key (registro) references Personas(registro) /*llave foranea a personas*/

para explicar un poco la llave foranea, esto se lee así.

foreign key (registro) references Personas(registro)

crea una relacion de mi campo (registro), con la tabla personas en su campo (registro)

como bien sabemos, registro-personas es una llave primaria, si el campo no esta definido como llave primaria, entonces el DB marca un mensaje de error, ya que no existe una llave que tenga la funcion como tal y se genera un exepcion dentro del motor de base de datos.

ahora si espero que se entienda lo que dije y si hay alguna otra duda aqui estamos
_________________
Cool
Volver arriba
Ver perfil de usuario Enviar mensaje privado Enviar email MSN Messenger
Mensaje    Asunto: Responder citando

Gracias César. Creo que ya empiezo a entender como funciona. Es la primera vez que me enfrento a este tipo de cosas y ando bastante perdido. Os seguiré dando la paliza.
_________________
Un saludo

Necesitamos tu ayuda: Envía AI al 27595 (Coste 1,20 euros + IVA)
Volver arriba
Ver perfil de usuario Enviar mensaje privado Enviar email
Mensaje    Asunto: Responder citando

Hola Angel

Cita:
Es que tendría que haber explicado desde el principio las cosas más claritas. ¡En fin! La necesidad de que nombre y apellidos estén en la clave primaria viene dada única y exclusivamente por funcionalidad del programa ya que una de las opciones que tiene es avanzar o retroceder un registro alfabéticamente... Claro que tal como tengo esas funciones ahora me da igual que estén o no en el índice, por que hago una consulta SQL.


Estas mezclando conceptos nada tiene que ver las claves primaria con los indices, aunque siempre firebird creará un indice para cada clave primaria.

Alguna sugerencias para trabajar con firebird:

- Utilizar dominio en la definicion de los campos, en vez de usar directamente el tipo de datos. Esto tiene inumerables ventajas

Ejemplos:
Código:

create domain dNoRS as
varchar(75)
not null;

create domain dNombre as
varchar(50)
not null;

create domain dApellido as
varchar(40)
not null;

create domain dSexo as
char(1)
not null
check (value in('M','F'));

create domain dSuperficie as
integer
default 0
not null
check (value >= 0);

create domain dID as
integer
default 0
not null;


-Utilizar campos del tipo entero para claves primarias, es este caso yo siempre uso un campo definido con el dominio dID. Este campo será autoincremental y el incremento lo manejarás desde un trigger utilizando generadores

Ejemplo:
Código:


create table Productores(
   Id dId,
   NoRS dNoRS,
   Documento dDocumento,
   Cuit dCuit,
   Direccion dDireccion,
   CP dCP,
   IdLocalidad dId,
   Telefono1 dTelefono,
   Telefono2 dTelefono,
   Celular dCelular,
   Mail dMail
);

create table Localidades(
   ID dID,
   Localidad dTexto50,
   Pedania dPedania,
   IdDepartamento dIdNull,
   IdProvincia dId
);

create table Departamentos(
   ID dID,
   Departamento dTexto50
);

create table Provincias(
   ID dID,
   Provincia dTexto50
);

/*Claves Primarias*/
alter table Productores add constraint pk_Productores primary key (ID);
alter table Localidades add constraint pk_Localidades primary key (ID);
alter table Departamentos add constraint pk_Departamentos primary key (ID);

/*Claves Foraneas*/

alter table Productores add constraint fk_Productores_01 foreign key (IdLocalidad) references Localidades(ID);

alter table Localidades add constraint fk_Localidades_01 foreign key (IdDepartamento) references Departamentos(ID);
alter table Localidades add constraint fk_Localidades_02 foreign key (IdProvincia) references Provincias(ID);

/*Claves unicas*/
alter table Departamentos add constraint unq_Departamentos unique (Departamento);
alter table Provincias add constraint unq_Provincias unique (Provincia);

/*Indices*/
create index idx_Productores_01 on Productores (NoRS);

/*Generadores*/

create generator genIdProductor;
set generator genIdProductor to 0;

create generator genIdLocalidad;
set generator genIdLocalidad to 0;

create generator genIdDepartamento;
set generator genIdDepartamento to 0;

create generator genIdProvincia;
set generator genIdProvincia to 0;

/*Trigger para claves primarias*/

create trigger trgIdProductor for Productores
active before insert position 0 as
begin
   new.ID = gen_id(genIdProductor, 1);
end ^

create trigger trgIdLocalidad for Localidades
active before insert position 0 as
begin
   new.ID = gen_id(genIdLocalidad, 1);
end ^

create trigger trgIdDepartamento for Departamentos
active before insert position 0 as
begin
   new.ID = gen_id(genIdDepartamento, 1);
end ^

create trigger trgIdProvincia for Provincias
active before insert position 0 as
begin
   new.ID = gen_id(genIdProvincia, 1);
end ^



Vemos que IdLocalidad es en Productores una clave foranea (id es en localidad clave primaria)
IdDepartamento e IdProvincia son claves foraneas en Localidades

alter table Departamentos add constraint unq_Departamentos unique (Departamento); esto evita que existan dos o más departamentos con el mismo nombre.

Vemos también que hay un indice en productores para el campo NoRS declarado en forma explicita. También hay dos indices más, no declarados, un para el campo Id, y otro para el campo Idlocalidad.
Olvidate de los indices, una vez creados los administra el propio motor.

-La utilización de vistas y procedimientos almacenados ayuda bastante al desarrollo.

Hay mucho más.


Saludos
TJose
Volver arriba
Ver perfil de usuario Enviar mensaje privado
Mensaje    Asunto: Responder citando

Gracias por las ayudas.
Cita:
Estas mezclando conceptos nada tiene que ver las claves primaria con los indices, aunque siempre firebird creará un indice para cada clave primaria.

En este sentido tendréis que perdonarme si mezclo conceptos; ese por ser novato en estas lides de tablas relacionales. De todas formas: ¿qué diferencia hay entre clave primaria, pro ejemplo, e índice?

Otra cosa, y perdonad si soy un pesado. ¿Qué es eso de «dominios»? ¿Cómo funcionan? Es que con los ejemplos que has puesto no acabo de entenderlo muy bien. Es decir: no veo que ventaja tiene declarar sobre declaración; no comprendo en que beneficia esto:
Código:
create domain dNoRS as varchar(75) not null;
create table Productores(Id dId, NoRS dNoRS,...

¿No es duplicar el trabajo? ¿Por qué es mejor esa definición que esta?
Código:
create table Productores(Id dId, NoRS varchar(75) not null,...

_________________
Un saludo

Necesitamos tu ayuda: Envía AI al 27595 (Coste 1,20 euros + IVA)
Volver arriba
Ver perfil de usuario Enviar mensaje privado Enviar email
Mostrar mensajes de anteriores:   
Cambiar a:  
No puedes publicar temas nuevos en este foro
No puedes responder a temas en este foro
No puedes editar tus mensajes en este foro
No puedes borrar tus mensajes en este foro
No puedes votar en las encuestas de este foro


Hosting WordPress Indedmedia desde 3,99 €
© 2002–2026 Club Builder · Funciona con phpBB.