Árvore de páginas

Versões comparadas

Chave

  • Esta linha foi adicionada.
  • Esta linha foi removida.
  • A formatação mudou.

...

Cuando una variable de una rutina transporte algún valor de campo, es importante recordar que su tamaño siempre debe respetar el tamaño del campo. Este caso es común cuando se recibe un contenido de campo de una variable de memoria o de algún GET elaborado específicamente para recibir un valor de campo y de arrays que almacenan un modelo de base de datos.
// VariableTransportFieldSample.prw
nTamPar := TamSX3("E1_PARCELA")[01]
cParcela := PadR(SuperGetMV("MV_1DUP"), nTamPar)

...

En funciones y método se indica el uso de la grabación y restauración de marcación de registros de todas las tablas utilizadas dentro de la función o método de utilidad.
// ProtectionAgainstDislodgmentSample.prw
aArea := GetArea() // Graba el puntero de registro del área activa
If SB1->(DbSeek(XFilial("SB1") + cVar))
// Encontré el registro, por lo tanto hago lo que es necesario
Else
// Genero el retorno negativo de la ejecución del procesamiento.
EndIf
RestArea(aArea) // Restaura el Área almacenada en la Variable aArea

...

Al ejecutar un DbSeek() verifique siempre si se localizó el registro, por ejemplo:
// DbSeekSample.prw
If SB1->(DbSeek(XFilial("SB1") + cVar))
// Encontré el registro, por lo tanto hago lo que es necesario.
Else
// Genero el retorno negativo de la ejecución del procesamiento.
EndIf
Aunque sea obvia la existencia del registro, haga la prueba para evitar cualquier interrupción indeseada del programa o que se corrompa la integridad de los datos.
La no verificación de la marcación, en gran parte de los casos, no genera error o ningún problema aparente. La principal consecuencia de la no verificación es el uso de información equivocada. Por ejemplo, suponiendo que la rutina esté calculando un contador cualquiera, si no se confirma que el DbSeek encontró el registro correcto, de todas formas se realizará el cálculo, sin embargo, el resultado del cálculo estará equivocado, pues se utilizó información equivocada. Errores de integridad de este tipo son muy difíciles de encontrar y generalmente sólo ocurren en un determinado escenario y ese escenario generalmente no está en la máquina del programador o analista que está verificando la situación.

...

La función DbSeek() tiene la opción "SoftSeek", es decir, determina si se utilizará una búsqueda relativa durante una búsqueda en una base de datos. Si no se encuentra ninguna correspondencia, el puntero de registro se quedará en el próximo registro del índice que tenga un valor más alto que la expresión utilizada en esta función. Esta opción debe utilizarse con la máxima atención, pues si está conectada, podrá localizar un registro equivocado. El mismo principio se aplica cuando una clave parcial se utilizada en el DbSeek sin el SoftSeek activado.

...

La función MsSeek(), cuando se utiliza bien, puede disminuir mucho el I/O de disco cuando se ejecuta.
Su principal característica es realizar el cache del resultado del seek, tanto con relación al recno encontrado como con relación al contenido del registro.
El uso incorrecto del MsSeek puede crear una situación de lectura sucia no deseada.
<enlace para el tópico de lectura sucia>

...

Las siguientes funciones tienen utilización restringida:
// FunctionsRestrictedSample.prw
DbGoTop()
DbSeek(XFilial("SA1"))
¡Importante!
La utilización de estas funciones, solo se justifica cuando se está utilizando algún filtro de lectura o un archivo temporal.
El DbSeek únicamente con la transferencia de XFilial() debe evitarse. Si la clave de búsqueda, incluye otros que no sean solamente la Sucursal, se libera el uso del DbSeek.

...

Las siguientes funciones son utilidades que sirven en los campos de validación e inicialización estándar del diccionario de datos. No se recomienda el uso de estas utilidades dentro de los códigos fuente, porque realizan tratamientos adicionales de protección e interpretación, que se utiliza dentro de un código fuente y puede sobrecargar innecesariamente el procesamiento.

...

También podemos buscar una información en determinado campo utilizando la función Marque.
Ejemplo:
// PosicioneSample.prw
cDesc := Posicione("SB1", 1, XFilial("SB1") + cCodigo, "B1_DESC")
De esta manera, se realizará una búsqueda en el SB1, en el orden 1, clave de la búsqueda XFilial("SB1") + cCodigo y se retornará el contenido del campo "B1_DESC". Observe que esta función no restaura la marcación original del archivo objetivo (en el caso SB1).
Es necesario transferir la sucursal del archivo a la clave transferida como parámetro, en caso de que exista en la clave del índice.

...

ExistCpo()

Retorna si determinada clave existe o no en el archivo.
Ejemplo:
// ExistCPOSample.prw
ExistCpo("SE1", M->EF_PREFIXO + M->EF_TITULO + M->EF_PARCELA, 1)
De esta manera, se realizará una búsqueda en el SE1, en el orden 1, clave: M->EF_PREFIXO+M->EF_TITULO+M->EF_PARCELA. Y retornará si la clave se encuentra o no (. (.T.) o (.F.)
No es necesario transferir la sucursal. Esta se incluirá automáticamente en la clave de búsqueda por la propia función.

...

Cuando trabajamos en un entorno multiusuario, es necesario un control de bloqueo de esta información para cuando esta se actualice o se borre. Este bloqueo serializa las operaciones, permitiendo que varios usuarios realicen las modificaciones en el mismo registro, pero no al mismo tiempo.

...

Tiene la función de crear un registro en blanco para incluir o bloquear el registro actual para edición, en este caso la función ejecuta un refresh del dato, buscando la información más actual en la base. Durante el período que el registro esté bloqueado los otros usuarios pueden accederlo apenas para consulta (vea más adelante – DeadLock - lectura sucia).
Si no es posible el bloqueo del registro, la función interactuará con el usuario, cuestionando si debe permanecer intentando el bloqueo o desistir de la operación.
Ejemplo:
Verifica si el registro existe en la tabla SA1:
// RecLockSample.prw
If !DbSeek(XFilial("SA1") + "000001")
// Si no existe, incluya un registro en blanco y lo bloquea
Reclock("SA1", .T.)
Else
// Bloquea el registro encontrado
Reclock("SA1", .F.)
EndIf

...

Libera el registro creado o bloqueado por la RecLock.
Ejemplo:
Verifica si el registro existe en la tabla SA1:
// MsUnlockSample.prw
If !DbSeek(XFilial("SA1") + "000001")
// Si no existe, incluye un registro en blanco y lo bloquea
Reclock("SA1", .T.)
Else
// Bloquea el registro encontrado
Reclock("SA1", .F.)
EndIf
SA1->A1_SALDO := nNovoSaldo
MSUnlock("SA1")
La recomendación es que se utilicen únicamente las funciones RecLock y MsUnlock.

...

No se recomienda el uso de las siguientes funciones para realizar trabamientos y destrabamientos:

  • DBRLock.
  • DBRUnlock.
  • MSRLock.
  • MSRUnlock.
  • DBUnlock.
  • DBUnlockAll.
  • MultLock(Alias, aChaves, nOrd).
  • SoftLock.
  • MSUnlockAll.
  • MSUnlockSoft.

...

Cuando se trabaja en un entorno multiusuario, es necesario un control de concurrencia de las actualizaciones realizadas en la base de datos. Control de concurrencia es un método utilizado para garantizar que las transacciones se ejecuten de forma segura y que cumplan las reglas de Atomicidad, Consistencia, Aislamiento y Durabilidad. Una transacción es una unidad que preserva la consistencia. Por lo tanto, es necesario, que cualquier ordenamiento producido al procesarse un conjunto de transacciones concurrentes, sea computacionalmente equivalente a un ordenamiento que ejecute estas transacciones serialmente en algún orden. Se dice que un sistema que garantiza esta propiedad asegura la seriabilidad. La serialización de las operaciones reduce el desempeño de la aplicación, principalmente cuando se realiza en tablas de alta concurrencia.
La línea Microsiga Protheus dispone de un estándar para las operaciones de concurrencia, que tiene como objetivo reducir las ocurrencias de la serialización de la aplicación. Estas operaciones pueden dividirse en:

  • Lectura.
  • Bloqueo de Interfaz.
  • Bloqueo de procesamiento.
  • Bloqueo de transacción.

...

En rutinas de procesamiento en entornos multiusuarios es común que dos usuarios soliciten el mismo procesamiento simultáneamente.
Ejemplo: El usuario A y B solicitan la liberación de todos los pedidos pendientes. En esta situación existen dos alternativas:

  • Bloquear la rutina para uso Monousuario.
  • Tratar la concurrencia.

El bloqueo de la rutina para uso Monousuario debe evitarse, pero si es necesario, debe realizarse por medio del uso de un semáforo. La función LockByName es la más recomendada para esta finalidad. Sin embargo, debe tomarse en cuenta el aislamiento necesario. Si la rutina es multiempresa, debe considerarse la empresa, si es multisucursal debe considerarse la sucursal en la formación de la clave de aislamiento. Ejemplo: Para que la rutina MATA330 esté aislada de todas las empresas y sucursales, debe utilizarse el Grupo de empresas en la formación de la clave de aislamiento LockByName("MATA330_"+cEmpAnt). Si solo es necesario el aislamiento de la sucursal, debe utilizarse la sucursal en la formación de la clave de aislamiento LockByName("MATA330_"+cFilAnt)
Tratar la concurrencia en rutinas de procesamiento es una tarea simple, que proporciona un enorme beneficio para el desempeño de la aplicación. Cuando dos usuarios ejecutan la misma rutina simultáneamente es mucho más probable que uno de los usuarios bloquee el registro principal y los otros se queden esperando la liberación del bloqueo. Después de la liberación del bloqueo, uno de los usuarios conseguirá realizar el bloqueo, pero no habrá procesamiento para realizar y el próximo registro ya estará bloqueado. Como se puede ver, no hay ningún beneficio para el sistema, además del perjuicio de consumo del hardware del Application Server.
Para que el sistema se beneficie con este tipo de situación es imprescindible el uso de la función SimpleLock. Esta función evalúa si el registro puede bloquearse y si no lo consigue, la rutina pasará al próximo registro y así sucesivamente, hasta la finalización del procesamiento. La gran ventaja de su uso es que, cuánto más llamadas haga el cliente a la misma rutina, más rápido será el procesamiento de la rutina.

Registros de la tabla

Momento

Llamada 1

Llamada 2

Llamada 3

10

1

Bloquea 10

Bloquea 11

Bloquea 12

11

2

Libera 10

Libera 11

Libera 12

12

3

Finaliza

Finaliza

Finaliza

...

Las transacciones tienden a serializar el sistema y dejarlo más lento en entornos multiusuario. Para mitigar la serialización son necesarios algunos cuidados.
El primer cuidado a tomarse, es evitar un DeadLock. Un sistema está en estado de DeadLock cuando existe una operación (A) haciendo un bloqueo en un registro (L1) e intentando bloquear otro registro (L2). En este mismo momento existe otra operación (B) bloqueando el registro (L2) e intentando bloquear el registro (L1). En esta situación no hay forma de que el banco resuelva las solicitudes, entonces elige aleatoriamente, una de las conexiones y la finaliza provocando un error para el usuario.
Image Removed
Existen dos maneras de resolver un DeadLock. La primera es garantizar que la transacción no bloquee más de un registro de la misma tabla. Esto puede realizarse reduciendo el tamaño de la transacción, para garantizar y mantener la integridad del sistema.
Ejemplo: Cuando se graba un formulario del tipo Master/Detail puede realizarse una transacción para el Master y el primer registro Detail y otra para los otros ítems.
// DeadlockPreventionSample.prw
For nX := 1 to Len(aCampos)
Begin Transaction
If nX == 1
RecLock("SC5", .T.)
EndIf
RecLock("SC6",.T.)
MaAvalSc6()
End Transaction Lockin "SC5"
Next nX
Begin Transaction
RecLock("SC5")
MaAvalSc5()
End Transaction
Observe que durante todo el proceso, la tabla SC5 está bloqueada y solamente se liberará en la última transacción. Esto lo garantiza el comando CLOSETRANSACTION LOCKIN, que actualiza la transacción, pero mantiene el bloqueo del registro para no generar problemas en otros procesamientos concurrentes, de acuerdo con lo informado anteriormente.
Otra manera de resolver, es utilizando la función MultLock. Esta función garantiza el bloqueo de todos los registros de una misma tabla. Su desventaja es el aumento de la serialización del producto. Por este motivo, debe utilizarse con mucho cuidado y tener presente que siempre hay una alternativa para evitar su uso.
// MultlockSample.prw
If MultLock("SB2",aMults,1)
Begin Transaction
// Hace alguna cosa
End Transaction
EndIf
La selección de uno de los métodos dependerá del tipo de transacción.
Las transacciones de formulario, obligatoriamente deben seguir el primer ejemplo. Las transacciones son más rápidas y el riesgo es la actualización parcial del formulario digitado, sin embargo, este quedara íntegro. Con toda seguridad el usuario final prefiere perder algunos datos a todo.
Sin embargo, existirán casos en que la actualización parcial no proporciona una transacción íntegra. Para estos casos, debe utilizarse "MultLock". Un buen ejemplo son las facturas, que en caso de interrupción, el usuario tiende a anular la factura y rehacerla si faltan ítems, es decir, el modelo anterior no proporciona beneficios al usuario.

...

XX_FILIAL

XX_CODIGO

XX_DESCRI

01

30

DESTORNILLADOR

01

22

DESTORNILLADOR DE ESTRELLA

02

21

TALADRO BOSCH

02

12

TALADRO BLACK & DECKER

01

31

TALADRO DREMEL

01

90

SIERRA DE VAIVÉN

02

48

PLOMADA

02

13

SERRUCHO

...

  • Grupo de empresas: Nivel superior que controla el diccionario de datos en el Microsiga Protheus. Ejemplo: SX1??0.DBF – el código de grupo sustituirá los puntos de interrogación
  • Empresas: Nivel que identifica las empresas que forman parte del grupo, es decir, ahora es posible utilizar más de una empresa para el mismo diccionario de datos.
  • Unidades de negocio: Nivel que identifica las unidades de negocios de las empresas. De esta manera, es posible definir un nivel de control entre las empresas y sucursales.
  • Sucursal: Nivel que identifica las sucursales de las unidades de negocios o de las empresas.

...

  1. Utilizar las siguientes directrices para que el programa acepte las modificaciones de tamaño del campo Sucursal:

...

  1. FWSizeFilial() -> Retorna el tamaño utilizado para la Sucursal (Sustituye el tamaño fijo de 2)
  2. FWCodFil() -> Retorna el código de la Sucursal marcada (Sustituye la utilización del M0_CODFIL)
  1. Todos los nuevos campos referentes a la Sucursal deben incluirse en el grupo de campo "033" (Sucursal).
  1. Para la actualización de campos concatenados, en el cual, la sucursal forma parte del contenido del campo, debe crearse una función con la sigla del módulo + "UpdFConj()" que retornará un array con la siguiente estructura:

...

  1. Funciones auxiliares para el desarrollo y estandarización de los programas:
  • FWSizeFilial - Retorna el tamaño del campo Sucursal
  • FWCodFil - Retorna el código de la sucursal
  • FWModeAccess - Retorna el modo de uso compartido
  • FWGrpCompany - Retorna el grupo
  • FWAllGrpCompany - Retorna los grupos de empresas
  • FWCompany - Retorna la empresa
  • FWAllCompany - Retorna las empresas del grupo de empresas
  • FWUnitBusiness - Retorna la unidad de negocio
  • FWAllUnitBusiness - Retorna las unidades de negocios para el grupo y la empresa
  • FWFilial - Retorna la sucursal
  • FWAllFilial - Retorna las sucursales para el grupo de empresas
  • FWLoadSM0 - Carga la información de las sucursales
  • FWSM0Layout - Retorna el layout
  • FWXFilial - Retorna el string
  • FWGrpName - Retorna el nombre del grupo de empresas
  • FWFilialName - Retorna el nombre de la sucursal
  • FWFilRazSocial - Retorna la Razón social
  • FWArrFilAtu - Retorna toda la información referente a la empresa, unidad de negocio y sucursal
  • FWFilialStatus - Retorna el estatus de la sucursal marcada
  • FWCodEmp - Retorna el código de la empresa
  • FWPesqSM0 - Muestra las sucursales disponibles para el grupo de la empresa actual
  • FWUnitName - Retorna el nombre de la Unidad de negocios
  • FWCompanyName - Retorna el nombre de la empresa
  • FWFilExist - Verifica si la sucursal existe
  • FWEmpName - Nombre de la empresa
  • FWFilName - Nombre de la sucursal
  1. Los programas que utilizan la barrida en la tabla SM0 (While), deben utilizar la función FWLoadSM0() que retorna un array con todas la información del SIGAMAT.

...

  1. Para retornar la información de una sucursal específica, utilice la función FWArrFilAtu().

...

Para que el registro realmente quede disponible para sus respectivas Sucursales, TODAS las rutinas que manejan registros directamente en la base de datos deben verificar la Sucursal por medio de la Función XFilial().
// XFilialSample.prw
SA1->(DbSeek(XFilial("SA1") + cCodigo + cLoja)
En el ejemplo anterior, se muestra que utilizando la función XFilial(), podemos encontrar un registro previendo todo el uso compartido de archivo.
Importante:

  • El campo XX_FILIAL forma parte de la clave de todos los índices del sistema.
  • Jamás utilice una campo sucursal de una tabla para ejecutar un DbSeek() en otra tabla. Pues una tabla podrá compartirse (campo sucursal en blanco), mientras que la otra podrá compartirse (campo sucursal completado).

...

Archivos e índices temporales deben utilizarse con cuidado, pues pueden generar un tiempo de respuesta largo mientras están construyéndose.
En el caso de índices temporales, el procesamiento de la rutina es mucho más rápido (después de su generación), pero de cualquier forma, siempre de preferencia a la utilización de los índices estándar del sistema o de querys.
Utiliza las funciones:
CriaTrab
Criatrab(cAlias,.F.) -> Crea solamente un archivo de índice temporal
Criatrab(cAlias,.T.) - > Crea un archivo de datos y un archivo de índice temporal (la creación del índice temporal no es obligatoria).
IndRegua
Crea efectivamente el índice, a partir del archivo creado con la CriaTrab.
Ejemplo:
Para crear dos índices temporales:
// IndReguaSample.prw
DbSelectArea("SE1")
cIndex := CriaTrab(Nil, .F.)
cIndex2 := CriaTrab(Nil, .F.)
cChave := IndexKey()
IndRegua("SE1", cIndex, "E1_FATURA+E1_NUM+E1_SERIE", , , "Seleccionando Registros...")
IndRegua("SE1", cIndex2, "E1_NUM", , , "Seleccionando Registros...")
nIndex := RetIndex("SE1")
DbSelectArea("SE1")

...

Cuando se crea un archivo de trabajo o un índice de trabajo (utilizando la función Indregua) es obligatorio que se borren al final del programa.
Ejemplo:
// CreateAndDeleteTemporaryDatabaseFileSample.prw
cArqTmp := CriaTrab( Nil, .T. ) //Creación de archivo
// Haz algún procesamiento
DbCloseArea()
FErase(cArqTmp + GetDbExtension()) // Borrando el archivo
FErase(cArqTmp + OrdBagExt()) // Deletando índice
Importante:
Utilice la función GetDbExtension() para retornar la extensión del archivo de trabajo. No utilice ".dbf", ".dbt", etc, como se muestra a continuación:
// GetDbExtensionSample.prw
FErase(cArqTmp + ".dbf") // Incorrecto.
FErase(cArqTmp + GetDbExtension()) // Correcto.

...

La utilización de filtros podrá realizarse de dos maneras:
<Ejercicio>Set Filter to
Ejemplo:
// SetFilterSample.prw
DbSelectArea("CV3")
cFilCV3 := XFilial("CV3")
Set Filter To CV3->CV3_FILIAL == cFilCV3 .And.;
CV3->CV3_DTSEQ == dDtCV3 .And.;
CV3->CV3_SEQUEN == cSequenc
// Haz algún procesamiento
Set Filter To
Es importante saber, que es incorrecto utilizar la función dentro de la Sintaxis del "Set Filter", y se recomienda que si es necesario, se utilice la forma del ejemplo:
cFilCV3 := XFilial("CV3")
Set Filter To CV3->CV3_FILIAL == cFilCV3 …
Al final de la utilización del filtro, este debe deshabilitarse, utilizándose una de las siguientes funciones / comandos:

  • DbSetFilter();
  • <Ejercicio>Set Filter to
  • DbClearFilter().

...

El estándar SQL del Microsiga Protheus se formó con base en la Revisión ANSI de 1992 y algunos comandos específicos de la base de datos, como por ejemplo DB2 y Oracle.
Cláusulas
Las cláusulas son condiciones de modificación utilizadas para definir los datos que se desea seleccionar o modificar en una consulta.

  • § SELECT – Se utiliza al inicio de una línea de comando para seleccionar datos, pudiendo utilizarse dentro de un otro "select" (conocido popularmente como "SubSelect") utilizado dentro de las cláusulas "From", "In" y "Not In"
  • § FROM - Se utiliza para especificar la tabla que seleccionará los registros.
  • § WHERE – Se utiliza para especificar las condiciones que deben reunir los registros que se seleccionarán.
  • § GROUP BY – Se utiliza para separar los registros seleccionados en grupos específicos.
  • § HAVING – Se utiliza para expresar la condición que debe satisfacer cada grupo.
  • § ORDER BY – Se utiliza para ordenar los registros seleccionados con un orden específico.
  • § DISTINCT – Se utiliza para seleccionar los datos sin repetición.

...

  • § INNER
  • § JOIN

...

  • § AND – Y lógico. Evalúa las condiciones y devuelve un valor verdadero si ambos están correctos.
  • § OR – O lógico. Evalúa las condiciones y devuelve un valor verdadero si alguno está correcto.
  • § NOT – Negación lógica. Devuelve el valor contrario de la expresión

...

Operador

Descripción

<

Menor

>

Mayor

<=

Menor o igual

>=

Mayor o igual

=

Igual

<>

Diferente

  • § BETWEEN – Se utiliza para especificar un intervalo de valores.
  • § LIKE – Se utiliza en la comparación de un modelo y para especificar los registros de una base de datos. "Like" + extensión % significa buscar todos los resultados con el mismo inicio de la extensión.
  • § IN - Se utiliza para verificar si el valor buscado está dentro de una lista. Ej.: valor IN (1,2,3,4).

...

  • § AVG – Se utiliza para calcular el promedio de los valores de un campo determinado.
  • § COUNT – Se utiliza para devolver el número de registros de la selección.
  • § SUM – Se utiliza para devolver la suma de todos los valores de un campo determinado.
  • § MAX – Se utiliza para devolver el valor más alto de un campo especificado.
  • § MIN – Se utiliza para devolver el valor más bajo de un campo especificado.

...

  • SUBSTRING
  • EXISTS

...

El objetivo del Embedded SQL es facilitar la escritura y lectura de query's. Se definió una sintaxis para que se pueda escribir la query directamente en el código ADVPL, sin necesidad de quedarse concatenando pedazos de string para componer el string final.
Se recomienda que nuevas querys se desarrollen valiéndose de este nuevo recurso.
// EmbeddedSQLQuery.prw
#INCLUDE "TOTVS.CH"
Function EmbeddedSQLQuery()
BeginSQL Alias cAliasTrb
SELECT R_E_C_N_O_ RECNOSN1
FROM %Table:SN1%
WHERE N1_FILIAL = %XFilial:SN1% AND
N1_CBASE >= %Exp:MV_PAR01% AND
N1_CBASE <= %Exp:MV_PAR02% AND
N1_ITEM >= %Exp:MV_PAR03% AND
N1_ITEM <= %Exp:MV_PAR04% AND
N1_GRUPO >= %Exp:MV_PAR05% AND
N1_GRUPO <= %Exp:MV_PAR06% AND
N1_AQUISIC >= %Exp:MV_PAR07% AND
N1_AQUISIC <= %Exp:MV_PAR08% AND
%Exp:cWhere% AND
%NotDel%
EndSQL
Return