Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Conexión con bases de datos (https://www.clubdelphi.com/foros/forumdisplay.php?f=2)
-   -   Insercción de registros en clientdataset (https://www.clubdelphi.com/foros/showthread.php?t=76289)

briast 21-10-2011 18:20:12

Insercción de registros en clientdataset
 
Hola. Utilizo Delphi 5 aunque esta prueba la he realizado también con Delphi 2010 y ocurre lo mismo. Os comento. Tengo en un form un dbgrid, un clientdataset y su correspondiente datasource para unirlos. Un par de cajas de texto (Tedit) y un botón agregar.
En el clientdataset he creado dos campos "N1" integer y "C1" string. En realidad lo que me interesa es el integer.
La idea es ir insertando en el clientdataset registros pero ordenados por el campo N1. No puedo poner índices por dicho campo porque realmente el problema es más complejo, y no es lo que trato de resolver ahora sino otra cuestión que os detallo a continuación.
El procedimiento consiste en que cada que vez que se pulse agregar el proceso recorre el dataset e inserte el registro en la posición correcta. Os pongo aquí el código del evento on click:

Código Delphi [-]
...
var insertado:boolean;
begin
       clientdataset1.first;
       insertado:=false;
       while (not clientdataset1.eof) and (not insertado) do
       begin
              if clientdataset1.fieldbyname('N1').value>strtoint(edit1.text) then
              begin
                     // Insertamos en la posición actual
                     clientdataset1.insert;
                     clientdataset1.fieldbyname('N1').value:=strtoint(edit1.text);
                     clientdataset1.fieldbyname('C1').value:=edit2.text;
                     clientdataset1.post; 
                     insertado:=true;
              end
              else clientdataset1.next;
       end;

       if not insertado then
       begin
                     // Insertamos al final
                     clientdataset1.append;
                     clientdataset1.fieldbyname('N1').value:=strtoint(edit1.text);
                     clientdataset1.fieldbyname('C1').value:=edit2.text;
                     clientdataset1.post; 
       end;
end;

Al final tenemos un clientdataset con los registros perfectamente ordenados por N1 con independencia del orden de ejecución. El problema es el siguiente:

- En Windows 7 32 bits el clientdataset final queda perfectamente ordenado.
- En Windows XP o Windows 7 64 bits, los insert hacen lo mismo que append y el clientdataset final está totalmente desordenado.

¿Alguna sugerencia?
Gracias

Al González 21-10-2011 18:54:32

Es poco probable que la causa de lo que describes se encuentre en la versión del sistema operativo. Aún así, puede que cada Windows tenga instalada una versión diferente de la biblioteca MIDAS.dll (el corazón de los client data sets). ¿Podrías verificar qué versión de ese archivo es el que se está usando en cada caso? Búscalo en el directorio de Windows, dale otro nombre temporalmente y ejecuta nuevamente la aplicación. Si te aparece un mensaje de error al respecto significa que ese es el que se está utilizando; en las propiedades del archivo puedes ver de qué versión se trata.

Pero dinos, ¿cuál es la razón por la cual no puedes establecer un simple ClientDataSet1.IndexFieldNames := 'N1'?

Saludos. :)

briast 21-10-2011 22:38:44

Ok. Revisaré eso que indicas. La verdad es que no lo había pensado. Gracias.

Respecto a porqué no puedo utilizar un índice por esa columna es porque los datos que deben insertarse ordenadamente en el clientdataset no pueden ordenarse por una columna o varias columnas ya que dependiendo del "nivel" pueden tener una ordenación u otra. Así pues, aprovecho ya que lo preguntas y pongo aquí la cuestión y a ver si hay una solución más eficiente que la que yo hago.

Tengo en una tabla de Firebird un conjunto de registros ordenados a partir de un proceso de cálculo. A modo de ejemplo inserto aquí una tabla con datos ficticios que generaría dicho proceso:

Código:

Nº registro  |  Compañía  |  Categoría  |  Modelo  |  Datos año actual  | Datos año anterior | Variación
-----------------------------------------------------------------------------------------------------
    1                  1                null            null                300                        100                200
    2                  1                1              null                100                          25                  75
    3                  1                1                1                  20                            2                  18
    4                  1                1                2                  39                            6                  33
    5                  1                1                3                  41                            3                    34
    6                  1                2                2                  45                              ...            ....
              ....
    n                5                null              null                  30                          15                    15
  n+1                5                1                null                  10                            3                    7
                  ....

La primera fila de la tabla contiene los totales de para la compañía 1. La segunda, los totales para la compañía 1 categoría 1, y así sucesivamente.
La cuestión ahora es que el usuario puede decidir que quiere los datos ordenados de la siguiente forma:
- Por compañía según los datos del año actual
- Dentro de cada compañía por categoría según los datos de variación
- Dentro de cada categoría por los datos del año anterior.

Evidentemente esto es un ejemplo que ilustra el problema. Ahora tengo que recorrer esta tabla y construir una nueva tabla con los datos perfectamente ordenados según este nuevo criterio. El algoritmo que he preparado lee los datos de esta tabla y los va insertando en un clientdataset, pero teniendo en cuenta que cada grupo debe ir junto con sus datos. Por este motivo no puedo establecer un índice por una columna ya que esto ordenaría los datos con independencia de los valores de los campos 2, 3 y 4 y eso no valdría.
Ahora mismo lo que hago es ir leyendo e insertando de forma ordenada en el clientdataset y aprovechando esa propiedad que creía que tenía que permitía insertar en medio. El motivo es que el primer algoritmo que diseñé era similar pero usaba una columna del clientdataset para llevar el orden, y cuando quería insertar uno tenía que renumerar todos los siguientes para abrir el hueco, lo cual hacía que el proceso tardase mucho.
El algoritmo que me sale es de orden N*N en el peor caso (leemos todos los datos y siempre insertamos al final), de orden N *1 en el mejor caso (siempre se inserta al principio por lo que sólo leemos un registro) y de orden N * (N/2) en el caso promedio, siempre usando el sistema de sólo insertar en el clientdataset y que el solo abra el hueco sin necesidad de llevar un orden a través de una columna.
Otra implementación podría ser llevando el orden en un TIntList o similar, pero entonces no habría que hacer actualizaciones pero si muchos setkey y gotokey para ir comparando el registro por el que vamos con el nuevo a insertar.

Gracias por vuestra ayuda.
Un saludo

Al González 22-10-2011 00:08:35

Bueno, de entrada no sé si esa sea la mejor estructura para la tabla pero, dándola por sentada, cuando el usuario desee la información ordenada subordinadamente por esas tres columnas, creo que una consulta SQL con el Order By adecuado lo resolvería. Cierto, el valor Null quizá sea un problema pero podría arreglarse de varias maneras, por ejemplo, añadiendo a la tabla Firebird algunos campos computados especiales para este propósito.

Por otra parte, no sé si hayas caído en la cuenta de que la propiedad IndexFieldNames puede llevar más de un nombre de campo, separándolos con punto y coma (IndexFieldNames := 'C1;C2;C3...").

Saludos. :)

Al.

briast 22-10-2011 09:50:38

Hola. Gracias por la respuesta pero creo que no me has entendido o no me he explicado bien. Sé que puedo poner mas de un campo por índice, pero tampoco serviría puesto que el criterio de ordenación varía en función del contenido de los registros.
Voy a preparar otro ejemplo más simple a ver si me explico mejor.

briast 22-10-2011 13:26:54

Bueno, aquí va otro ejemplo quizás más claro. Partimos de la siguiente tabla, cuyos registros están ordenados por el número de registro, que se genera al procesarse la tabla y generarse. Realmente, como se puede ver está ordenado por Cat1+Cat2.

Código:

NºReg  |  Cat1    |  Cat2  |    Res1  |  Res2  |  Res3
--------------------------------------------------------
    1            1          null        100          200        150
    2            1            1          25            35          50
    3            1            2          45            18          25
    4            1            3          35            27          10
    5            2          null          27          350        200
    6            2            1            5            100        87
    7            2            5          10            34          50
    8            3          null          45            17          24
    9            3            2          67            12          34
  10            3            9          34            23          21

Las filas que tienen un null en Cat2 corresponden con los totales de Cat1 para ese grupo (todos los que tienen el mismo valor en Cat1). La forma de calcular los datos totales no es importante para lo que quiero explicar.

Ahora el usuario solicita que se le muestren los datos ordenando las Cat1 por el campo Res1 y luego los registros de cada Categoría 1 que se ordenen por el campo Res2.

La tabla resultante debería ser la siguiente:

Código:

NºReg  |  Cat1    |  Cat2  |    Res1  |  Res2  |  Res3
--------------------------------------------------------
    1            2          null          27          350        200
    2            2            5          10            34          50
    3            2            1            5            100        87
    4            3          null          45            17          24
    5            3            2          67            12          34
    6            3            9          34            23          21         
    7            1          null        100          200        150
    8            1            2          45            18          25
    9            1            3          35            27          10
  10            1            1          25            35          50

Como puedes ver no se puede usar índices ya que algunos registros deben ordenarse por una columna junto con su subgrupo de registros, y luego a su vez este subgrupo va ordenado por otra columna.
El algoritmo más sencillo que se me ocurre aplicar es recorrer la tabla principal e ir insertando los registros en un clientdataset auxiliar pero de forma ordenada. Así el algoritmo me sale de orden N para el recorrido principal y luego el recorrido de insercción de orden N también ya que hay que leer los registros insertados y localizar donde hay que poner el nuevo que estemos procesando. Esta segunda tabla cada vez contiene más registros a medida que se van insertando. Si siempre insertáramos al principio, sólo habría que realizar una lectura por lo que sería el algoritmo global de Ord(n*1). Si estamos en el peor caso en el que hay que insertar al final, entonces siempre hay que leerse todos por lo que será de Ord(n*n). Por supuesto, al insertar hay que tener en cuenta que el registro esté dentro del grupo correcto pero esto es ya una cuestión de la comparación a realizar en cada registro.

Tal como lo tenía programado, al insertar en el clientdataset abría "hueco" y los registros siguientes se desplazaban hacia abajo. De esa forma no necesitaba actualizar los números de registro de los campos siguientes para abrir el hueco (y eso si que tarda aunque sea un recorrido y actualización). Pero por el problema que os planteo que me ocurre no veo otra solución mejor.

Un saludo

Al González 22-10-2011 19:41:49

Cita:

Empezado por briast (Mensaje 416470)
Ahora el usuario solicita que se le muestren los datos ordenando las Cat1 por el campo Res1 y luego los registros de cada Categoría 1 que se ordenen por el campo Res2.

La tabla resultante debería ser la siguiente:

Código:

NºReg  |  Cat1    |  Cat2  |    Res1  |  Res2  |  Res3
--------------------------------------------------------
    1            2          null          27          350        200
    2            2            5          10            34          50
    3            2            1            5            100        87
    4            3          null          45            17          24
    5            3            2          67            12          34
    6            3            9          34            23          21         
    7            1          null        100          200        150
    8            1            2          45            18          25
    9            1            3          35            27          10
  10            1            1          25            35          50


Esto es más difícil que aquello de encontrar al gato en la imagen. :p

Disculpa es que no entendí la parte que te pongo en negritas con relación a la tabla resultante. :o

De todas formas, si tuviéramos que recurrir a la inserción que venías realizando, creo que bastaría con que tu campo en el IndexFieldNames fuera de tipo flotante (un InternalCalc TFloatField, por ejemplo).

Así, cada vez que localizaras los dos registros entre los cuales debe insertarse, le asignarías al nuevo registro un valor que se encuentre entre esos dos, y sin tener que modificar nada de los registros ya agregados.

OrdenNuevoRegistro = (OrdenRegistroAnterior + OrdenRegistroSiguiente) / 2

briast 23-10-2011 11:20:56

Hola. Gracias por la ayuda.
Esa solución de insertar con un código también la pensé, pero el problema es que en el peor de los casos tendríamos que insertar siempre entre dos sucesivamente por lo que los códigos deberían tener hueco para 2 elevado al número de registros. Si tenemos que ordenar 10000 registros fíjate los números que ya me irían saliendo, y además eso entre dos, porque luego habría que ir poniendo saltos de la misma distancia. Da igual que lo hagamos con enteros que con decimales, la distancia debe ser similar.
Creo que no podría hacerlo así.

Al González 23-10-2011 19:35:43

Cita:

Empezado por briast (Mensaje 416470)
[...] ordenando las Cat1 por el campo Res1 y luego los registros de cada Categoría 1 que se ordenen por el campo Res2 [...]

¿?

Tal vez haya que explicar un poco mejor la forma en que son ordenados esos registros. Como te comenté en el mensaje anterior, no comprendí esa parte. :o

Y es que una vez entendido el algoritmo, o al menos esclarecidos grosso modo los criterios usados para el ordenamiento, más ideas de solución pueden surgir. :)

jo3litro 30-05-2017 17:18:23

Buenas, a todos.

Me sirvio este hilo para saber como insertar mis datos, ahora bien, y si tengo un generador, como le digo para que me inserte el valor del generador ??

Gracias.


La franja horaria es GMT +2. Ahora son las 12:29:09.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi