Page 1 of 3

SubNtx

Posted: Tue Sep 09, 2008 12:23 pm
by Antonio Linares
Hay algún antiguo usuario de SubNtx y Clipper ?

Tenemos curiosidad en realizar unas pruebas de velocidad, gracias :-)

un usuario!

Posted: Tue Sep 09, 2008 12:28 pm
by triumvirato
Soy usuario de Subntx desde hace muchos años...
En qué puedo ayudar? Me interesa muchísimo esa librería.

Saludos!

Posted: Tue Sep 09, 2008 12:36 pm
by Antonio Linares
David,

Este ejemplo está realizado con xHarbour usando un índice temporal creado en memoria, sobre una DBF de 2.600.000 registros (mide 1 giga).

La construcción del índice tempotal tarda 53 segundos, trabajando en el disco local. Te importaria hacer una busqueda similar con SubNtx y decirme que tiempo obtienes ? Puedes crear una DBF de este tamaño usando la utilidad DbCre8.exe.

Gracias! :-)

Code: Select all

REQUEST DBFCDX

function Main()

   local nStart, cCondicion := "( 'ESM' $ Art->Art_Nom .or. 'ESM' $ Art->Art_Tam .or. 'ESM' $ Art->Art_Col ) .and. ( 'ROJO' $ Art->Art_Nom .or. 'ROJO' $ Art->Art_Tam .or. 'ROJO' $ Art->Art_Col )"

   MsgInfo( File( "art.dbf" ) )
   USE Art VIA "DBFCDX" ALIAS "Art"
   MsgInfo( Alias() )
   nStart = Seconds()
   INDEX ON Art->Art_Nom TO "TEMP" FOR &cCondicion MEMORY
   MsgInfo( ( Seconds() - nStart ) / 60 )
   
return nil 

Posted: Wed Sep 10, 2008 7:41 am
by triumvirato
Perdona por la tardanza Antonio.

SubNtx() necesita de un índice maestro, desde el cuál extraer registros y generar el subíndice, por lo que creamos un índice, donde se incluyan _ de búsqueda, en tu caso Antonio, lo he llamado Art_Nom.NTX que será dentro del índice en cual buscará SubNtx().
La clave del índice, los tres campos de tu ejemplo: ( Art_Nom + Art_Tam + Art_Col )

Adaptando tu código a lo que sería esa búsqueda con SubNtx():

Code: Select all


 local nStart, result_code, cCondicion := "ESM*ROJO"

 CLEAR
 USE Art
 nStart = Seconds()
 result_code = SubNtx( "Art_Nom.NTX", "Temp.NTX", "", "", "*"+cCondicion+"*" )
 @1,1 SAY Str( ( Seconds() - nStart ) )
 @2,1 SAY Str( result_code ) // muestra el número de registros copiados al subíndice generado por SubNtx()
 INKEY( 0 )
 SET INDEX TO Temp
 DbEdit ( 5, 1, 12, 75, { "Art_Nom", "Art_Tam", "Art_Col" } )

 CLOSE ALL
 QUIT
Esta prueba, en las condicones que pedías (2.600.000 registros, 1Giga), ha tardado 3,46 segundos, bastante menos que los 53 segundos que con tu código e índices CDX.
¿Cosas de Meigas? :D

Posted: Wed Sep 10, 2008 7:56 am
by Antonio Linares
David,

> ha tardado 3,46 segundos

Gracias por tus pruebas! :-)

Si, eso me habian comentado, que SubNtx() es muchisimo más rápido que un índice condicional (y generado en memoria) de Harbour/xHarbour.

Sería interesante estudiar como trabaja SubNtx() y aplicarlo a Harbour/xHarbour.

Sigue activa la empresa que produce el SubNtx ? En caso contrario, alguien tendría acceso a su código fuente (en caso de que haya sido liberado) ?

Posted: Wed Sep 10, 2008 12:04 pm
by surGom
Antonio fijate si es esto lo que buscas

http://www.bookcase.com/library/softwar ... ipper.html

Luis

Otra prueba!

Posted: Thu Sep 11, 2008 6:57 am
by triumvirato
Antonio,

Hecha la prueba con:

Code: Select all

cCondicion := "( ( 'ESM' $ ( Art->Art_Nom + Art->Art_Tam + Art->Art_Col ) ) .and. ( 'ROJO' $ ( Art->Art_Nom + Art->Art_Tam + Art->Art_Col ) ) )"
por ser más semejante al modo de trabajo de SubNtx() como me has sugerido, pero el tiempo siguen siendo los 53 segundo exactos, contra los 3,46 de SubNtx().

El código fuente de la SubNtx() creo recordar (en algún sitio lo leí), que fué liberado o que se distribuía con la propia librería, en el enlace que pone el compañero Luis, dentro de un Zip, existen dos ficheros exe's autoextraíbles, dentro de uno de ellos iban esos fuentes en "C", pero requerían de una contraseña. Esa es la versión 2.0 donde no se había liberado y creo que en las últimas (6.0) ya se distribuía...
Espero que alguien de más luz sobre el asunto.

Saludos!

Posted: Thu Sep 11, 2008 11:48 am
by Antonio Linares
Buscando por google he encontrado este código publico que es realmente extraordinario, tan solo hay que adaptarlo con unos _ a 32 bits. Con esta rutina ya tenemos la mitad de la funcionalidad de SubNtx:

http://mediasrv.ns.ac.yu/extra/fileform ... tx/ntx.txt

Copio aqui el contenido de la página, para que no se pierda :-)

Code: Select all

NTX file format
---------------
Version of this document: 2.0
by Cesar A. Gil, January 06, 1997


Introduction

	The NTX file format is the format of index files used by
   Clipper. It is much more efficient than NDX files, with respect to
   performance and file size (usually more than twice the speed, and
   only 80% of the size of an equivalent NDX file). The extension is also
   different to indicate that its format is not the same as NDX.

	The NTX file stores a B-tree. A B-tree consists of nodes pointing
   to sub-nodes.

	The B-tree allows you to traverse the nodes in a depth-first
   order, thus allowing you to retrieve the keys in crescent order. It also
   allows you to retrieve a given key or its closest-matching key with an
   efficient algorithm, since you must only traverse a few nodes to
   locate a specific record. However, as far as theory is concerned,
   nothing beyond a diagram of a B-tree will be given here.

	Updating an NTX file may be very difficult, since after a
   change you must keep the B-tree balanced. However updating the B-tree
   is a laborious task, the information given here may easily allow you
   to read the NTX file.


Diagram of a B-tree

			    root node
			    -----------------------
			    | . | 50 | . | 60 | . |
			    -----------------------
			      |        |        |
	     +----------------+        |        +-----------+
	     |                         |                    |
  -----------------------   -----------------------   --------------
  | . | 10 | . | 20 | . |   | . | 52 | . | 56 | . |   | . | 62 | . |
  -----------------------   -----------------------   --------------
    |        |               |        |        |       |        |
   ///       |              ///      ///      ///     ///      ///
	     |
	-----------------------
	| . | 15 | . | 16 | . |
	-----------------------
	  |        |        |
	 ///      ///      ///

   In this diagram we have assumed that each node can hold up to 2 keys,
   however a B-tree can be specified to have any maximum number of keys
   per node (n >= 2). A node doesn't need to be used up to the maximum. For
   example, in this diagram we have a node which is not fully used (the node
   which only contains the value 62).

   We also didn't care here to create a balanced tree, as would happen
   in a real situation, since we are concerned about giving the reader
   a feeling of how it is to traverse a B-tree. The diagram is also
   intended to help visualize the structure of the NTX file.

   Each node contains pointers between its key values. For example, look
   at the root node. The pointer to the left of the value 50 points to a
   sub-tree that contains every key lesser than 50. The pointer bewteen the
   values 50 and 60 points to a sub-tree containing every value between 50
   and 60. The pointer after the value 60 points to a sub-tree containing
   all values greater than 60.

   At some given time we will arrive at a point where there will be no more
   branching. We indicate that by an empty pointer (represented here by
   ///). In NTX files, an empty pointer (that does not point to a
   sub-tree) contains a 0.

   Now it is very easy to see that, if you traverse the tree in a depth-
   first search, you'll retrive all the key values in order. It's also
   very easy to define an algorithm to locate a given key efficiently
   in the tree. The difficult part is updating the B-tree, by inserting,
   changing or removing records, because the B-tree must be kept balanced.
   The algorithm for doing so is very complex and literature is available
   on the subject.


Description of an NTX file

    The NTX file consists of pages of 1K.
    The first 1K page contains a header.
    The other pages contain nodes of the B-tree. (Each 1K page will hold
      a node).

    Header structure:

	Offset
	  00h  unsigned signature - contains 09 if good NTX file
	  02h  unsigned version   - version of indexing system that the
				    file was created under
				    (0x1 is typical but may change)
	  04h  long root          - file offset (within the NTX file)
				    where the root page is.
	  08h  long free_page     - file offset (withing the NTX file)
				    where the first of a list of free
				    pages is. A 0 value ends the list.
				    The Clipper index system is intended
				    to reuse free pages.
	  0Ch  unsigned item_size - the size of the item.
	  0Eh  unsigned key_size  - the size of the index key
	  10h  unsigned key_decim - number of decimal positions for the
				    key, if it is a numeric key.
	  12h  unsigned max_item  - maximum number of items per page.
	  14h  unsigned min_item  - minimun number of items per page
				    (max_item/2 is typical)
	  16h  char key_expr[256] - an array containing the key
				    expression for the index.
				    The expression must be terminated by
				    null (ASCII 0) and its length cannot
				    be greater than 256 bytes.
	  272h unsigned unique    - contains the value of the UNIQUE
				    option for the index.
				    (1=index is UNIQUE; 0=isn't UNIQUE)

    Comments:

    (1)	The size of the index key (key_size) is determined when you issue
	the command

	    INDEX ON key_expr TO file.ntx

	The key_expr is evaluated for each record of the data file.
	The value returned by evaluating key_expr has its size calculated.
	It is assumed that all the evaluations of key_expr will return
	values of the same type and length.
	If you define a key_expr that returns values of different lengths,
	the Clipper indexing system will give unpredictable results and,
	eventually, an error will happen.

	An example (of what you shouldn't do) is:

	    INDEX ON TRIM(CLIENT_NAME) TO CLIENTS


	Based on the determined key_size, Clipper will calculate how
	many items a page can hold and will thus determine max_items
	and min_items. Remember, each item holds a key value, whose size
        depends on the key_size. That's why the item size is dynamically
        determined.



    Node structure:

	Offset
	  00h  unsigned item_count - how many items this particular
				     page holds.
	  02h  unsigned item_offset[item_count] - holds the offsets, within
		the page, where the items are located.
	  ??   item area

	       The size of each item is: 8 + key_size.
	       The last item doesn't contain a key value, only a pointer
		 to a sub-tree.

	       Each item has the following structure:

	       long page_offset - file offset (within NTX file) where
				  sub-tree is. If there's no subtree,
				  a 0 is stored.
	       long recno       - record number for this key value
				  (in the DBF file)
	       char key_value[key_size]




	   For example, the node:

			    -----------------------
			    | . | 50 | . | 60 | . |
			    -----------------------
			      |        |        |
			   to page  to page   to page
			     at       at       at
			  offset x  offset y  offset z

	   Will be represented in a page containing:

	   Offset
	     00h  unsigned item_count = 3
	     02h  unsigned item_offset[0] = 08h
	     04h  unsigned item_offset[1] = 12h
	     06h  unsigned item_offset[2] = 1Ch
	     08h  item[0] :
		      long page_offset  : x
		      long recno        : DBF record number pointed by
					 key value 50
		      char key_value[2] : '50'
	     12h  item[1] :
		      long page_offset  : y
		      long recno        : DBF record number pointed by
					 key value 60
		      char key_value[2] : '60'
	     1Ch  item[2] :
		      long page_offset  : z


Conclusion:

    The NTX file structure is easy to access. The main concern is about
    the fact that each NTX file will have items of different sizes and
    pages with different number of items.


Source code example - NTXVIEW.C :

    Author     : Cesar A. Gil
    Date       : January 06, 1997
    Language   : C
    Use        : NTXVIEW file.ntx
    Description: lists the contents of the ntx file's header, then
		 traverses all the tree in depth-first order, listing all
		 the keys in order.


    ---------------------------------------------------------------

    /*
     * ntxdump.h
     */
    #define MAX_KEY  256
    #define BUF_SIZE 1024

    typedef struct {
	unsigned type;
	unsigned version;
	long     root;
	long     next_page;
	unsigned item_size;
	unsigned key_size;
	unsigned key_dec;
	unsigned max_item;
	unsigned half_page;
	char     key_expr[MAX_KEY];
	char     unique;
    } NTX_HEADER;

    typedef struct
    {
	long page;
	long rec_no;
	char key[1];
    } NTX_ITEM;

    typedef struct
    {
	unsigned item_count;
	unsigned item_offset[1];
    } NTX_BUFFER;

    ---------------------------------------------------------------

    /*
     * ntxview.c
     */
    #include "stdio.h"
    #include "stdlib.h"
    #include "fcntl.h"
    #include "io.h"

    #include "ntxview.h"

    NTX_HEADER ntx_header;
    int        ntx_handle;

    void view_page(long page_offset);


    void main(int argc, char ** argv)
    {
	if( argc != 2 ) {
	    printf( "Use: ntxhead arquivo.ntx\n" );
	    exit(1);
	}

	ntx_handle = open(argv[1], 0x8000);
	if( ntx_handle == -1 ) {
	    printf( "Open error: %s\n", argv[1] );
	    exit(1);
	}

	/* Reads header */
	if( read(ntx_handle, &ntx_header, sizeof(NTX_HEADER))
	     != sizeof(NTX_HEADER) ) {
	    printf( "Error reading header of %s\n", argv[1] );
	    exit(1);
	}

	/* Prints header information */
	printf( "***** NTX header *****'n" );
	printf( "Signature %xh\n", ntx_header.type );
	printf( "Version %xh\n", ntx_header.version );
	printf( "Root %lxh\n", ntx_header.root );
	printf( "Next page %xh\n", ntx_header.next_page );
	printf( "Item size %xh\n", ntx_header.item_size );
	printf( "Key size %xh\n", ntx_header.key_size );
	printf( "Key decimals %xh\n", ntx_header.key_size );
	printf( "Max item %xh\n", ntx_header.half_page );
	printf( "Key expr: '%s'\n", ntx_header.key_expr );
	printf( "Unique: %d\n", ntx_header.unique );

	view_page(ntx_header.root);

	close(ntx_handle);
    }


    void view_page(long page_offset)
    {
	char       * page;
	NTX_BUFFER * buffer;
	NTX_ITEM   * item;
	unsigned   i;

	/* Allocates page */
	page = malloc(BUF_SIZE);
	buffer = (NTX_BUFFER *) page;

	if( page == NULL ) {
	    printf( "Error allocating memory.\n" );
	    exit(1);
	}

	/* Moves to the given page offset within the file */
	lseek(ntx_handle, page_offset, 0);

	/* Reads page */
	if( read(ntx_handle, buffer, BUF_SIZE) != BUF_SIZE ) {
	    printf( "Error reading page %lxh\n", page_offset );
	    exit(1);
	}

	/* Traverses the vector, item by item */
	for( i = 0; i < buffer->item_count; i ++ ) {
	    item = (NTX_ITEM *) (page + buffer->item_offset[i]);
	    if( item->page != 0 )
		view_page(item->page);
	    printf("dbf recnum: %5lx   Key: %.*s\n", item->rec_no,
		ntx_header.key_size, &item->key);
	}

	/* Handles last pointer */
	item = (NTX_ITEM *) (page + buffer->item_offset[buffer->item_count]);
	if( item->page )
	    view_page(item->page);

	/* Deallocates page */
	free(page);
    }

    ---------------------------------------------------------------


- end of document -

Posted: Thu Sep 11, 2008 12:01 pm
by Antonio Linares
Aqui teneis el código anterior adaptado a 32 bits y con unas pequeñas trazas para entender mejor como se recorren las páginas del fichero NTX.

Podeis construirlo asi:
go.bat

Code: Select all

SET PATH=c:\bcc55\bin
c:\bcc55\bin\bcc32 ntxview.c
Podeis descargar el fuente y el EXE desde aqui:
www.fivetechsoft.com/files/utilities/ntxview.zip

Para probarlo: ntxview.exe customer.ntx > info.txt

ntxview.c versión en 32 bits:

Code: Select all

  #define MAX_KEY  256
  #define BUF_SIZE 1024

  typedef struct {
	unsigned short int     type;
	unsigned short int     version;
	long     root;
	long     next_page;
	unsigned short int     item_size;
	unsigned short int     key_size;
	unsigned short int     key_dec;
	unsigned short int     max_item;
	unsigned short int     half_page;
	char     key_expr[MAX_KEY];
	char     unique;
  } NTX_HEADER;

  typedef struct
  {
	long page;
	long rec_no;
	char key[1];
  } NTX_ITEM;

  typedef struct
  {
	unsigned short item_count;
	unsigned short item_offset[1];
  } NTX_BUFFER;

  #include "stdio.h"
  #include "stdlib.h"
  #include "fcntl.h"
  #include "io.h"

  NTX_HEADER ntx_header;
  int        ntx_handle;

  void view_page(long page_offset);


  void main(int argc, char ** argv)
  {
	if( argc != 2 ) {
	    printf( "Use: ntxhead arquivo.ntx\n" );
	    exit(1);
	}

	ntx_handle = open(argv[1], 0x8000);
	if( ntx_handle == -1 ) {
	    printf( "Open error: %s\n", argv[1] );
	    exit(1);
	}

	/* Reads header */
	if( read(ntx_handle, &ntx_header, sizeof(NTX_HEADER))
	     != sizeof(NTX_HEADER) ) {
	    printf( "Error reading header of %s\n", argv[1] );
	    exit(1);
	}

	/* Prints header information */
	printf( "***** NTX header *****\n" );
	printf( "Signature %xh\n", ntx_header.type );
	printf( "Version %xh\n", ntx_header.version );
	printf( "Root %lxh\n", ntx_header.root );
	printf( "Next page %xh\n", ntx_header.next_page );
	printf( "Item size %xh\n", ntx_header.item_size );
	printf( "Key size %xh\n", ntx_header.key_size );
	printf( "Key decimals %xh\n", ntx_header.key_dec );
	printf( "Max item %xh\n", ntx_header.half_page );
	printf( "Key expr: '%s'\n", ntx_header.key_expr );
	printf( "Unique: %d\n", ntx_header.unique );

	view_page(ntx_header.root);

	close(ntx_handle);
    }


  void view_page(long page_offset)
  {
	   char       * page;
	   NTX_BUFFER * buffer;
	   NTX_ITEM   * item;
	   unsigned char   i;

	/* Allocates page */
	page = malloc(BUF_SIZE);
	buffer = (NTX_BUFFER *) page;

  printf( "view_page\n" );

	if( page == NULL ) {
	    printf( "Error allocating memory.\n" );
	    exit(1);
	}

	/* Moves to the given page offset within the file */
	lseek(ntx_handle, page_offset, 0);

	/* Reads page */
	if( read(ntx_handle, buffer, BUF_SIZE) != BUF_SIZE ) {
	    printf( "Error reading page %lxh\n", page_offset );
	    exit(1);
	}

  printf( "items: %i\n", buffer->item_count );

	/* Traverses the vector, item by item */
	for( i = 0; i < buffer->item_count; i ++ ) 
	{
	   item = (NTX_ITEM *) (page + buffer->item_offset[i]);
	   if( item->page != 0 )
		    view_page(item->page);
	   printf( "%7i recno: %3i Key: %s\n", i, item->rec_no, &item->key);
	}

	/* Handles last pointer */
	item = (NTX_ITEM *) (page + buffer->item_offset[buffer->item_count]);
	if( item->page )
	    view_page(item->page);

	/* Deallocates page */
	free(page);
    }

Posted: Thu Sep 11, 2008 12:35 pm
by Antonio Linares
En esta versión he eliminado el uso de malloc() y free() para conseguir más velocidad aún :-)

www.fivetechsoft.com/files/utilities/ntxview2.zip

Code: Select all

#define MAX_KEY  256
#define BUF_SIZE 1024

typedef struct {
	unsigned short int     type;
	unsigned short int     version;
	long     root;
	long     next_page;
	unsigned short int     item_size;
	unsigned short int     key_size;
	unsigned short int     key_dec;
	unsigned short int     max_item;
	unsigned short int     half_page;
	char     key_expr[MAX_KEY];
	char     unique;
} NTX_HEADER;

typedef struct
{
	long page;
	long rec_no;
	char key[1];
} NTX_ITEM;

typedef struct
{
	unsigned short item_count;
	unsigned short item_offset[1];
} NTX_BUFFER;

#include "stdio.h"
#include "stdlib.h"
#include "fcntl.h"
#include "io.h"

NTX_HEADER ntx_header;
int        ntx_handle;

void view_page(long page_offset);

void main(int argc, char ** argv)
{
	 if( argc != 2 ) 
	 {
	    printf( "Use: ntxhead arquivo.ntx\n" );
	    exit(1);
	 }

	 ntx_handle = open( argv[1], 0x8000 );
	 
	 if( ntx_handle == -1 ) 
	 {
	    printf( "Open error: %s\n", argv[1] );
	    exit(1);
	 }

	 /* Reads header */
	 if( read(ntx_handle, &ntx_header, sizeof(NTX_HEADER) ) != sizeof(NTX_HEADER) ) 
	 {
	    printf( "Error reading header of %s\n", argv[1] );
	    exit(1);
	 }

	 /* Prints header information */
	 printf( "***** NTX header *****\n" );
	 printf( "Signature %xh\n", ntx_header.type );
	 printf( "Version %xh\n", ntx_header.version );
	 printf( "Root %lxh\n", ntx_header.root );
	 printf( "Next page %xh\n", ntx_header.next_page );
	 printf( "Item size %xh\n", ntx_header.item_size );
	 printf( "Key size %xh\n", ntx_header.key_size );
	 printf( "Key decimals %xh\n", ntx_header.key_dec );
	 printf( "Max item %xh\n", ntx_header.half_page );
	 printf( "Key expr: '%s'\n", ntx_header.key_expr );
	 printf( "Unique: %d\n", ntx_header.unique );

	 view_page( ntx_header.root );

	 close(ntx_handle);
}


void view_page( long page_offset )
{
	 NTX_ITEM * item;
	 unsigned char i;
	 char page[ BUF_SIZE ];
	 NTX_BUFFER * buffer = (NTX_BUFFER *) page;

   printf( "view_page\n" );

	 /* Moves to the given page offset within the file */
	 lseek(ntx_handle, page_offset, 0);

	 /* Reads page */
	 if( read( ntx_handle, buffer, BUF_SIZE ) != BUF_SIZE ) 
	 {
	    printf( "Error reading page %lxh\n", page_offset );
	    exit(1);
	 }

   printf( "items: %i\n", buffer->item_count );

	 /* Traverses the vector, item by item */
	 for( i = 0; i < buffer->item_count; i ++ ) 
	 {
	    item = (NTX_ITEM *) (page + buffer->item_offset[i]);
	    if( item->page != 0 )
		     view_page(item->page);
	    printf( "%7i recno: %3i Key: %s\n", i, item->rec_no, &item->key);
	 }

	 /* Handles last pointer */
	 item = (NTX_ITEM *) (page + buffer->item_offset[buffer->item_count]);
	 if( item->page )
	    view_page(item->page);
}

Posted: Thu Sep 11, 2008 12:36 pm
by Ruben D. Fernandez
Antonio:

El info.txt me dice error:Open customer.ntx

Gracias

Ruben Fernandez.

Posted: Thu Sep 11, 2008 12:41 pm
by Antonio Linares
Ruben,

Comprueba que tengas un fichero customer.ntx y que no esté abierto por una aplicación. Aqui funciona de lujo :-)

Y la versión sin malloc() y free(), es que vuela! :-D

Posted: Thu Sep 11, 2008 1:32 pm
by Rochinha
Antonio,

Esta es la minha prueba de 2004.

Code: Select all

// Sample showing how to use SUBNTX with NTX indexes

#include "FiveWin.ch"

static oWnd, cTempo1, cTempo2, cName := "COMERC"

//----------------------------------------------------------------------------//

function Main()

   DEFINE WINDOW oWnd FROM 1, 1 TO 20, 60 ;
      TITLE "Sistema de Faturamento" 

   SET MESSAGE OF oWnd TO OemToAnsi( "5volution.com - (r) Jose Carlos da Rocha, 2004 - Brasil" )
   ACTIVATE WINDOW oWnd ;
      ON INIT ShowDlg()

return nil

//----------------------------------------------------------------------------//

function ShowDlg()
      IF ! FILE("CLIENTES.DBF")
         msgrun("CLIENTES.DBF nao encontrado!")
         QUIT
      ENDIF
      USE CLIENTES
      IF lastrec() < 75
         EXPAND_DBF()
      ENDIF
      IF ! FILE("SUBDEMO.NTX")
         //msgrun("WAIT. Creating file SUBDEMO.NTX...")
         MsgMeter( { |oMeter,oText,oDlg,lEnd| ;
                           MakeIndex( oMeter, oText, oDlg, @lEnd, "CLIENTES->NOME+CLIENTES->ESTADO", "SUBDEMO.NTX", "" ) },;
                           "Creating index SUBDEMO.NTX..." )
      ENDIF
          
      msgwait("Primeiro. Mostro todo database...")
      browse()                                          

      msgwait("Agora. Usando um simples SEEK...")
      set index to SUBDEMO
      go top
      seek "5VOLUTION"
      browse()                                          

      msgwait("Agora. Usando um simples SET FILTER...")
      go top
      seek "5vol"
      msgwait("Observacao. Quando mover o cursor alem do browse o cursor se perde por muito tempo...")
      msgwait("((provavelmente mais de um minuto)")
      cTempo1 := time()
      set filter to CLIENTES->NOME = "5VOLUTION"
      go top
      cTempo2 := time()
      MsgAlert("Tempo de Operacao: "+tempo( cTempo1,cTempo2 )+" segundo(s)" )
      browse()                                                                        
      set filter to

      msgwait("E AGORA. Usando SUBNTX...")

      nString := 1
      do while .t. //cName <> "exit"
         if MsgGet( "Digite uma frase:",;          // Title
                 "Nome:",;                     // Label
                 @cName )                           // A variable by reference
            set index to
            cTempo1 := time()
            //
            num = subntx( "SUBDEMO.NTX", "_SUB.NTX", cName )
            //
            cTempo2 := time()
            if ! FILE("_SUB.NTX")
               msgwait("Nenhum filtro foi gerador...")
            else
               set index to _sub
               //MsgAlert("Tempo de Operacao: "+tempo( cTempo1,cTempo2 )+" segundo(s)" )
               browse()
            endif
            nString := nString + 1
            do case
               case nString = 2
                    cName := "SA"
               case nString = 3
                    cName := "JOS"
               case nString = 4
                    cName := "W"
               case nString = 5
                    cName := "BE"
               case nString = 6
                    cName := "PANI"
               otherwise
                    msgwait("Eu gosto disto. Obrigado...")
                    quit // exit
            endcase
            set index to
            erase _sub.ntx
         else
            exit
         endif
      enddo
      close databases
   return nil

//----------------------------------------------------------------------------//
function EXPAND_DBF
   msgrun("STAND BY while expanding CLIENTES.DBF...")
   ERASE SUBDEMO.NTX
   COPY TO _SUBTMP
   FOR i = 1 to 49
       APPEND FROM _SUBTMP
   NEXT
   ERASE _SUBTMP.DBF
   REPLACE ALL REC WITH RECNO()
   return nil

//----------------------------------------------------------------------------//
function tempo( inicio, final )
   return str(val(strtran(substr(final,4,5),":",""))-val(strtran(substr(inicio,4,5),":","")),6)

//----------------------------------------------------------------------------//
function MakeIndex( oMeter, oText, oDlg, lEnd, cChave, cIndex, cFor )
   FIELD Last
   oMeter:nTotal = RecCount()
   IF EMPTY( cFor )
      INDEX ON &(cChave) TO &(cIndex) ;
            EVAL ( oMeter:Set( RecNo() ), SysRefresh(), ! lEnd )
   ELSE
      INDEX ON &(cChave) TO &(cIndex) FOR &(cFor) ;
            EVAL ( oMeter:Set( RecNo() ), SysRefresh(), ! lEnd )
   ENDIF
return nil
Download: http://www.5volution.com/forum/prueba.zip

Posted: Thu Sep 11, 2008 1:49 pm
by Biel EA6DD
Supongo que esto es parecido a WildSrch, buscaba una cadena dentro de un indice NTX, y te devolvia un array con el numero de registro que la contenian, era rapidisima, y esta implementada en PRG.
Yo la utilizaba en los tiempos del clipper, y la he probado con harbour. Actualmente no la uso, ya que solo funciona con NTX y los deje de utilizar.

El codigo fuente es dominio público, si lo buscais por google seguro que debe de aparecer.

Code: Select all

//-----------------------------------------------
FUNCTION WildSrch ( cNTXFile, cSrchArg, nMaxHits )
//-----------------------------------------------
/*
parameters    :  cNTXFile  : drive:\path\filename.ext
                             (the NTX-File, to be scanned)
                 cSrchArg  : character-string with the search-argument

                 nMaxHits  : If the result-array is as long as <nMaxHits>
                             the function returns.
                             If nMaxHits = NIL, nMaxHits is set to 4,096
                             (Array is full..)

returns       :  aResult   : an array with the record-numbers who
                             matched <cSrchArg>
*/


// Search Result
   LOCAL aResult   := { }               // array to hold the record-numbers
// For Low-Level-Services
   LOCAL nBlock    := 1024              // Size of each index-page
   LOCAL cBuffer   := Space( nBlock )     // Buffer for FREAD
   LOCAL nHandle   := - 1                // file-handle
   LOCAL nLen      := 0                 // length of index-file
   LOCAL nPages    := 0                 // index-pages (excl. header)
// Index-Header (offset from 0, substr from 1 -> always (+1) )
   LOCAL nPtrItemSize   := 13    // h0C -> Pointer to ItemSize (keysize+8)
   LOCAL nPtrKeySize    := 15    // h0E -> Pointer to KeySize
   LOCAL nPtrMaxItem    := 19    // h12 -> Pointer to max. items on each page

   LOCAL nLenItem       := 0      // length of each item
   LOCAL nLenKey        := 0      // length of each key
   LOCAL nItemMax       := 0      // max. items on each page

// IndexPage   (offset from 0, substr form 1 -> always (+1) )
   LOCAL nPtrItemPage   := 1     // h00 -> Pointer to Items on this page

   LOCAL nItemCnt       := 0     // items on this page

   LOCAL x := 0, y := 0              // FOR-loop indices
   LOCAL nPtrItem       := 0      // pointer to current item
   LOCAL nItemOffset    := 0      // offset of current item
   LOCAL cKey           := ""     // key-value of current item
   LOCAL nRecord        := 0      // recno of current item (inside the DBF-File)

// Check nMaxHits and Set Break
   LOCAL lBreak    := .F.        // Break-Condition

   nMaxHits := IF( nMaxHits == NIL, 4096, nMaxHits )

// Let's do..

// FileOpen

   nHandle := FOpen( cNTXFile, FO_READ + FO_SHARED )
   IF nHandle == - 1
      MsgAlert( 'Error ' + Str( FError(), 2 ) + ' no pudo abrise el fichero ' + cNTXFile )
      RETURN { }
   ENDIF
// Calculate how many pages excl. header-page

   nLen    := FSeek( nHandle, 0, FS_END )
   nPages  := Int( nLen / nBlock ) - 1
   FSeek( nHandle, 0 )
// Read Header and get some information

   FRead( nHandle, @cBuffer, nBlock )

   nLenItem := Bin2W( SubStr( cBuffer, nPtrItemSize, 2 ) )
   nLenKey  := Bin2W( SubStr( cBuffer, nPtrKeySize, 2 ) )
   nItemMax := Bin2W( SubStr( cBuffer, nPtrMaxItem, 2 ) )

   //Now Read all Index-Pages and check for Search-Value

   FOR x := 1 TO nPages

      FRead( nHandle, @cBuffer, nBlock )
      // check how many items on this page
      nItemCnt := Bin2W( SubStr( cBuffer, nPtrItemPage, 2 ) )
      // check all items on this page
      FOR y := 1 TO nItemCnt

         // Page-Offset to read Item-Offset
         nPtrItem := ( y * 2 ) + 1

         // Read Item-Offset
         nItemOffset := Bin2W( SubStr( cBuffer, nPtrItem, 2 ) ) + 1

         // Check current item
         cKey := SubStr( cBuffer, nItemOffset + 8, nLenKey )
         IF At( cSrchArg, cKey ) != 0   // SearchArgument found
            // Read recno from index-page (beginning at <nItemOffset+4>)
            nRecord  := Bin2L( SubStr( cBuffer, nItemOffset + 4, 4 ) )
            // Add recno to result-array
            AAdd( aResult, nRecord )

            // Check nMaxHits
            IF Len( aResult ) >= nMaxHits
               lBreak := .T.
               EXIT
            ENDIF

         ENDIF

      NEXT y

      IF lBreak
         EXIT
      ENDIF

   NEXT x

// close NTX-File
   FClose( nHandle )

   RETURN( aResult )   // EOF WildSrch
Estaria bien disponer de algo parecido para CDX

Posted: Thu Sep 11, 2008 2:08 pm
by Kleyber
Biel,

No seria la función OrdWildSeek() de xHarbour, para CDX?

Saludos,