martes, 4 de octubre de 2016

Copias de Seguridad y Restauraciones en SQL Server 2005 y posteriores

Este artículo viene a completar el anterior que escribí hace un tiempo sobre como restaurar una base de datos en una nueva ubicación. Hoy vamos a detallar como hacer, mediante T-SQL, copias de seguridad y, luego, como restaurar esos backups.

Este artículo está casi "copy-paste" de un blog pero no recuerdo ahora cual. Solo quería aclarar que no era de mi autoría.

Copias de Seguridad (Backups)

Backups Completos

BACKUP DATABASE Nombre_BBDD TO DISK = 'Unidad:\ruta\ficherobackup.bak' WITH INIT



Con el parámetro WITH INIT nos aseguraremos de que el fichero de backup contiene una única copia de seguridad ya que, por defecto, el comando BACKUP lo añade al fichero existente. De esta forma nos aseguramos que el fichero se sobreescribe.

Backups diferenciales

La restauración de los backups del registro de transacciones tiende a ser una operación lenta, especialmente si nuestro backup completo es semanal, o incluso superior en su programación en el tiempo. Los backups diferenciales intentan decrementar el tiempo de recuperación. La cláusula T-SQL es:

BACKUP DATABASE Nombre_BD TO DISK = 'Unidad:\ruta\ficherobackup.dif' WITH DIFFERENTIAL, INIT


El backup "ficherobackup.dif" contiene todos los cambios realizados desde el último backup completo. Podemos utilizarlo durante el proceso de restauración en combinación a los backups del registro de transacciones. En primer lugar, restaurando el backup completo, seguido de la restauración del último diferencial, y a continuación restaurando cualquier log de transacciones posterior.

Backups del Log

El comando para realizar el backup del fichero de log es:

BACKUP LOG Nombre_Log_BBDD TO DISK = 'Unidad:\ruta\ficherobackup.trn'


Cada acción contra la base de datos se asigna a un Log Sequence Number (LSN). Para restaurar a un punto específico en el tiempo, debemos tener un continuo registro de LSNs.

Backups divididos (striped)

Algunas bases de datos son demasiado grandes para crear un backup completo en una única cinta LTO o en un array de discos. En estos casos, podemos hacer uso de los backup striped, también denominados multiplexados. La ventaja es que casa dispositivo utiliza la totalidad de su capacidad para crear el backup. Su desventaja es que, en caso de fallo, todas las cintas o ficheros se necesitarán para completar una restauración.
Para crear un backup striped utilizaremos:

BACKUP DATABASE Nombre_BD TO DISK = 'unidad1:\ruta1\fichero1.bak' , 'unidad2:\ruta2\fichero2.bak', 'unidad3:\ruta3\fichero3.bak' WITH INIT, CHECKSUM, CONTINUE_ON_ERROR


El backup será "extendido" a través de todos los ficheros indicados.


Restauraciones

Para realizar la restauración deberemos tener en cuenta que la ubicación de la base de datos, al tratarse de otro servidor, puede ser diferente. Para ello haremos uso de la opción MOVE que nos servirá para indicar la ubicación tanto del fichero de datos como del registro de transacciones. Para conocer la ubicación en disco exacta pulsaremos con el botón secundario del ratón sobre la BBDD sobre la que vayamos a realizar la restauración, y seleccionamos "Propiedades". En las pestañas "Archivo de datos" y "Registro de transacciones" de la ventana que se abre, podremos conocer la ubicación de estos dos ficheros o (si la base de datos se
 encuentra dañada o en modo sospechoso) ubicando los ficheros lógicos y físicos a traves de la master, consultando en sys.databases y sys.master_files.

En primer lugar deberemos poner nuestra base de datos en modo monousuario o exclusivo, para lo cual nos conectaremos al servidor destino mediante el analizador de consultas de SQL Server y ejecutaremos la sentencia:

ALTER DATABASE BaseDeDatos SET Single_User;


Podemos "expulsar" a los usuarios inmediatamente o tras un determinado tiempo haciendo de la opción ROLLBACK, para lo cual añadiremos a la sentencia anterior WITH
ROLLBACK AFTER segundos o WITH ROLLBACK IMMEDIATE.

 Y una vez realizado este paso, podemos comenzar el proceso de restauración. En primer lugar hacemos uso del Full Backup:

RESTORE DATABASE BaseDeDatos

FROM DISK = 'D:\backup\bk_BaseDeDatos.bak' WITH

    MOVE 'BaseDeDatos' TO 'D:\MSSQL\Data\BaseDeDatos_Data.mdf'

  , MOVE 'BaseDeDatos_log' TO 'D:\MSSQL\Data\BaseDeDatos_log.ldf', NORECOVERY



Indicando la opción NORECOVERY. En el caso de que tuviésemos incrementales, deberíamos ir haciendo restauraciones sucesivas de cada uno de estos backups.
A continuación, restauramos el registro de transacciones:

RESTORE LOG BaseDeDatos FROM DISK = 'D:\backup\bk_BaseDeDatos_log.trn' WITH RECOVERY


O si queremos hacer una restauración hasta un punto concreto en el tiempo podemos hacer uso de la opción STOPAT:
RESTORE LOG BaseDeDatos FROM DISK = 'D:\backup\bk_BaseDeDatos_log.trn' WITH STOPAT = N'9/19/2016 11:01:45 PM', RECOVERY


Una vez finalizado el proceso de restauración, ponemos la BD en modo multiusuario:

ALTER DATABASE BaseDeDatos SET Multi_User;




martes, 19 de enero de 2016

SQL Server - Restaurar una base de datos en una ubicación nueva y con un nombre nuevo (Transact-SQL)

-- -- --
-- -- Restaurar una base de datos en una ubicación nueva y con un nombre
-- -- nuevo (Transact-SQL)
--
USE master
GO
-- Primero habría que determinar (si no se sabe) los nombres lógicos de los
-- archivos de la copia de seguridad que se desea restaurar.
RESTORE FILELISTONLY
   FROM DISK = 'D:\MSSQL\Backup\bkp_DB_Sistema'


-- Luego, con los nombres lógicos de los datos (.mdf) y de registro (.ldf)
-- se puede hacer una restauración a una nueva ubicación con la opción MOVE.
RESTORE DATABASE DB_Nueva
   FROM DISK = 'D:\MSSQL\Backup\bkp_DB_Sistema'
WITH REPLACE,
   MOVE 'DB_Sistema_Data' TO 'D:\MSSQL\Data\DB_Nueva_Data.mdf',
   MOVE 'DB_Sistema_Log' TO 'D:\MSSQL\Data\DB_Nueva_Log.ldf'
GO


-- Usar REPLACE, de ser necesario. Si es que se tiene que
-- sobreescribir por ser diferente los nombres lógicos.

SQL Server - Usos no deseados de la operación UNION

Hace pocos días me topé en el trabajo con código SQL embebido, escrito por otro programador tiempo atrás, en donde hacía un uso indebido de la operación UNION. En el ejemplo en cuestión, se quería realizar el promedio de los valores de una columna TOTAL y, si bien todos los valores eran positivos, al sumarlos había que tener en cuenta la columna TIPO para sumarlos o restarlos al resultado, puesto que dependían de esta columna, para saber si eran de tipo créditos o débitos. Es decir, éstos valores dependían de una condición que los separaban en negativos y positivos.

Cuando veo como estaba escrito el código SQL me doy cuenta que el resultado no iba a ser el esperado porque se estaba separando la condición con una operación UNION 
Escribo la consulta en cuestión:


SELECT CantidadCasos = COUNT(*)
      ,Promedio = SUM(Total) / COUNT(*)
FROM Pedidos
UNION ALL
SELECT CantidadCasos = COUNT(*)
      ,Promedio = SUM(Total * (-1)) / COUNT(*)
FROM FACT0003
GO




El conjunto de resultados sería el siguiente:


Como se ve, esto nos da un registro por cada consulta (SELECT) en el caso que se den ambas condiciones (en el caso que la unión sea válida para ambos casos) y por ende, el promedio tampoco va a ser el correcto.

La forma correcta sería tratarlo con una expresión del tipo condicional, CASE o IF, teniendo en cuenta como se hace uso de las consultas de agregado (SUM(), COUNT(), etc) que es en donde muchos se equivocan o creen que no se puede realizar este tipo de consultas. Si prestan atención, el condicional siempre está dentro del agregado (en este caso el CASE dentro del SUM():


SELECT CantidadCasos = COUNT(*)
      ,Importe = SUM(CASE Tipo WHEN 'N' THEN Total * (-1) ELSE Total END) / COUNT(*)
FROM Pedidos
GO



El conjunto de resultados, para la consulta anterior, sería el siguiente:



Ahora sí, el resultado es el esperado.

SQL Server - Operaciones con Fechas y Horas II

Hace unos años publiqué (En el 2010... muchos ya.. tengo que publicar más seguido me parece :D) un primer post sobre operaciones con fechas y horas en SQL Server. Hoy, en el trabajo, se presentó una situación, que podría presentarse alguna otra vez en el futuro, y es bueno documentarlo para no estar pensando como resolverlo y/o googleando al cuete de nuevo.

Teníamos que averiguar el período (osea el intervalo de tiempo), en días, entre dos fechas para después usar los días para sumarlo a una fecha. Solo los días, manteniendo la hora como estaba. Combinando DATEDIFF() y DATEADD() lo podemos hacer.

Si tenemos dos parámetros de tipo DATETIME, @FechaDesde y @FechaHasta. Para averiguar la diferencia en días entre los dos hacemos:


SELECT DATEDIFF(DAY, @Fecha, @FechaLimite)

-- Si esto nos da una diferencia de 40 días, supongamos,
-- podemos luego, ir recorriendo todos los registros de la tabla 
-- que necesitabamos con las distintas fechas e ir sumando 40 a la variable @Fecha

SELECT DATEADD(DAY, 40, @Fecha)


Lo bueno cuando trabajamos con la funciones es que, si no las formateamos con CAST() y CONVERT(), no perdemos la hora que tenga grabada nuestra fecha de inicio (si viene de un campo DATETIME con hora y todo). Al sumarle días, nos va a mantener la hora como estaba.

viernes, 2 de octubre de 2015

Encendiendo el PT-6020 luego de un reemplazo de batería

Para encender el colector de la línea Hasar PT-Series luego de un reemplazo de baterías o una carga full por agotamiento de la misma hay que apretar el botón de reset y el botón de power simultanamente.


martes, 15 de septiembre de 2015

Solución a problemas comunes en colectores win CE series

Las baterías principal y auxiliar se agotan.

  1. Revisar los archivos de la aplicación que se quiere ejecutar. (fbcolect por ejemplo)
  2. Enviar el acceso directo de la aplicación.-
  3. Instalar el .NET Compact Framework.-
  4. Cambiar la configuración regional a Spanish y revisar los formatos de Number, Date, etc.

Error "can't find pinvoke dll 'dbnetlib.dll'."

Si da el error al tratar de hacer una consulta SQL es porque le falta esa librería. Probar instalando uno de los dos .cab. que están en:

C:\Program Files\Microsoft SQL Server Compact Edition\v3.5\Devices\Client\wce500\armv4i\

Creo que es el 2do. pero probar con el otro (tienen las mismas librerías adentro).

Siempre copiar el archivo de instalación .cab en el directorio del dispositivo a instalar. Nunca ejecutar directamente desde acá porque al instalarse se borra el .cab. 



miércoles, 19 de noviembre de 2014

Error al abrir Clarion 8 luego de un mal cierre de sesión de windows

Ayer cuando se me quedó sin batería la UPS luego de un corte de luz, justo estaba cerrando el Clarion 8 y se me apagó la PC. Cuando volvió la energía eléctrica, mi Clarion no iniciaba. Daba una lista sábana de errores de inicilización en un .xml que no encontraba. Tras reinstalar Clarion, Framework .NET, y demás seguía dando el mismo error.

El problema, tras googlear, se debe a que, como todo .NET Framework, al iniciar y cerrar el IDE, lee y escribe unos archivos XML de configuración de pantalla y del estado del IDE al cerrar. Se ve que al justo apagarse la PC se dañaron esos archivos.

La solución? Hay que resetear las preferencias nuevamente.




























Con lo que hay que poner el usuario y contraseña de registro, y registrar los templates nuevamente.

Otra cosa que hay que hacer el borrar las soluciones y proyectos (*.sln y *.cwproj) de la carpeta de trabajo para que se puedan  crear de nuevo al abrir un .app nuevamente.

Saludos y hasta la próxima!!

viernes, 21 de marzo de 2014

SQL Server - Listar Nombres de Columnas de Tablas Temporales

De vez en cuando, tenemos que trabajar con tablas temporales, ya sea que estamos en medio de cursores, trabajando con los nuevos operadores PIVOT y UNPIVOT, etc. para insertar, recuperar o transformar datos.

Si tenemos que hacer nada más que consultas de los datos de la tabla temporal, y suponiendo que mi tabla temporal se llama "temptable", solo hay que hacer


SELECT * FROM [tempdb].[dbo].[temptable]



Ahora bien, hay veces que necesitamos listar los nombres de las columnas de nuestra tabla temporal. Si fuera una tabla de nuestra base de datos, llamada "MiTablita", eso lo podríamos saber haciendo:


SELECT COLUMN_NAME
FROM information_schema.columns
INNER JOIN information_schema.tables ON (information_schema.columns.TABLE_NAME = information_schema.tables.TABLE_NAME)
INNER JOIN sysobjects ON (information_schema.tables.TABLE_NAME = sysobjects.name)
WHERE sysobjects.xtype = 'u' AND information_schema.columns.TABLE_NAME = 'Tablita'



Pero si es una tabla temporal no la vamos a encontrar en "information_schema" sino en "tempdb.information_schema" así:


SELECT COLUMN_NAME
FROM tempdb.INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'temptable'



martes, 20 de agosto de 2013

Habilitar y Deshabilitar Constraints en SQL Server

-- Deshabilitar Constraints
ALTER TABLE [tabla]
NOCHECK CONSTRAINT [nombre_restriccion]
GO

-- Habilitar Constraints
ALTER TABLE [tabla]
CHECK CONSTRAINT [nombre_restriccion]
GO

domingo, 2 de junio de 2013

Trabajar con diferentes versiones de Python en Linux con Pythonbrew

Hay veces que necesitamos trabajar con distintas versiones de Python en nuestro equipo, para ello una de las alternativas mas fáciles de instalar y usar es pythonbrew.

En este blog el autor explica paso a paso como hacerlo.

miércoles, 11 de abril de 2012

SQL Server: Problema al reducir el archivo de LOG

Una vez me pasó que intenté reducir el LOG de una base de datos y la consulta arrojaba este mensaje:
"No se puede reducir el archivo de registro 2 porque no hay el espacio mínimo de registro necesario."
Después de tanto buscar, encontré la solución en el blog de Andrés Naranjo.
Hay que cambiar el modo de recuperación de la base de datos de completa a simple.
Manos a la obra entonces:
ALTER DATABASE NombreBD
SET RECOVERY SIMPLE;
GO

--  nombreArchivo_Log debe ser el nombre lógico de la base de datos,
--  verificar este parametro en las propiedades de la base de datos opción  archivos

DBCC SHRINKFILE (NombreBD_log, 1)
GO

ALTER DATABASE NombreBD
SET RECOVERY FULL
GO

martes, 12 de julio de 2011

Consultas Distribuidas I - Habilitar el MSDTC en XP para SQL Server

Siempre lo mismo, resuelvo alguna tarea complicadísima y no la documento como es debido, pasa el tiempo y se da el mismo escenario en otro cliente y.... CHAN!!! No me acuerdo como lo había resuelto!!!! (Vale aclarar, debido a éstas cuestiones fue que nació éste blog)
Pues bien, eso mismo me volvió a pasar hace unos días así que (como ya tengo mi ayuda memoria "on web") comparto, para los usuarios hispanoparlantes el paso a paso de una de las tareas más "rompecocos" con las que me toco lidiar:
Hacer consultas DML distribuidas entre servidores MS SQL 2000
Estas, como algunos sabrán, son los SELECT, INSERT, UPDATE O DELETE con JOIN en donde una (o más) de las tablas combinadas pertenecen a distintos servidores (uno el local y los otros remotos). Por ej. "Si quisieramos consultar todos los registros que no existen de una tabla x del servidor remoto en la misma tabla x pero del servidor local" escribiriamos los siguiente:
SELECT * 
FROM [SERVIDOR_REMOTO].[DB_REMOTA].[dbo].Tabla_X TablaRemota_X
WHERE NOT EXISTS (
   SELECT *
   FROM Tabla_X TablaLocal_X
   WHERE Tabla_X.Id = TablaRemota.Id)
GO
Este escenario se podría dar en un ambiente de replica de SQL Server (de hecho es una de las consultas que estaba haciendo hace un rato). Si quisieramos ejecutar esta consulta el motor nos daría el siguiente error:

Iniciamos en el "Administrador de SQL Server" el servicio de "Coordinador de Transacciones distribuidas" o (MSDTC) y, si queremos, tildamos para que se ejecute al iniciar con el sistema operativo.
Al hacer ésto nos puede aparecer el siguiente mensaje de error:
Error 1068: no se puede iniciar el servicio o grupo de dependencia.
Si luego de iniciado el MSDTC nos da la siguiente pantalla de error al ejecutar nuestra consulta a un servidor linkeado:
"[OLE/DB provider returned message: No se puede dar de alta la nueva transacción en el coordinador de transacciones especificado. ]
Traza de error de OLE DB [OLE/DB Provider 'SQLOLEDB' ITransactionJoin::JoinTransaction returned 0x8004d00a].
No se puede realizar la operación. El proveedor OLE DB 'SQLOLEDB' no pudo iniciar una transacción distribuida." y blablablabla
Hay que habilitar un par de cosillas en el MSDTC:
Vamos a inicio --> ejecutar y escribir DCOMCNFG y apretamos Enter.
Se abre el administrador de "Servicios de Componentes": Hacemos click en el ícono "Servicios de Componentes", luego en la carpetita "Equipos" y, en el ícono "Mi PC", hacemos click con el control derecho y nos dirigimos a "propiedades". Una vez dentro de la ventana de propiedades tildamos la pestaña MSDTC y apretamos el botón "Configuración de seguridad..." y tildamos "Acceso a DTC desde la red", "Permitir clientes remotos", "Permitir administracion remota", "permitir entrantes" y "permitir salientes" y "no se requiere autenticación"
Luego de aceptar todo, reiniciamos los servicios del SQL Server Y ahí debería ya funcionar nuestra consulta LDM (SELECT, INSERT, UPDATE o DELETE) distribuida.

viernes, 22 de abril de 2011

Oracle anuncia "Java Magazine", una revista online sobre Java gratuita

Este lunes Oracle anunció Java Magazine, una revista online sobre Java gratuita. La revista todavía no ha publicado ningún número, y el primero no aparecerá hasta finales de julio. A partir de entonces será una revista bimensual electrónica gratuita que, según Oracle, será "una esencial fuente de conocimiento sobre la tecnología Java".


La revista no estará disponible abiertamente online, sino que requerirá registro (gratuito) para poder acceder a los números. En el registro se os despedirá bastante información personal (más de la que al menos yo considero razonable). Aunque el primer número no estará disponible hasta julio, el registro ya se ha abierto.

Fuente: JavaHispano

miércoles, 16 de marzo de 2011

Cambiar de Plataforma de Destino en Visual Studio sobre .NET CF

Uffff!!! Tarde tanto en hacer algo esta mañana y era una pavada. Dios!! Mi memoria falla cada día más!!!

Volví a trabajar con el .NET Compact Framework. Abrí mi Visual Studio y elegí el proyecto con el cual quería trabajar. Hasta aquí todo bien.
Lo que necesitaba hacer era probar un emulador de un dispositivo movil que tenía instalado pero no era el que estaba predeterminado al abrir el proyecto y el el cual me mostraba en el entorno del VS al inicio.


Al querer cambiar de plataforma desplegando la ventanita solo me mostraba el dispositivo que se ve en la figura. No acá no era...

- "A ver... Era acá?" - Me dije, haciendo click en el icono resaltado de la figura a continuación...


- "Ah!! Creo que acá era!!" - Afirmé, incrédulo, al abrirse la ventana de opciones y mostrar los dispositivos instalados para la plataforma:


Al elegir Pocket PC 2003 y aceptar miré, para mi sorpresa que no me cambiaba al dispositivo en el menú principal. Entonces?!?!? Cómo era?!?!?

Después de hurguetear en el ayuda un rato, encontré que hay que ir al menú "Proyecto" --> "Cambiar plataforma de destino..." y ahí si, indicarle con cual plataforma queremos trabajar:



Nos preguntá si deseamos cerrar y volver a abrir el proyecto para que pueda cambiar de plataforma...


Le decimos que si y listo, problema resuelto.

miércoles, 1 de diciembre de 2010

SQL Server - Operaciones con Fechas y Horas

El tratamiento de fechas y horas en SQL Server es uno de los temas que más generan preguntas en los foros y grupos de noticias de programación.

Aquí muestro algunas consultas que usé alguna vez para hacer comparaciones entre fechas. Para más referencias podemos buscar DATEADD en el ayuda del SQL.

Cuando queremos:

-- Restar 30 minutos a la fecha y hora actual
SELECT DATEADD(MINUTE, -30, GETDATE()) AS '30 minutos antes'
GO

-- Sumar 1 hora a la fecha y hora actual
SELECT DATEADD(HOUR, 1, GETDATE()) AS '1 hora después'
GO

-- Restamos 1 día a la fecha actual
SELECT DATEADD(DAY, -1, GETDATE()) AS '1 día antes'
GO

-- Seleccionar solo la Fecha de un campo DATETIME (Fecha y Hora)
SELECT CONVERT(DATETIME, CONVERT(VARCHAR, @variable-fecha, 101), 101)
GO

viernes, 24 de septiembre de 2010

SQL Server - Reducir Log de Transacciones eficientemente (MSSQL 2000-2005)

/*
Ejecuta dos o tres veces la instrucción CHECKPOINT.
Esto asegurará que todas las páginas de memoria se han escrito en el fichero de datos.
*/
CHECKPOINT

/*
Luego haz un BACKUP LOG WITH TRUNCATE_ONLY para que trunque el registro de transacciones.
*/
BACKUP LOG
NombreBaseDatos WITH TRUNCATE_ONLY

/*
Posteriormente ejecutas DBCC SHRINKFILE indicando el nombre del fichero del log a reducir
*/
DBCC SHRINKFILE (
NombreBaseDatos_Log)

Vale aclarar que estas instrucciones funcionan para la versión 2000 y 2005 del SQL Server.
Listo!!!

lunes, 20 de septiembre de 2010

Tip Linux: Loguearse como root en Ubuntu

En contra de lo que muchos usuarios creen, en Ubuntu también es posible loguearse como root desde un terminal. Salvo que la forma de hacerlo es diferente a la tradicional ($ su).

El comando a escribir para loguearse como superusuario en un terminal bajo Ubuntu, es el siguiente:

$ sudo -s

Luego el interprete nos preguntará nuestra contraseña de usuario antes de mostrarnos el prompt correspondiente al superusuario.

Otras maneras de loguearse como root son:

$ sudo su

$ sudo bash

Ah!, y para salir del login como root, basta escribir "exit" en la terminal.

root@desktop:~# exit
exit
cristian@desktop:~$

lunes, 30 de agosto de 2010

SQL Server - "The conversion of a char data type to a datetime data type resulted in an out-of-range datetime value"

Cuando buscamos en el ayuda en línea de "Don Microsoft" encontramos esto:

"...This error occurs when trying to convert a string date value into a DATETIME data type but the date value contains an invalid date. The individual parts of the date value (day, month and year) are all numeric but together they don’t form a valid date."

Cuya traducción by "CristianTranslator" podría ser:

"...Este error se produce al intentar convertir un valor fecha en un tipo de datos DATETIME, pero el valor fecha contiene una fecha no válida. Las partes individuales del valor fecha (día, mes y año) son numéricos, pero juntos no forman una fecha válida."

Solución:

En el Servidor de Base de Datos --> Seguridad --> Inicios de sesion,

elegir con cual usuario nos estamos conectando (el que nos trae el problema) y cambiarle el idioma Ingles por Español.

lunes, 23 de agosto de 2010

SQL Server - Registros Duplicados

Muy util query de SQL Server para saber los registros duplicados por un cierto campo x en una tabla:

SELECT *
FROM Tabla
WHERE CampoX IN
(SELECT CampoX
FROM Tabla
GROUP BY CampoX
HAVING COUNT(*) > 1)

jueves, 12 de agosto de 2010

Tip Clarion Nro. 1 - TAB dentro del Embed

Cortito y al pié:

Cuando empecé como programador en Clarion descubrí, sin querer, que apretando TAB dentro del embed se abre la ventana "Block Indent" para poder indentar un grupo de sentencias que estén dentro de un mismo bloque.

Lo comparto porque los todos los "gurus" de Clarion que me acompañaban en la ofi, y de los cuales aprendí todo, no lo sabían y en los foros de programación del lenguaje tampoco figuraba ese atajo de teclado.

Era eso nomás, justo lo utilicé recién y me acordé.

Abrazo de gol y hasta la próxima...