Ir al contenido


Foto

Evitar datos duplicados en una Tabla (TDBGrid)


  • Por favor identifícate para responder
19 respuestas en este tema

#1 JRichard

JRichard

    Advanced Member

  • Miembros
  • PipPipPip
  • 67 mensajes

Escrito 08 abril 2014 - 01:52

Saludos.

¿Alguien sabe como validar los registros que serán agregados a un TClientDataSet y así evitar de que se agreguen filas duplicadas?

Tengo un TClientDataSet  enlazado a un TDataSource y este último a un TDBGrid, al agregar filas a la tabla (TDBGrid) mediante el TClientDataSet todo funciona bien, ahora debo validar que cada vez que intente agregar datos se emita un mensaje en caso de que esta nueva fila ya exista dentro de la tabla.

Por Ejemplo:

Tengo una tabla con tres campos "Código, Nombre y Teléfono".

Entonces agrego "001, Juan, 0412" luego "002, Pedro, 0412" y al momento de que intente agregar nuevamente "001, Juan, 0412" se emita un mensaje diciendo que dicho registro ya se encuentra dentro de la tabla.

Agradecería mucho la ayuda!  (y)


  • 0

#2 Fenareth

Fenareth

    Advanced Member

  • Administrador
  • 3.486 mensajes
  • LocationMexico City

Escrito 08 abril 2014 - 08:54

Hola amigo JRichard,

Al ClientDataSet puedes especificarle qué campos se consideran como KeyField (tiene un propiedad)... nunca lo he probado pero podrías intentar haciendo la restricción en esa propiedad y que el ClientDataSet se encargue de manejar las inserciones duplicadas...

Saludox ! :)
  • 0

#3 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 08 abril 2014 - 12:47

El sólo hecho de que el campo Código sea una clave primaria hace que sea imposible agregar un duplicado por lo que cualquier intento de insertar un registro con el mismo código llevará a que se levante una excepción por el motor de base de datos que luego el sistema puede capturar.  ;)

Saludos,
  • 0

#4 Fenareth

Fenareth

    Advanced Member

  • Administrador
  • 3.486 mensajes
  • LocationMexico City

Escrito 08 abril 2014 - 12:50

El sólo hecho de que el campo Código sea una clave primaria hace que sea imposible agregar un duplicado por lo que cualquier intento de insertar un registro con el mismo código llevará a que se levante una excepción por el motor de base de datos que luego el sistema puede capturar.  ;)

Saludos,


De acuerdo si y solo si el ClientDataSet está conectado a la base de datos a través de un DataSetProvider y un DataSet, pero eso no lo especifica  ;)

Saludox ! :)
  • 0

#5 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 08 abril 2014 - 01:03

De acuerdo si y solo si el ClientDataSet está conectado a la base de datos a través de un DataSetProvider y un DataSet, pero eso no lo especifica  ;)

Saludox ! :)

Umm  ^o| , yo mucho no he podido explorar sobre la estructura que conlleva el uso del TClientDataSet, si... es un error mio lo se, asi que pregunto: ¿Que acaso se puede saltear al DataSetProvider para lograr vincular todos los objetos?  :|
Además si se está trabajando con campos persistentes creería que ya se hace todo automático, dejando en el campo Field que se trata de un campo clave.
Eso es lo lindo de tener un RAD ¿o no?

Saludos,
  • 0

#6 genriquez

genriquez

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 539 mensajes
  • LocationCali, Colombia

Escrito 08 abril 2014 - 02:09

Hola

El TClientDataSet es muy poderoso, para mi es una de las herramientas más robustas y necesarias para la programación con datos,  lamentablemente es menospreciado.  :(

El TClientDataSet  tiene unas propiedades llamadas FieldDef e IndexDef, en estas propiedades es posible agregar campos e indices, ya sean primarios o no.

Al definir un indice primario es posible evitar tuplas duplicadas.


Estas estructuras se pueden crear de 3 maneras diferentes,  la primera por código.



delphi
  1. Var
  2.   CDS : TClientDataSet;
  3.  
  4. Begin
  5.   CDS := TClientDataSet.Create(Nil)
  6.   CDS.FieldDefs.Add('NombreCampo', TFieldType, Size, Required);
  7.   CDS.FieldDefs.Add('NombreCampo', TFieldType, Size, Required);
  8.   CDS.FieldDefs.Add('NombreCampo', TFieldType, Size, Required);
  9.   CDS.FieldDefs.Add('NombreCampo', TFieldType, Size, Required);
  10.  
  11.   CDS.IndexDefs.Add('NombreIndice','llave1;llave2;llaven',[ixPrimary]);
  12.  
  13.   CDS.CreateDataSet;



La segunda forma de crearla es por el editor del IDE, doble click a la propiedad fieldef y doble click a la propiedad IndexDef, luego con el botón derecho del mouse sobre el componentes, se le dice "Create DataSet"

La tercera es con la utilización de un DataSetProvider y crearlo a partir de un SQL.

Otras cosas que permite hacer el ClientDataSet son:

1. Guardar la información localmente en formato binario o XML.
2. Recuperar una tabla de un archivo local previamente guardado.
3. Cargar datos locales, conectarse a una tabla real y aplicar cambios almacenados
4. Agrupar y sumarizar en varios niveles.
5. Sincronizar los datos y controlar los errores con respecto a la base de datos.
6.  Permite trabajar como memorytables
7. Es compatible desde Delphi4 hasta XE5.

No olvidar incluir en los uses  la unidad  midaslib, de lo contrario se requiere el uso del midas.dll.

Espero les sea de utilidad.

Saludos.

  • 0

#7 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 08 abril 2014 - 02:14

Gracias genriquez por la aclaración.  (y)
Por otro lado, he modificado tu mensaje para darle la etiqueta de código.

Saludos,
  • 0

#8 Fenareth

Fenareth

    Advanced Member

  • Administrador
  • 3.486 mensajes
  • LocationMexico City

Escrito 08 abril 2014 - 02:42

Gracias por la explicación amigo genriquez (y)  :ap:


De acuerdo si y solo si el ClientDataSet está conectado a la base de datos a través de un DataSetProvider y un DataSet, pero eso no lo especifica  ;)

Saludox ! :)

Umm  ^o| , yo mucho no he podido explorar sobre la estructura que conlleva el uso del TClientDataSet, si... es un error mio lo se, asi que pregunto: ¿Que acaso se puede saltear al DataSetProvider para lograr vincular todos los objetos?  :|
Además si se está trabajando con campos persistentes creería que ya se hace todo automático, dejando en el campo Field que se trata de un campo clave.
Eso es lo lindo de tener un RAD ¿o no?

Saludos,


De acuerdo Delphius, pero otra belleza del RAD es que te permite hacer las cosas como mejor te acomoden, y para eso permite que un ClientDataSet se utilice a través de un DataSetProvider y un DataSet... o no

Por ejemplo, recientemente estoy involucrada en el desarrollo de un sistema donde la indicación principal es: sin conexiones abiertas hacia la BD, es decir, hago una consulta (a través de un SP) obtengo los registros y me desconecto de la BD.

En el caso de que necesite mostrar los registros recuperados en TDBGrid es suficiente con que alimente mi ClientDataSet con los datos obtenidos y sea éste el que se encargue de mostrarlos.

Saludox ! :)
  • 0

#9 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 08 abril 2014 - 03:03

A ver,
Fena entiendo el uso y objetivo que propone el TClientDataSet a lo que yo apunto es que independiente de la arquitectura que se utilice para el acceso hacia la base de datos, si en la base de datos existe una adecuada planeación de las claves los componentes que se comuniquen hacia ésta deben reflejarlo.
Es decir que ya sea una inserción directa o indirecta hacia la base de datos dichos componentes por defecto al configurarlos debidamente DEBERIAN ser capaces de efectuar tal advertencia de violación de duplicidad.

Tengo entendido que el TClientDataSet internamente contiene dos caché, una "alfa" y otra "beta" en donde temporalmente se van efectuando las operaciones ABM. Operaciones que luego se van a materializar hacia la base de datos (No pueden, o no deberían quizás debiera decir, quedar en el "limbo" enternamente). Es en este punto en donde el motor bien inteligentemente advierte de una violación de duplicidad y los componentes están diseñados para capturarlas.

¿Que el TClientDataSet no está pensado para capturar un error de este tipo? Creería que no.

Saludos,
  • 0

#10 Fenareth

Fenareth

    Advanced Member

  • Administrador
  • 3.486 mensajes
  • LocationMexico City

Escrito 08 abril 2014 - 03:27

Estoy de acuerdo contigo, pero me parece que no estás viendo un punto en específico:

Creo que la idea de que hay registros temporalmente almacenados en un ClientDataSet, pero cuyo destino final es la base de datos, es absolutamente correcta.

Lo que yo comentaba y acorde con lo que expuso genriquez sí es posible, es que el control de inserción puede hacerse directamente en el ClientDataSet antes de que la información sea almacenada finalmente en la BD. Claro que el motor debería tener el control de las inserciones aunque no se controlara desde Delphi, pero el control es posible en ambos lados.

Saludox ! :)
  • 0

#11 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 08 abril 2014 - 03:45

Es que eso no invalida mi punto amiga.

Si efectivamente el TClientDataSet tiene la forma de efectuar el control, el sólo hecho de intentar hacer un INSERT en su tabla interna llevaría al error; error que el mismo captura. Sigue siendo una imitación del propio motor de base de datos; simplemente se ha cambiado el grado de abstracción.

El intentar redirigir las cosas por otra vía (como por ejemplo lanzar un SELECT previo para buscar si existe ya un registro) es desaprovechar el buen diseño que le han dado los ingenieros de CodeGear.

En mi primer mensaje al hilo simplemente he resumido el punto: ¡ya las herramientas hacen el trabajo! ¡Que no se necesita de más! Estoy notando en JRichard que quiere entrarle a las cosas un tanto desconfiado y con miedo. Tu éntrale amigo; que si luego salen dudas o más errores no es malo, ¡todo lo contrario!  ;)

¿Me explico?
Estoy pensando que hoy no es mi día... o que estuve tan pero tan perdido y fuera de los conceptos de programación que me han cambiado los esquemas.  *-)

Saludos,
  • 0

#12 Fenareth

Fenareth

    Advanced Member

  • Administrador
  • 3.486 mensajes
  • LocationMexico City

Escrito 08 abril 2014 - 03:53

No, es que nadie en ningún momento ha sugerido métodos adicionales de validación de datos al momento de hacer una inserción, simplemente JRichard preguntó cómo podía validar eso desde un ClientDataSet y le hemos expuesto el modo de hacerlo ya sea que éste tenga conexión directa con la BD (en cuyo caso el motor se encargará de la validación), o no (aquí el CDS tendría que hacerse cargo)...

Pero claro que le hemos sugerido que la misma funcionalidad del componente lo realice, sólo le dijimos por dónde tenía que buscar la respuesta...

Saludox ! :)
  • 0

#13 genriquez

genriquez

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 539 mensajes
  • LocationCali, Colombia

Escrito 08 abril 2014 - 04:08

Hola

Para llegar a acuerdo, recuerden de las diferentes formas de creación del contenido de un TClientDataSet,  Si el TClientDataSet está conectado por medio de un TDataSetProvider a una base de datos, el TClientDataSet, se llena con la estructura de la misma y si esta tabla tiene una llave primaria, creará automáticamente los indices y las validaciones.  En otras palabras, el TClientDataSet refleja la estructura de la base de datos.

Pero como mencionan, lo bueno del RAD Studio o Delphi es que te deja hacer las cosas como quieras, así que si no tienes conexión a la base de datos y lo quieres hacer a mano, para tener una tabla en memoria desconectada, también tienes la opción de crear un indice que solo existe en la memoria y hace la validación de tuplas duplicadas.

Habría que revisar las propiedades del TDataSetProvider para ver que puede o no hacer el TClientDataSet conectado a la base de datos,  ya que puede traer el metadata o solo los datos. 

Saludos.



  • 0

#14 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 08 abril 2014 - 04:17

Bueno, entiendo tu punto y lo acepto.
Solamente dejame puntualizar algo: de una u otra forma, tarde o temprano el motor efectuará el control. Si se hace el control en el CDS se estará haciendo las cosas dos veces, lo cual es innesesario y puede evitarse.

Saludos
  • 0

#15 genriquez

genriquez

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 539 mensajes
  • LocationCali, Colombia

Escrito 08 abril 2014 - 04:38

Hola, el modelo del TClientDataSet es el mismo del MemoryTable, primero todo se hace en memoria sin que el servidor tenga participación y si se utiliza el TClientDataSet conectado a la base de datos, obviamente en algún momento se enviará a guardar esta información a la base de datos.

En este punto el que actúa es el TDataSetProvider, este componente se encarga de construir los SQL necesarios (CRUD) para la actualización de los datos contenidos en el TClientDataSet sobre la base de datos.  es en este momento donde el motor de la BD interviene y enviará los respectivos mensajes al TClientDataSet para ser corregidos o tomar acciones sobre ellos.

Esta técnica nos permite hacer aplicaciones en 3 capas y es necesaria para el desarrollo de aplicaciones móviles.  Los problemas que se presentan aquí son las inconsistencias cuando hay varias versiones en diferentes dispositivos,  cada uno tendrá una copia de lo que había originalmente en la base de datos, pero al final cada uno tendrá su propia versión.

Pero no se preocupen, Delphi piensa en todo y tiene los respectivos mecanismos para corregir o darle al usuario la opción de tomar decisiones sobre este tema.

Yo en particular tengo aplicaciones haciendo uso extensivo de esta técnica con celulares y tablet y es bastante eficiente a la hora de la concurrencia. 

Además tengo programas de sincronización entre bases de datos y es bastante eficiente en este tema,  moviendo miles de registros en solo unos segundos.

;)
  • 0

#16 cram

cram

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 832 mensajes
  • LocationMisiones, Argentina

Escrito 08 abril 2014 - 04:57

JRichard,
Si solo conectas desde el TClientDataSet a la grilla (pasando por el DataSource) es obvio que no utilizas un DataSetProvider, por lo tanto, estás haciendo uso de una Tabla de Memoria que bien lo explicó GEnriquez.
Si te sirve de algo, podrías hechar un vistazo a los eventos AfterInsert, BeforeInsert, OnNewRecord para hacer las validaciones. Existe una sentencia para abortar las operaciones: Abort. Creo que por allí está la solución.
Por otro lado como lo dice Delphis puede agregar Índices, desde IndexDefs (análogo a FieldDefs), marcarlo como ixUnique ycapturar los errores.
  • 0

#17 Fenareth

Fenareth

    Advanced Member

  • Administrador
  • 3.486 mensajes
  • LocationMexico City

Escrito 08 abril 2014 - 10:14

JRichard,
Si solo conectas desde el TClientDataSet a la grilla (pasando por el DataSource) es obvio que no utilizas un DataSetProvider, ...


Yo no estaría tan segura amigo... puede ser que su estructura sea simplemente ClientDataSet -> DataSource -> DBGrid o bien, DataSet -> DataSetProvider -> ClientDataSet -> DataSource -> DBGrid... sería cuestión de que él nos los aclarara...

Saludox ! :)
  • 0

#18 JRichard

JRichard

    Advanced Member

  • Miembros
  • PipPipPip
  • 67 mensajes

Escrito 08 abril 2014 - 10:22

Saludos a todos.

Gracias por responder a mi Tema. Me ayudaron a salir de varias dudas. Logre resolver de la siguiente manera:



delphi
  1.  
  2. //Se Activa el TClientDataSet. En mi caso el TClientDataSet se llama cdsServidores
  3.     if not cdsServidores.Active then
  4.     begin
  5.       cdsServidores.Open;
  6.     end;
  7.  
  8. //Posicionamiento en la primera Fila del TClientDataSet.
  9.     cdsServidores.First;
  10.  
  11. //Itero hasta llegar a el final del TClientDataSet
  12.     while not cdsServidores.EOF do
  13.     begin
  14.       try
  15.       //Se toma el Valor del primer campo del TClientDataSet.
  16.       fila := cdsServidores.Fields[0].Value;
  17.  
  18. //Se compara con el codigo del Servidor, si es igual i = 1. FcodigoServidor es una variable que almacena el código del nuevo registro que estoy intentando insertar
  19.       if fila = FcodigoServidor then
  20.         begin
  21.         i := 1;
  22.         end;
  23.       except
  24.       end;
  25.  
  26. //Se itera con el fin de posicionarse en una nueva fila.
  27.       cdsServidores.Next;
  28.     end;


Luego creo una condición donde si i = 1 no se agrega el registro a el TClientDataSet y si es distinto agrego el registro.

Por ahora me funciona perfectamente jejeje, si existe algún error con el tiempo lo iré optimizando, pero por algo se empieza. Gracias a todos!!!  (b)
  • 0

#19 JRichard

JRichard

    Advanced Member

  • Miembros
  • PipPipPip
  • 67 mensajes

Escrito 08 abril 2014 - 10:25

Aclaro algo, la estructura que utilizo es ClientDataSet -> DataSource -> DBGrid[/b].  (y)
  • 0

#20 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 09 abril 2014 - 07:47

Por favor JRichard, emplea las etiquetas de código.
He editado tu mensaje para darle el formato al código.

Saludos,
  • 0




IP.Board spam blocked by CleanTalk.