Ir al contenido



Foto

Editor con Access programado entre todos


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

#41 Wilson

Wilson

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.137 mensajes

Escrito 26 julio 2010 - 08:28

La idea de trabajar con los dataset anidados es que (para mi gusto) da mucha flexibilidad al manejo de las relaciones maestro detalle, supongan el caso de una tipica aplicación de facturas con una tabla maestra en la que está el número de factura, la fecha, el cliente, etc; y una de detalles con las líneas de esa factura, en donde la clave principal de factura estará en cada línea de detalle de esa factura, por cuenta de la Integridad Referencial es necesario que se grabe primero en la DB el resgistro del encabezado de factura para luego poder grabar las líneas de detalles; en una aplicación hecha sin mucho complique, para hacer post en una línea de detalles deberíamos primero hacer post en el encabezado, hasta allí todo bien, pero supongamos que nuestro usuario final decide abortar esa factura cuando ya está grabado el encabezado; ¿que opciones quedan? Encapsular todo el grabado  de las dos tablas en una transacción con mucho más trabajo en la codificación, o hacer uso del poder y la versatilidad de los TClientDataset y los TDatasetProvider y dejar que ellos hagan el trabajo por nosotros y proveer una interfáz muy cómoda para el usuario final. Obviamente esto depende mucho del tipo de proyecto.

Yo me he decantado en este caso por lo segundo, en donde la tabla notas es comparable con el encabezado de factura y las tablas Tags y Tareas serían como las líneas de detalles de dicha factura.

Por este método el usuario final tiene plena libertad de hacer cambios tanto en las tablas maestras como de detalles antes de realizar la inserción pues nada ocurrirá hasta que se pulse el botón aceptar puesto que todo esto se desarrolla en memoria, a la hora de aplicar los cambios de una modificación o de una inserción el TDatasetProvider genera las respectivas sentencias SQL las encapsula en una transacción y aplica los cambios en el orden correcto, todo esto completamente trasnparente a nosotros, por esta razón es que no nos sirven los campos autoincrementales en las tablas involucradas para este caso.

En el próximo Post explicaré la configuración de los objetos del módulo.

Saludos

Paso en el sigui
  • 0

#42 Wilson

Wilson

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.137 mensajes

Escrito 26 julio 2010 - 08:52

Imagen del módulo

Imagen Enviada

Primero que todo un TADoConnection que apunta a nuestra DB.
Luego un TADoQuery (qrNotas) con la sentencia:


sql
  1. SELECT * FROM Notas WHERE Id_Nota = -1


Esta consulta no arroja resultados porque es obvio que no hay notas con Id = -1 pero en cambio nos lleva los metadatos al otro lado del TDatasetProvider que es en realidad un puente entre el TAdoQuery y el TClientDataset.
En qrNotas creamos los campos persistentes, para esto hacemos dobleclik sobre qrNotas y cunado aparezca el editor de campos hacemos click con el boton derecho del mouse (sobre el editor de campos) y pinchamos la opción AddAllfields del popupmenu,  entonces parecerá la lista con los campos de la consulta pinchamos sobre el campo Id_Nota y nos vamos  al Inspector de Objetos, expandimos la opción ProviderFlags y configuramos a True la opción pfInKey, con esto le estamos diciendo al TDatasetProvider (que enlazaremos luego) que Id_Nota es la clave principal de esta tabla, para que lo pueda usar correctamente  ala hora que el genera las sentencias SQL. Esta es la única configuración importante del qrNotas, aunque podrían cambiar los valores del DisplayLabel de los otros campos a su gusto, esto es opcional.


  • 0

#43 Wilson

Wilson

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.137 mensajes

Escrito 26 julio 2010 - 09:17

Posteriormente agregamos un TDataSource (dsNotas) que en su propiedad Dataset apunta a qrNotas.
Luego creamos los  dos querys de detalles qrTags con la sentencia:



sql
  1. SELECT * FROM TAGS
  2. WHERE ID_NOTA = :ID_NOTA



y qrTareas con la setencia:



sql
  1. SELECT * FROM TAREAS
  2. WHERE ID_NOTA = :ID_NOTA



Observe que las dos setencias en la condición WHERE apuntan a la clave foránea Id_Nota
WHERE ID_NOTA = :ID_NOTA  contra un parámetro que tiene que tener obligatoriamente el mismo nombre del campo, para poder realizar correctamente la relación y ante todo las actualizaciones o inserciones. Este detalle es de suma importancia, de otro modo no funciona.

Posteriormente hacemos que tanto qrTags como qrTareas apunten su propiedad DataSource(ir al inspector de objetos) hacia la anteriormente creada dsNotas. Luego configuramos en ambos el parametro yendo al inspector de objetos y haciendo clik en el elipsisButton de la propiedad parameters, una vez aparezca el parámetro id_Nota hacemos que su propiedad DataType sea ftInteger.

Luego creamos los campos persistentes en ambos querys (qrtareas - qrTags) al igual que lo hicimos con qrNotas, una vez creados los campos persistentes en qrTags pichamos el campo Id_Tag y en el inspector de objetos expandimos ProviderFlags y ponemos a True la propiedad pfInKey, hacemos lo mismo en qrTareas con el campo Id_tarea esto con el mismo fin de decirle al TDatsetProvider cual es la clave principal de cada tabla.

Próximo post 
TDatasetProvider
  • 0

#44 Wilson

Wilson

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.137 mensajes

Escrito 26 julio 2010 - 09:36

Con el TDatasetProvider (dpNotas) hacemos que su propiedad Dataset apunte a qrNotas expandimos el arbol Options y configuramos así:
PIncFieldProps a True : Esto hace que  las propiedades que configuramos anteriormente en los querys pasen automáticamente hasta el TClentDataset.

PoCascadeDeletes y poCascadeUpdates a True: Para que se generen las sentecias correctas de borrado y actualización en cascada propios de las relaciones Maestro_Detalles, configuradas previa y directamente en la Db en las relaciones.

poPropogateChanges  a True : Para que se propagen los cambios que se realizan en el lado de los querys hasta el lado del TClientDataset.

PoAllowCommandText  a true : Para permitir  cambiar el CommandText en el lado del Tclientdataset y así permitirnos flexibilidad en las búsquedas.

Finalmente configuramos la propiedad UpdateMode a UpWhereChange para que las setencias que genera el proveedor solo incluya los campos que han cambiado, esto es importante cuando se hace en un entorno cliente servidor poque minimiza el tráfico.

Próximo post

Los TClientDataset
  • 0

#45 Wilson

Wilson

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.137 mensajes

Escrito 26 julio 2010 - 10:38

Vamos con CdNotas, configuramos su propiedad ProviderName para que apunte hacia DpNotas, creamos los campos persistentes igual que hicimos con los querys, luego en el inspector de objetos  vamos a la propiedad active la ponemos a true y luego nuevamente a False, con esta acción todas propiedades que configuramos en el qrNotas se han transmitido a cdNotas gracias a que el el DatasetProvider tenía configurada la propiedad PoIncFiledprops a true.

Observemos detenidamente los campos que se crearon en CdNotas, note que además de los campos de qrNotas están dos nuevos de nombres qrTareas y qrTags . Esta es la esencia de los Datasets anidados.
(Mas adelante veremos que hacemos con ellos).

Pasamos a crear en el editor de campos de cdNotas dos campos de tipo fkLookUp que nos mostraran los verdaderos valores de los campos categoria y creador, estos se llamarn RCategoria y RCreador, para hacer esto hacemos click derecho sobre el editor de campos de cdNotas y escogemos NewField del PopUpmenu, primero colocamos el nombre de Rcategoria, el tipo sera string, yel tamaño 20, escogemos la opcion Lookup en KeyField buscamos Id_categoria, en Dataset escogemos tbCategorias en LookUpKeys escogemos Id_Categoria y en ResulField  Categoria, de igual manera creamos rCreador pero apuntando a tbCreadores.

Hacemos un paréntesis para echar una mirada a la función Id que ejecuta y obtiene el valor del qrGenerador  a partir de  su único campo(numero), puede mirar la propiedad SQL del qrGenerador, luego  mediante el CommandText(cmActualizarGenerador) incrementamos el valor del unico campo de la tabla generador.

Vista esta función pasamos a configurar el evento OnNewRecord de cdNotas que apunta hacia el procedimiento GenerarId, que comparte con los TClientDataset cdTags y cdTareas, los tres TclientDataset obtienen el valor de su clave primaria mediante el procedimiento compartido GenerarId


delphi
  1. Dataset.Fields[0].Value := id

para que cuando regresen los datos al Proveedor ya tengan estos valores para poder generar correctamente las sentencias de actualización.
Igual ocurre con el evento OnReconcileError que lo comparten los tres TClientDatasets mediante el procedimeinto ReConciliar que muestra el mensage de error en caso de que algo haya ido mal.

Solo nos resta configurar  cdTags y cdTareas, observe que para estos dos no configuraremos la propiedad ProviderName como lo hicimos con CdNotas ¿Lo recuerdan? mirenlo de nuevo....
en cambio configuraremos la propiedad DatasetField de cdTags para que apunte a cdNotas.qrTags la propiedad DataSetField del cdTareas para que apunte a cdNotas.qrTareas.

Cree los campos persistentes como de costumbre, abralos y cierrelos para actualizar sus propedades, no olviden compartir los eventos OnNewRecord y OnReconcileError como lo dijimos anteriormente.

A TbCategorias y tBCreadores solo hay que configurarle la conexión, el nombre de la tabla y crear los campos persistentes.

Finalmente asegúrese de que la Con quede connected a False en tiempo de diseño.

Con esto concluimos los objetos de DataModule, luego miraremos algo de la interfaz.


  • 0

#46 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 13.962 mensajes
  • LocationMéxico

Escrito 27 julio 2010 - 07:28

Menuda explicación has dado amigo Wilson solo puedo decir una palabra Excelente (y)

Salud OS
  • 0

#47 esocrates

esocrates

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 102 mensajes
  • LocationArgentina

Escrito 27 julio 2010 - 05:07

Hola Wilson:
Te molesto por una pregunta elemental pero importante para mí.
Revisando el desarrolo de tu explicación no entiendo cuando se usa los dos puntos y cuando no. Por ejemplo en estas líneas de tu desarrolo:

SELECT * FROM Notas WHERE Id_Nota = -1


SELECT * FROM TAGS
WHERE ID_NOTA = :ID_NOTA


y qrTareas con la setencia:

Código: [Seleccionar]
SELECT * FROM TAREAS
WHERE ID_NOTA = :ID_NOTA

Muchas gracias por tu generosidad
Un saludo
  • 0

#48 Caral

Caral

    Advanced Member

  • Administrador
  • 4.241 mensajes
  • LocationCosta Rica

Escrito 27 julio 2010 - 05:53

Hola
Me meto:
Primero decir que la explicacion de Wilson esta impresionante. (y)

Ahora con la duda de esocrates:
En una sentencia SQL los dos puntos se usan antes del nombre de un parametro, estos son los que le dicen a esta sentencia que lo que viene es un parametro.

En la primera sentencia:


sql
  1. SELECT * FROM Notas WHERE Id_Nota = -1


Se define que el campo Id_Nota es integer por esa razon Wilson puso un numero pero tambien se podria usar un parametro, algo asi:



sql
  1. SELECT * FROM Notas WHERE Id_Nota = :num


Y cuando mostremos la sentencia colocaremos el parametro.

No hay regla definida de cuando usar parametros y cuando no, es una cuestion de gusto, comodidad, orden etc., depende de cada quien.

Espero que lo entiendas mejor, si no ya sabes, dilo. (y)
Saludos
  • 0

#49 esocrates

esocrates

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 102 mensajes
  • LocationArgentina

Escrito 27 julio 2010 - 06:06

Hola Caral:
¡Muchas gracias! Este era uno de los problemas que tenía, porque en los tutoriales SQL que he leído no lo indicaban. Me pregunto si esto es sólo válido para Delphi, porque en "Delphi al límite" sí esta´indicado.
Un saludo
  • 0

#50 Caral

Caral

    Advanced Member

  • Administrador
  • 4.241 mensajes
  • LocationCosta Rica

Escrito 27 julio 2010 - 06:12

Hola
No amigo, es un estándar de SQL, en cualquier lenguaje entre ellos delphi.
Mira yo, tan conocedor de otros lenguajes. :D :D
Saludos
PD: Prueba de ello es que en los editores de sql es aceptado y reconocido y estos se usan para cualquier lenguaje.
  • 0

#51 esocrates

esocrates

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 102 mensajes
  • LocationArgentina

Escrito 27 julio 2010 - 06:19

Hola
No amigo, es un estándar de SQL, en cualquier lenguaje entre ellos delphi.
Mira yo, tan conocedor de otros lenguajes. :D :D
Saludos
PD: Prueba de ello es que en los editores de sql es aceptado y reconocido y estos se usan para cualquier lenguaje.

Entiendo. La idea de los parámetros se hace clara ahora. Los ejemplos que he visto mostraban datos integer o cadenas, no parámetros. Tal vez de ahí mi confusión.
Muchas gracias Caral

Nota: un favor ¿Podriás resubir tu nominas.zip.
http://www.delphiacc...hp?topic=1689.0
El archivo está corrupto
Un saludo
  • 0

#52 Caral

Caral

    Advanced Member

  • Administrador
  • 4.241 mensajes
  • LocationCosta Rica

Escrito 27 julio 2010 - 06:32

Hola
Ya actualice el programa, esta vez lo puse en el mismo hilo, no en la zona de descarga ya que no pude modificarla.
Aqui esta.
Saludos
Pd: Gracias por el aviso.
  • 0

#53 esocrates

esocrates

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 102 mensajes
  • LocationArgentina

Escrito 27 julio 2010 - 06:36

Hola
Ya actualice el programa, esta vez lo puse en el mismo hilo, no en la zona de descarga ya que no pude modificarla.
Aqui esta.
Saludos
Pd: Gracias por el aviso.


¡Gracias Caral! Me viene muy bien para seguir con el tema

Un saludo
  • 0

#54 Caral

Caral

    Advanced Member

  • Administrador
  • 4.241 mensajes
  • LocationCosta Rica

Escrito 27 julio 2010 - 06:44

Hola
Con relacion a los parametros:
Ya que he pasado por ese lado recomiendo el uso de los parametros en toda sentencia sql.
Esto lo notaras cuando, algun dia, quieras cambiar de access a firebird, mysql u otra BD.
Access tiene algunas manias con el sql, pero acepta el estándar por eso es mejor aprender y usar este (el estandar).
Asi si quieres cambiar tu programa a cualquier BD el cambio sera mas sencillo y rapido.
No te pegues mucho a access, tarde o temprano te convendrá usar otra BD.
Saludos 
  • 0

#55 esocrates

esocrates

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 102 mensajes
  • LocationArgentina

Escrito 27 julio 2010 - 06:58

Hola
Con relacion a los parametros:
Ya que he pasado por ese lado recomiendo el uso de los parametros en toda sentencia sql.
Esto lo notaras cuando, algun dia, quieras cambiar de access a firebird, mysql u otra BD.
Access tiene algunas manias con el sql, pero acepta el estándar por eso es mejor aprender y usar este (el estandar).
Asi si quieres cambiar tu programa a cualquier BD el cambio sera mas sencillo y rapido.
No te pegues mucho a access, tarde o temprano te convendrá usar otra BD.
Saludos

Te muestro de dónde ha venido mi confusión con el "Where="
http://www.w3schools...l/sql_where.asp
Saludos
  • 0

#56 Caral

Caral

    Advanced Member

  • Administrador
  • 4.241 mensajes
  • LocationCosta Rica

Escrito 27 julio 2010 - 07:17

Hola
Veamos el primer ejemplo de esa pagina:

This is correct:

SELECT * FROM Persons WHERE FirstName='Tove'

This is wrong:

SELECT * FROM Persons WHERE FirstName=Tove


Aqui dice lo que es correcto y lo incorrecto.
Bien:

Cuando trabajamos con un programa sea delphi u otro este tiene que hacer de enlace con la bd.
Para que el programa entienda y traduzca lo que hay en la bd.

En los programas NO es normal una sentencia como la mostrada, ya que da el nombre de la persona.
Normalmente el nombre (en este caso) se busca y para eso usamos las herramientas del programa.

Por ejemplo:
En vez de que la sentencia diga que busque todo con el nombre (Tove) usamos un edit u otro:

Concatendado:
Usamos el signo + antes del nombre del componente.
Ten en cuenta que cuando concatenamos lo hacemos en el programa, por ende no lo entendera ningun gestor de bd como sentencia correcta.


sql
  1. SELECT * FROM Persons WHERE FirstName = +Edit1.text;



Parametros:
Usamos los dos puntos antes del nombre del parametro (el nombre puede ser cualquiera)


sql
  1. SELECT * FROM Persons WHERE FirstName = :N



Como ves, el nombre lo buscara en el edit, no se lo damos directamente.
Ese es el uso de parametros o concatenacion tambien, es para que interactuemos con la bd dandole nosotros los datos a buscar.

Saludos
  • 0

#57 esocrates

esocrates

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 102 mensajes
  • LocationArgentina

Escrito 27 julio 2010 - 07:28

Hola
Veamos el primer ejemplo de esa pagina:


This is correct:

SELECT * FROM Persons WHERE FirstName='Tove'

This is wrong:

SELECT * FROM Persons WHERE FirstName=Tove


Aqui dice lo que es correcto y lo incorrecto.
Bien:

Cuando trabajamos con un programa sea delphi u otro este tiene que hacer de enlace con la bd.
Para que el programa entienda y traduzca lo que hay en la bd.

En los programas NO es normal una sentencia como la mostrada, ya que da el nombre de la persona.
Normalmente el nombre (en este caso) se busca y para eso usamos las herramientas del programa.

Por ejemplo:
En vez de que la sentencia diga que busque todo con el nombre (Tove) usamos un edit u otro:

Concatendado:
Usamos el signo + antes del nombre del componente.
Ten en cuenta que cuando concatenamos lo hacemos en el programa, por ende no lo entendera ningun gestor de bd como sentencia correcta.


sql
  1. SELECT * FROM Persons WHERE FirstName = +Edit1.text;



Parametros:
Usamos los dos puntos antes del nombre del parametro (el nombre puede ser cualquiera)


sql
  1. SELECT * FROM Persons WHERE FirstName = :N



Como ves, el nombre lo buscara en el edit, no se lo damos directamente.
Ese es el uso de parametros o concatenacion tambien, es para que interactuemos con la bd dandole nosotros los datos a buscar.

Saludos

Lo voy entendiendo más ahora.
Me queda una duda sobre el concepto "parámetro"
en


sql
  1. SELECT * FROM Persons WHERE FirstName = :N


"N" es un campo de la tabla? como en el ejemplo de Wilson?



sql
  1. WHERE ID_NOTA = :ID_NOTA



Gracias una vez más

  • 0

#58 Caral

Caral

    Advanced Member

  • Administrador
  • 4.241 mensajes
  • LocationCosta Rica

Escrito 27 julio 2010 - 07:38

Hola
No amigo, N en este caso es simplemente el nombre que le di al parametro, podria haberlo llamado Caral, Maria, Pepe, N, 1a,  etc.........

Traduzcamos este ejemplo a delphi:

Concatenado:


delphi
  1. Adoquery1.sql.text:= 'SELECT * FROM Persons WHERE FirstName = '+Edit1.text;
  2. Adoquery1.Open;


Como ves Buscamos el nombre en el edit1 y abrimos la tabla

Parametros:


delphi
  1. Adoquery1.sql.text:= 'SELECT * FROM Persons WHERE FirstName = :N';
  2. Adoquery1.Parameters[0].Value:= Edit1.text;
  3. Adoquery1.Open;


Buscamos el nombre en el parametro, osea, el programa buscara lo que contiene el parametro, en este caso lo mismo, buscara el nombre en el edit1 ya que como ves en la linea 2 le decimos que el valor del parametro es igual al edit1.

Parece mas simple el primer ejemplo, contatenado, pero es mucho mas claro, preciso, etc el uso de parametros.

No dudes en preguntar si te quedan dudas, no se si me explico bien.
saludos
  • 0

#59 esocrates

esocrates

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 102 mensajes
  • LocationArgentina

Escrito 27 julio 2010 - 07:47

Ahora me parece entenderlo.
Lo voy a estudiar un poco.
Creo que daría para una mini aplicación de ejemplo en algún momento. Pero ya he pedido demasiado.

Caral sos un maestro

Muchísimas gracias
Saludos
  • 0

#60 Caral

Caral

    Advanced Member

  • Administrador
  • 4.241 mensajes
  • LocationCosta Rica

Escrito 27 julio 2010 - 07:54

Hola
Maestro nada, cada dia soy mas novato. :
Cuando gustes abre un hilo, create una BD con una tabla o dos y le damos duro al uso de concatenacion, parametros y otras curiosidades que tiene este lenguaje. (y)
Te insisto en el uso de parametros y creo que si no lo manejas bien deberias de entarle mucho ya que es muy Practico cuando se quiere hacer un insert, delete, update etc con sql, sobre todo cuando son muchos campos.
Me alegra servirte de algo amigo. (y)
Saludos
  • 0