10 diciembre 2006

Visual Basic .NET - Dejemos todo al azar...

¿Alguna vez has escuchado alguna frase relacionada a los "azares del destino"? No sé que tan cierto es eso que el destino sucede al azar (algunos están en desacuerdo con esta idea, como lo está Amado Nervo en su poema "Vida nada te debo"), pero los programadores no creen en que hay tanto azar en las cosas porque, aunque es posible (y muy útil) hacer archivos que se puedan accesar de manera aleatoria, se obtiene solamente lo que se ha grabado en ellas.

A lo mejor ya los hice bolas con los poemas (lo mencioné como simple cultura general) y los programadores. La idea es que puedo crear archivos por medio de los archivos de programación (aunque ahorita me interesan los que se pueden hacer en Microsoft Visual Basic .net) que pueden ser accesados de manera aleatoria. Esto del acceso aleatorio es una manera rimbombante de decir que podemos accesar cualquier parte del archivo que se nos pegue la gana en el momento en que se nos pegue la gana. Como habrás notado si leíste algunos de mis posts anteriores, este tipo de archivo difiere de los archivos secuenciales en que éstos últimos solamente pueden ser leídos desde el principio hasta el final, o sea, no es permitido saltar a cualquier parte del archivo y agarrarnos leyendo. No es que un tipo de archivo sea mejor que otro: cada uno tiene sus propios usos.

Como no todo es al azar, los programadores decidieron ponerle cierta estructura a las cosas para que no todo fuera tan alocado. Gracias a estas estructuras, podemos usar un archivo de acceso aleatorio como una tabla de una base de datos.

Para ilustrar como funciona esto, hice una pequeña aplicación que le podría servir a la biblioteca personal de alguien. Esta es la imágen del programa en funcionamiento:



La aplicacion usa las cajas de texto para llenarlos de valores y guardar el registro o para mostrar los datos de un libro leído. Todo esto funciona por medio de los botones, pero solo para farolear y aumentar mi ego y autoestima, decidí también tener un menú contextual que sirva para hacer lo mismo. Así es el diseño del menú:



Ahora, vámonos al código. Pero, como diría Jack el destripador, vámonos por partes. Para empezar, esta es la estructura que voy a usar para guardar los datos de los libros y algunas variables globales que me van a ser útiles. Todo esto va a dentro del Public Class Form1.



No voy a redundar con una explicación acera de cómo se hacen las estructuras porque ya escribí acerca de ellos aquí. De las variables, solo quiero aclarar lo que guardar. Librote es la instancia de la clase Libro que voy a usar, NombreArch es una cadena que tiene el nombre del archivo (toda la ruta completa) y NoReg guarda el número de registros que están almacenados en el archivo.

Todo esto se inicializa en el Form1_Load como se ve en esta figura. Lo primero que hace es asignarle el nombre Biblioteca.tvr a la cadena NombreArch (fíjate que al usar el Directory.GetCurrentDirectory, estoy diciendo que ese archivo va a estar en la misma carpeta donde se encuentra el archivo .EXE de esta aplicación). Luego consigo un número de archivo disponible (con el FreeFile()) y lo abro. Se te fijas, el uso de FileOpen() cambia un poco en relación a su uso con los archivos secuenciales. Como en el código no se ve toda la instrucción, más adelante (después de este código). Luego cuento el número de registros y guardo en dato en NoReg. Para hacer esto, comienzo a leer todos los registros usando la instrucción FileGet(), uno a uno, e ir acumulando en NoReg cuantas veces he leído hasta llegar al final del archivo (uso EOF() para saber si ya llegué al final). Y no hay que olvidar cerrar el archivo al final. Esto lo hago con FileClose().

En este mismo bloque de código, puse lo que tiene el Form1_MouseDown que se va a encargar de mostrar el menú contextual chiroliro que puse.



Ahora sí: la instrucción FileOpen() completo :

FileOpen(NoArch,NombreArch,OpenMode.Random,OpenAccess.ReadWrite,OpenShare.Default,Len(Librote))


Arriba expliqué lo que contiene NoArch y NombreArch. OpenMode.Random indica que vamos a usar un archivo de acceso aleatorio, OpenAccess.ReadWrite indica que vamos a poder leer datos del archivo y escribir datos a él. OpenShare.Default maneja toda la movida que pasa cuando varias personas quieren abrir el archivo al mismo tiempo. Se puede bloquear de escritura, lectura, etc. Con el Default le estoy dejando la bronca a VBasic .NET y le digo que ahi le encargo la bronca, que lo resuelva como mejor le parezca. Al final, uso la función Len() para que me devuelva el tamaño en bytes de la estructura, o sea, el tipo de dato de la variable Librote (o sea, una instancia de la estructura Libro). Esto es muy necesario para poder manejar las estructuras en el archivo. Si no le digo cuanto mide cada registro, es imposible que lo pueda hallar a la primera cuando le digo "quiero el registro 1355 y lo quiero rapidito". En el archivo, mide le número de bytes que se tiene que "saltar" para leer ese registro, o sea, el tamaño de Librote * 1355. ¿Entendido? ¡Adelante pues con lo que sigue!

Para cada acción que puede realizar el usuario tengo dos eventos donde lo puede hacer ya sea puchurrando (nuevo verbo, jeje) el botón o usando el menú contextual farol. Por esta razón, hice funciones que hicieran estas tareas vanas y rutinarias. Aquí está el ejemplo del código de los eventos que pone en blanco a todas las cajas de texto de la ventana y luego los eventos que guardan la información que está en la pantalla. Está tan fácil, que ni necesita explicadera.


El código que limpia la ventana (muy original, se llama LimpiaVentana) no tiene ninguna ciencia. Limpia la ventana (o sea, quita cualquier resto de texto que haya quedado por allí... pensándolo bien, nunca hay texto por allí... en fin, esa es la idea) e inicializa la estructura en ceros. Después hay una función AsignaLibro que copia los valores que hay en Librote a las cajas de texto de la ventana.



Ahora sí, algo más interesante. A continuación está la función GuardaLibro que se encarga de meterlo en su lugar quesque-aleatorio en el archivo. Hace lo mismo que hace Form1_Load al principio: encuentra un número de archivo que se puede usar y abre el archivo. Después le asigna a Librote los valores que hay en los Textboxes y al final le agrega uno al número total de registros para poder poner allí, en esa posición del archivo (en este caso, al final) el contenido de Librote. Una vez terminado, escribo el registro usando la función FilePut(), lo cierro y uso la función que limpia la ventana.



Sería inútil en este mundo guardar cosas si no los voy a usar. Bueno, eso pasa en nuestras casas cuando vamos acumulando tilichero, pero en las computadoras intentamos reducir el tilichero al máximo. Depsués de divagar en tiliches, intento decir que es muy importante consultar los datos que almacenamos porque si no los consultamos, mejor dedíquense a otra cosa y no se esfuercen en hacer programas. Pero como queremos hacer algo útil, ahi va la manera de accesar los datos que hemos guardado.

Hay 3 botones (y 3 elementos del menú farol) que hacen diferentes consultas. Dos de ellas son muy similares, el que busca por el título y el que busca por el ISBN (nada que ver con el INSEN). Los dos hacen prácticamente lo mismo: van desde el principio del archivo leyendo registros y comparándolos con el título o ISBN dado en un InputBox chafa que puse allí (mi idea es la de ejemplificar, no la de hecer una aplicación 100% terminada y fresón). El funcionamiento es también muy similar a lo que expliqué en el Form1_Load(), así que escribo aquí el código de la función BuscaISBN.


La otra manera de buscar sería la de accesar de forma directa el registro que quiero y leer los datos, o sea, la función quesque aleatoria del archivo. Para hacer esto, hay de dos sopas: usar la función FileGet() con el último parámetro que indica el número de registro que quier leer o usar la función Seek() para moverse al lugar en el archivo donde está ese registro y luego usar el FileGet() sin el parámetro del número del registro. Como es un mero ejemplo, lo hice de las dos maneras en la función BuscaReg, quien es la función encargada de buscar un libro por número de registro.


Parece que es todo. Espero que esto les sea útil. Bike.

06 diciembre 2006

SQL: SELECT parte 9 - Cambiando nombres

Si eres una persona como yo, es común que se te olvide los nombres de las personas que te rodean. O incluso he da por cambiarles el nombre a la gente. Parece ser que es un mal común a los que usan SQL porque incluyen una pequeña ayuda para que puedas cambiarle los nombres a los campos de una tabla cuando lo estás consultando. No hace ningún cambio al diseño de la tabla, solo cambia el nombre del campo en tu consulta.

¿Y cómo funciona esta maravilla del mundo moderno? Fácil, solo tienes que conocer el AS (nótese de antemano que solo tiene una S, no se confundan...). El AS se pone al final de un campo seguido por el nuevo nombre que se le dará a ese campo. Solamente lo cambiará el nombre del campo en la consulta por lo que no altera el diseño de las tablas.

Por ejemplo, supongamos que me da flojera mostrar un campo cuyo nombre es muy largo. Se vería así:

SELECT apellido_paterno_del_empleado AS Paterno FROM Clientes ORDER BY Sexo, Edad DESC, Salario

¡Y ya está! Simplemente simplifica la consulta por parte del usuario. No parece gran ayuda, pero considera el caso en que están mandando instrucciones SQL a tu manejador de bases de datos (o DBMS) por medio de algún lenguaje extraño de programación. Estar escribiendo ese nombresototote de campo a cada rato puede cansar y enfadar...

05 diciembre 2006

Visual Basic .NET: Estructurando ideas

Tengo un problema más o menos grave: tengo una pésima memoria (bueno, casi carezco de ella). Entonces, cuando salgo de viaje, pongo todas las cosas que puedo necesitar durante el en una sola mochila. De esa manera, son menos las cosas que tengo que andar buscando por todos lados y con saber donde está mi mochila, ya sé donde están todas mis cosas (el detalle también es que si se me olvida la mochila, ya bailé con Bertha las calmadas...). Podría tener mis cosas repartidas en diferentes mochilas, bolsas, etc., sin embargo me simplifico la vida si tengo todo junto.

A lo mejor este es un mal de los programadores porque en muchos lenguajes existe la manera de crear estructuras de datos, las cuales son entidades que son compuestas por varias variables, pero en un solo bloque. Esto facilita el manejo de datos en casos que se tiene información relacionada, por ejemplo, si quiero almacenar los datos de un amante, en lugar de tener que guardar sus datos en diferentes lugares (por ejemplo, arreglos de nombre, apellidos, domicilio, teléfono, IMeil, etc. o archivos diferentes donde un archivo guarda un dato o campo). Es por esto que se usan tablas en las bases de datos: cada registro tiene todos los datos que tienen que ver entre sí.

Espero que esta analogía resalte la importancia que tiene el uso de estructuras. La idea es la misma en todos los lenguajes que los soportan, lo único que cambia es la manera de definirlos. En VBasic .NET se definen de la siguiente manera:

Structure nombre
declaración de las variables
End Structure

Por ejemplo:

Structure Amante
Dim Nombre as String
Dim Apellidos as String
Dim IMeil as String
Dim Tel as String
Dim Edad As Integer
Dim Salario As Decimal
End Structure

¿Está bien esto? Casi. A diferencia de otros lenguajes (como el C o C++), VBasic tiene un tipo String que puede contener cadenas de cualquier longitud. Sin embargo, para nuestros fines particulares, tenemos que limitar el número de puede almacenar cada una. Para esto, escribimos algo como lo siguiente:


¡Ahora si! Hasta aquí todo bien pero, ¿cómo se usan? Más adelante (otro día) escribiré como usar las estructuras para crear archivos de acceso aleatorio, sin embargo, ahorita voy a escribir un poco de código que asigne datos a una variable de tipo Amante.

... (adentro de cualquier función) ...
Dim Persona As Amante
Persona.Nombre="Lisa"
Persona.Apellidos="Simpson"
With Persona
.Edad=9
.Salario=1245.33
.Tel="(33) 3435-3444".IMeil=lisa_simpson@hotmail.com
End With


Ahora si. Se usa el punto para separar el nombre de la variable de tipo Amante (Persona) de la variable a la que quiero hacer acceso. También puedo usar el With para ahorrarme la flojera de escribir tanto el nombre del objeto tantas veces.

En la próxima usaré las estructuras para hacer archivos de acceso aleatorio para guardar, buscar y leer datos, al estilo de una base de datos. ¡Hasta la próxima!

03 diciembre 2006

Dreamweaver: Marcos (vulgarmente llamados "Frames")

Los marcos son muy útiles a la hora de diseñar páginas güev (o sea, la versión mexicana de la "web", jeje) de todo tipo. Básicamente es una zona de la página que contiene otra página y su contenido es independiente de los otros elementos en la ventana del navegador. Podemos hacer páginas web que definan un conjunto de marcos y jugar entre ellos con ligas y muchas otras cosas divertidas.

Antes de empezar con todo el choro mareador que traigo, quiero que recuerdes que los marcos no son la solución definitiva para todo tipo de formatos chidos en páginas web. De hecho, pese a que antes eran muy usados, han comenzado a caer en desuso. Pero yo los explico aquí y ya es bronca tuya si los usas o no o como los uses o porque los usas o donde los usas o con quien los usas... en fin, ahi va.

Para este experimento, empecé haciendo una nueva página HTML. El archivo lo llamé "marcos.html". Lo voy a ir transformando en un archivo que defina 3 marcos: uno arriba uno estrecho a la izquierda y otro de todo el espacio que queda.

Para insertar un marco, pongo el cursor (o sea la rayita vertical que parpadea, no la del mouse) en la página o marco donde deseo añadir uno. Una vez situado, posicionado y listo para la acción, voy en los menús Insert>HTML>Frames (o Insertar>HTML>Marcos si estás usando la versión en español). Allí hay varias opciones de marcos a insertar. Yo comencé insertando un marco en la parte superior de la ventana (o sea la opción top). Al darle eso, me apareció la siguiente ventana que me pide que le de un nombre al marco. Es necesario, nuestro deber y salvación ponerle un nombre en todo lugar, bueno en todo marco (eso se escuchó medio hereje... sorry, solo era una bromita) porque si no tiene nombre, ¿dónde sé que se va a poner en cada marco? Imagínate que vas a una pachanga y conoces a 3 chicas (o chicos) super guapas(os). Si no sabes el nombre de cada uno, te vas a meter en broncas porque no vas a saber como llamarle a cada uno. Si dices "mi amor", sepa cuál va a voltear (si es que alguno voltea). Y como esto, mil otras broncas.

Ya divagué bastante. Regresando a la materia, despues de mi lapsus brutus, en la ventana que aparece (Frame Tag Accessibility Attributes) le tengo que decir cuál ventana (en mi caso, elegí topFrame, o sea el marco de arriba arribototota) y le puse el nombre MarcoSuperior (uy, que original...). Así se ve desde lejos, en la lontananza:


Una vez que le piqué al Ok, mi página y la barra de propiedades (que cambia según lo que tengo seleccionado) se ven así:


En mi página se ve una raya que divide verticalmente a la página en 2. Una vez hecho esto, voy a agregar un marco en el lado izquierdo, abajo del marco superior. Para esto, le doy click al marco inferior (para posicionar allí el cursor) y voy a Insert>HTML>Frames>Left. Ahora la ventana que me pide el nombre tiene 3 opciones: ponerle nombre al topFrame (que ya se llama MarcoSuperior), al leftFrame (el de la izquierda) y al mainFrame (lo que queda). Al leftFrame le puse como nombre "MarcoIzquierdo" y al mainFrame lo nombré "MarcoDerecho" (bueno, no soy muy original para nombrar marcos...). Ahora mi ventana se ve así:


Ahora, si son muy observadores, verán que en la pestaña que indica la ventana en que estoy trabajando (esquina superior izquierda de la ventana) dice marcos.html* (el * quiere decir que no lo he guardado desde la última modificación). Pero si pongo mi cursor en el marco superior o izquierdo, cambia de nombre. Se debe a que estoy creando un documento que tiene marcos y cada marco es independiente de los otros, tiene un archivo HTML diferente al de los demás. Por lo tanto, nuestro marcos.html ha quedado relegado al marco "MarcoDerecho".

Ahora solo resta ponerle contenido a los marcos (es decir, crear los archivos HTML de cada marco). Para esto, hago lo mismo en cada marco que haría en cualquier página (inserto imágenes, tablas, escribo texto, etc.). Sin embargo, al final cuando quiero guardar todo, sucede algo inesperado: de pronto quiere guardar un tal UntitledFrameset-2.html (que yo guardé como "defineframes.html"), un UntitledFrame-3.html (que yo le puse "izq.html") y UntitledFrame-4.html (que yo puse "arriba.html") y marcos.html. Conste que los nombres pueden variar un poco.

Ahora voy a "ligar" entre los marcos. Si se recuerdan (y si no se recuerdan, se las recuerdo, perdón, les recuerdo el concepto) cuando especifico un vínculo para un texto o imágen, hay una propiedad llamada Target que me permite poner donde quiero que se abra la liga. Ahora, trabajando en defineframes.html, aparece en el combobox Target las opciones mainFrame, leftFrame y topFrame, como podrás ver en la siguiente imagen. De esta manera puedes definir en que marco quieres que aparezca la página que quieres "ligar".



Creo que con eso tienen para seguir experimentando. ¡Hasta la próxima!

02 diciembre 2006

Generando archivos secuenciales con VBasic .NET y 2005

Pese a lo super modernos que nos hemos vuelto con el almacenamiento de datos, ya sea con ADO, ODBC, Oracle, Informix, SQL Server y hasta access, uno de los archivos más usados por los programas son los archivos de texto.

¿Archivos de texto? te preguntarás. ¿Qué no son ese tipo de archivos que usan los nacos que son tan tercermundistas que no pueden usar ni siquiera el Access que es casi gratuito? Eso creen algunos. Sin embargo, saber usar archivos secuenciales es escencial para poder hacer programas que verdaderamente valgan la pena.

¿Aún crees que son poco usados? Pues la mayor parte de los programas que requieren valores para configurarlos los guardan en archivos de texto (generalmente con extensiones INI o algo por el estilo).

Pero ahora le pregunta es, ¿cómo los programo? ¿Qué tengo que hacer? Precisamente en eso estaba pensando el otro día en una de mis meditaciones matutinas, así que se me ocurrió hacer un programita que generara y leyera archivos de texto secuenciales.

Aquí está la ventana en el modo de diseño. Consta de 5 Textboxes y tres opciones de un MenuStrip:



La opción de salir es muy clara. La idea es que cuando el usuario seleccione la opción Salir, el programa finalice. Por eso su código es tan simple (un simple End) que ni le doy más importancia.

Las otras opciones del menú principal son más interesantes. Del menú Generar muestra 2 maneras en que se pueden generar archivos de texto secuenciales: con o sin streams (los streams son objetios que nos permiten realizar funciones de entrada y salida. Estos son básicos con le manejo de archivos en C++ y como el VBasic .net se parece cada vez más al C++, 'pos ahi sabrás...). Lo que van a hacer estas funciones es grabar el texto de las 4 textboxes al archivo secuencial. LE puse texto por default, pero se puede cambiar por el usuario. De igual manera, el menú Leer consta con las mismas opciones, solo que en este caso son dos diferentes maneras de leer datos de un archivo de texto secuencial y arroja lo leído a la caja de texto grandota, el Textbox5. Así se ve el diseño de los menús:


Ahora sí, como dijo el dermatólogo, vámonos directo al grano: vamos viendo el código. Antes que nada, antes incluso del Public Class Form1, hay que escribir la instrucción Imports System.IO, el cual nos da acceso a las funciones de acceso a los archivos que vamos a usar más adelante.

Después de esto, la clase Form1 tiene 5 funciones: la NormalToolStripMenuItem_Click que se ejecuta cuando selecciona la opción de generar el archivo de texto de manera "normalita" (más adelanto explico el código de éste y las demás funciones), la ConStreamsToolStripMenuItem_Click que genera el archivo usando streams. Luego siguen las funciones que leen del archivo de texto sin y con streams (de hecho, se llaman casi igual que las funciones anteriores. Cuando esto sucede, VBasic le agrega un número al final como podrás observar).


Al hablar de la manera "normal" de escribir archivos, todo el acceso al archivo se hace mediante un número (un entero del 1 en adelante). Para esto resulta muy útil la función FreeFile() el cual nos devuelve el siguiente número disponible. Luego usé la función InputBox para pedir el nombre del archivo. Después de esto, la función FileOpen() me permite abrirlo. Los parámetros son sencillos: primero va el número que va a identificar al archivo, luego el nombre del archivo y después van los parámetros que indican el acceso. En este caso, como quiero escribir, le digo que el modo de apertura es de salida, o sea que vamos a escribir en él (OpenMode.Output) y que el modo de acceso, o sea cómo voy a acceder al archivo, va a ser de escritura (OpenAccess.Write) . Después uso la función WriteLine para escribir cada caja de texto al archivo. Sólo le tengo que dar el número del archivo y la cadena que quiero que escriba. Y al final de todo el rollo, ya que hice todo lo que quise con el archivo y no quiero usarlo más, le digo que cierre el archivo usando la función FileClose. Aquí está todo junto:



Esa es una manera de escribir, pero los chicos que están más en la onda de todo lo relacionado y orientado hacia los objetos prefieren usar streams. En este caso, se va a usar un objeto de tipo StreamWriter que al inicializarlo (con la instrucción New) abre el archivo para escritura (ese archivo se lo dí como parámetro). Después uso el método WriteLine del objeto StreamWriter para escribir todos los renglones. La única diferencia con el método "normal" es que no se necesita el número de archivo ya que este objeto "es" el archivo. Y al final, para cerrarlo todo, uso el método Close para terminar la escribidera.


¿Y qué método es mejor? ¿Con o sin streams? No hay mejores ni peores, solo maneras diferentes de hacer lo mismo. Yo te los comparto para que uses el que se te pegue la gana (suponiendo que se te pegó la gana programar, claro está...).

Ahora toca escribir. De la manera "normal" procedo casi igual que para escribir el archivo, solo que ahora uso los parámetros OpenMode.Input para decirle que el archivo va a dar entrada a datos y el OpenAccess.Read para leer de él. Además, en lugar de usar el WriteLine para leer, uso la función LineInput para leer una línea completa del texto y lo hago dentro de un ciclo (así me aseguro que va todo). Al final, igual que el metodo "normal" anterior, le digo FileClose para cerrar todo el changarro.


Así como leer de manera "normal" es casi igual que escribir de forma "normal", el uso de streams para leer es muy semejante al que se usó para escribir. Solo que ahora el objeto se llama StreamReader y estoy usando una función muy chida para leer todos los datos de un solo jalón: el tremendo ReadToEnd. Existe el LineInput, pero si teniendo algo "más mejor" como dirían en mi pueblo, lo usamos. Este es el código:


Con esto termino, con esto acabo. Presenté aquí dos maneras simples de escribir y leer datos a/desde un archivo secuencial de texto. Ahora faltaría hablar de archivos de acceso aleatorio, pero por lo pronto, ahi muere.

30 noviembre 2006

¡Concurso de Programación!

Les mando la convocatoria del concurso de programación 2007.

¡Anímense! Se va a poner chido. Para mayores informes e inscripciones, visita la página de la OMI.

SQL: SELECT, parte 8 - Eliminando a los repetidos

En el amplio mundo de las bases de datos, encontramos que lo que se repite es muy repetitivo. Bueno, en muchas ocasiones es inútil. Sin embargo, encontramos que en las consultas SELECT que hamos visto hasta ahorita es posilbe tener muchos registros con el mismo valor.

Por ejemplo, supongamos que un chavo tiene una base de datos para controlar sus citas amorosas. Este galán va a salir con una chica que acaba de conocer en el último concierto del grupo musical "Los asesinos del ritmo" y quiere apantallarla, por lo que no quiere ir a ninguno de los lugares donde ha ido con sus otras queridas (no vaya ser que lo cachen en la movida, jeje). En su tabla de Citas almacena, entre otras cosas, el lugar donde tuvo lugar cada cita. Por lo tanto, si hace un SELECT común y corriente para ver todos los lugares, obtendría algo así:

SELECT Lugar FROM Citas

¿Es útil esta información? Sí, pero puede mejorar. Lo puedo ordenar agregando un ORDER BY para obtener also así:

SELECT Lugar FROM Citas ORDER BY Lugar


Esto es un poco más fácil de comprender, pero de todos modos hay mucha repetición. Sería más útil y menos aburrido si pudiéramos quitar todos los duplicados de mi consulta. Para eso tenemos la poderosa cláusula DISTINCT que podemos ponerle a nuestras consultas para dejar los datos tal y como los quiero. Vamos escribiendo esto:

SELECT DISTINCT Lugar
FROM Citas
ORDER BY Lugar



¡Listo! Ahora si esto es tan útil para un galán, ¡imagínate para un usuario simple y mortal de un sistema que hagas! Recuerda que si dominas el SQL, podrás dominar el mundo. Bueno, tal vez exageré un poco.

28 noviembre 2006

SQL: SELECT, parte 7 - Ordenando el Caos

Ya casi agotamos las opciones que tiene la cláusula SELECT. Todo lo que puedo hacer con el WHERE me permite seleccionar solo los registros que me son útiles, pero falta todavía algo: ordenar la información obtenida. Como te podrás imaginar, esto te simplifica la vida un montón porque aunque obtengas solo los registros pertinentes, verlos a todos es un relajo, imprimirlos es un desmadre... en pocas palabras, si es posible, mejor ordénalos. No te cuesta nada y puede ayudar mucho.

Pero, ¿cómo los ordeno? Fácil. Usamos la cláusula ORDER BY de la instrucción SELECT. Para incluirlo, escribimos al final de la instrucción SELECT algo con la siguiente sintáxis (nota: lo que va entre corchetes cuadrados es opcional):

ORDER BY campo [ASC o DESC][,campo2 [ASC o DESC][,campo3 [ASC ó DESC][, ...]]]

Se ve medio raro, pero es fácil. En primer lugar, puedo ordenar por muchos campos. O sea, si ordeno una lista de alumnos por apellido paterno, es posible que tenga varios registros que tengan el mismo apellido. ¿Cómo los ordeno entre ellos? Puedo especificar un segundo campo, por ejemplo, el apellido materno. Y si ese también "empata" (por ejemplo hay dos o más hermanos en una escuela), podemos especificar que lo ordene por un tercer campo, como podría ser el nombre.

En este caso, podría escribir lo siguiente:

SELECT Nombre,ApellidoP,ApellidoM
FROM Alumnos
ORDER BY ApellidoP,ApellidoM,Nombre


¡Wow! Este aca un reporte de TODOS los alumnos (no tiene la cláusula WHERE, ¿recuerdas que dijimos que es opcional?) ordenados en primer lugar por el campo ApellidoP y, si hay varios con el mismo apellido paterno, ordena los "repetidos" por el campo ApellidoM. Y si hay algunos con el mismo apellido paterno y materno, los ordeno según el nombre.

Si no especifico nada, el ordenamiento se hace de manera ascendente (o sea de la A a la Z), pero puedo modificarlo si necesito que sea de forma descendente (de la Z a la A). Para esto sirve la parte DESC. Esto lo incluyo al final de cada campo. Por ejemplo, si quiero ordenar por apellido paterno de manera descendente y luego por el materno de forma ascendente, escribo esto:

SELECT Nombre,ApellidoP,ApellidoM
FROM Alumnos
ORDER BY ApellidoP DESC,ApellidoM


Como podrás imaginar, si le pongo ASC después del campoestoy especificando que va a ser de forma ascendente. Pero como es la opción por defáult, se puede omitir.

El uso del ORDER BY, como te habrás dado cuenta es muy fácil, así que ahora te dijo para que lo uses y juegues con él. ¡Se divierten!

27 noviembre 2006

SQL: SELECT, parte 6 - ¿Estás IN o solo te encuentras entre muchos? ¿O acaso no existes?

Otros operadores del WHERE que pueden ser muy útiles son el IN, el BETWEEN y los que tienen que ver con el NULL, los cuales sirven para ver si un valor dado se encuentra en un conjunto de opciones, entre dos valores o sin valor respectivamente. ¿Cómo funcionan? Vamos primero a hablar sobre el IN.

Creo que se entiende mejor con ejemplos, así que hay va uno. Quiero obtener el nombre de todas las compañías de mi tabla Proveedores que sean de Japón, Italia, Francia y Timbuktú. Esta consulta quedaría así:

SELECT Nombre,Pais
FROM Proveedores
WHERE Pais IN ('Japón','Italia','Francia','Timbuktú')


Bien, hasta aquí podemos buscar registros que tienen un campo que tiene valores que pertenecen a cierto conjunto, especificado después de la palabra IN en la cláusula WHERE.

Algo muy similar sucede con la palabra BETWEEN. Este sirve para obtener los registros que tienen un campo que se encuentra en un rango dado. Por ejemplo, si quiero ver todos los platillos de mi tabla Menu que cuestan entre $50 y $150, escribiría la siguiente instrucción SQL:

SELECT Platillo,Precio
FROM Menu
WHERE Precio BETWEEN 50 AND 150


¿Pero qué sucede si estoy buscando los registros que carecen de un valor? A lo mejor el usuario no capturó uno o varios campos y quiero saber cuáles son (no necesariamente tiene que ser un error, a lo mejor el programa está diseñado para dejar valores por defáult o lo usan para indicar otros procesos, etc.). Para esto me sirven las opciones IS NULL e IS NOT NULL. Nota: los únicos campos de una tabla que no pueden dejarse en blanco son los que componen la llave primaria.

Supongamos que ando actualizando mi base de datos de alumnos para completar sus datos personales. Particularmente quiero ver todos los alumnos que no tienen teléfono dado de alta. Para saber quienes son, escribo la siguiente instrucción:

SELECT Nombre,ApellidoP,ApellidoM
FROM Alumnos
WHERE Tel IS NULL


También puedo poner el NOT en la instrucción para obtener todo lo contrario. Es poco usado, pero es posible poner NOT NULL en lugar del puro NULL.

En fin, por ahi lo dejo hoy. Estoy a sus órdenes si no comprendieron algo. ¡Saludos!

26 noviembre 2006

Menus en Visual Basic .net y 2005

Hay dos tipos de menus que se pueden poner en aplicaciones hechos en Visual Basic .NET: el menú principal (MenuStrip) y el contextual (ContextMenuStrip). Como dijo Jack el destripador, vámonos por partes...
El menú principal es el que aparece en las aplicaciones abajo de la barra de la ventana (o sea, la tirita que dice "Archivo, Edición, etc."). Para crear uno, se tiene que arrastrar el objeto MenuStrip a la ventana. El ambiente de diseño cambia un poco: el "control" aparece en la parte inferior, fuera de la ventana y en la parte superior de la ventana (o forma) que estás creando, aparece una cajita que dice "Escriba aquí". La pantalla se ve algo así:


Al darle click sobre ese mensaje, te permite escribir un texto (el letrero del primer menú) y aparecen "huecos" para escribir más opciones.


Las siguientes imágenes muestran el efecto que tiene ir tecleando opciones del menú.


Visualmente quedó muy padre. Ahora, ¿qué hacemos? Programar las opciones se hace como con cualquier otro control. En este caso, le dí un doble click sobre la leyenda "Famous Star" y me aparece la ventana de código así:


Ya que solo es un ejemplo y no quiero complicarme la vida, solo voy a hacer que aparezca una ventana que diga lo que eligió, así que agrego el código que hace aparecer un MessageBox de la siguiente manera:


Al correr el programa y al seleccionar la hamburguesa "Famous Star", aparece esta ventana:


Fácil, ¿no? Ahora toca retar a un menu contextual a un duelo. Bueno, exageré. Pero de todos modos vamos a crear un menu contextual. Es muy similar al menú principal, con algunas pequeñas variantes que iré detallando. Para empezar, tenemos que añadir el control ContextMenuStrip a la ventana para que quede abajo de la ventana que estoy diseñando. Algo así:


Como observarás es casi idéntico que el menú principal, solo que aquí no puedes cambiar el texto ContextMenuStrip1. Ahora puedes escribir las diferentes opciones de la misma manera en que lo hice con el menú principal. A mí me quedó así:


No te vayas a sacar de onda: si le das click afuera de la ventana del menú contextual que aparece, desaparece. Para volverlo a abrir, haz un doble click sobre el ContextMenuStrip1 (suponiendo que no le has cambiado el nombre) que está abajo de la ventana.


Al hacerlo, vuelve a aparecer el menú para que puedas seguir trabajando en él.

Agregar código a las opciones del menú contextual es idéntico a la manera en que se agregan en el menú principal (cosa que ya expliqué más arriba). Pero para que funcione, tenemos que programar el momento en que queremos que aparezca ese menú. Lo más común es hacer aparecer un menú contextual al darle un click con el botón derecho del mouse. Así que hay que programar el evento MouseDown. Yo programé el evento MouseDown de una etiqueta (Label1) porque me interesa que abra cuando estoy sobre el label, pero si quieres que aparezca cuando le das click derecho sobre la ventana, pon este código bajo el evento MouseDown del Form.

Espero que se haya entendido porque lo estoy programando bajo Label1_MouseDown() y no bajo Form1_MouseDown() como probablemente harías en tu proyecto, así que ahora hay que decirle al programa que si el botón presionado es el derecho, que haga aparecer el menú contextual. Se supone que en otra parte ya programamos lo que va a hacer cada opción (de la misma manera en que lo hacíamos para las opciones del menú principal). Esto sería así:


¡Y ya! Usamos la función Show que sirve para mostrar el menú en las coordenadas dadas. En nuestro caso, le estamos pasando las coordenadas del lugar donde está el cursor de nuestro mouse (e.X y e.Y).

¡Hasta la próxima!

24 noviembre 2006

SQL: El tremendo SELECT parte 5 - Lo lógico de lo ilógico

Ahora voy a hablar del uso de operadores lógicos. Éstos son 3: el AND (devuelve los registros que cumplan con todos los criterios de selección), el OR (devuelve los registros que cumplan cualquiera de los criterios de selección) y el NOT (niega la expresión que viene después del operador.

Algo que resulta muy útil al crear condiciones del WHERE, y muy en especial cuando estamos trabajando con operadores lógicos (también conocidos como booleanos) es el uso de paréntesis. El uso de estos te permiten:
  • Agrupar expresiones
  • Cambiar el órden en que se evalúan las condiciones
  • Hacer que las expresiones sean más claras, más fáciles de leer y entender
Ejemplos:

SELECT IdProducto,NombreProducto,IdProveedor,PrecioUnitario
FROM Productos
WHERE (NombreProducto LIKE 'T*' OR IdProducto=46) AND (PrecioUnitario > 16.00)


Este busca todos los productos cuyos nombres comienzan con la letra T o tienen un identificador de 46 y que su precio sea mayor a $16.00.

El resultado sería este:

IdProducto, NombreProducto, IdProveedor, PrecioUnitario
14, Tofu, 6, 23.25
29, Tomate, 12, 123.79
62, Tarte au sucre, 29, 49.3


Otro ejemplo:

SELECT IdProducto,NombreProducto,IdProveedor,PrecioUnitario
FROM Productos
WHERE (NombreProducto LIKE 'T*') OR (IdProducto=46 AND PrecioUnitario > 16)


Este devuelve los registros de todos los productos cuyo nombre empieza con T o que su numero de identificación es 46 y su precio es mayor a $16.00.

El resultado sería este:

IdProducto, NombreProducto, IdProveedor, PrecioUnitario
54, Tourtiére, 25, 7.45
62, Tarte au sucre, 29, 49.3
23, Tunnbröd, 9, 9
19, Teatime Chocolate Biscuts, 8, 9.2
14, Tofu, 6, 23.25
29, Tomate, 12, 123.79

Compara las dos consultas anteriores. Fíjate que devuelven diferentes resultados y son procesados de diferente manera porque son agrupados de manera diferente.

¡Hasta la próxima!

23 noviembre 2006

SQL: El tremendo SELECT parte 4 - el WHERE se encadena

El trabajo con texto en una base de datos es todo un relajo. Si estás buscando un texto, pero no le pusiste acento, te equivocaste con una letra u otro error de dedo, obtienes una consulta de datos que no querías o diste de alta un registro con datos erróneos.

Por eso, cuando trabajamos con texto, usamos fragmentos de texto (llamadas subcadenas por lo pipirisnais informáticos) para la búsqueda. Usamos parte del texto, algo que estamos seguros que está bien.

Para poder manejar esto, exiten los operadores LIKE y NOT LIKE, las cuales me permiten buscar subcadenas dentro de un campo de tipo texto. Por ejemplo, si quiero buscar a todos los registros cuyo nombre comience con las letras "Hern" sin importar los caracteres que estén a la derecha de este texto, escribiría la siguiente instrucción SELECT:

SELECT Nombre,ApellidoP,ApellidoM,Edad
FROM Alumnos
WHERE Nombre LIKE 'Fer*'


El asterisco sirve para indicar cualquier conjunto de caracteres (incluyendo una subcadena vacía). En algunos "dialectos" de SQL (o sea, en diferentes manejadores de bases de datos o DBMS), se usa el signo de porciento (%) en lugar del asterisco (*).

Por ejemplo, la sentencia SQL anterior devolvería los registros cuyo campo Nombre fuera Fernando, Fernanda, Fercho, Fermín, etc.

Ahora, si quiero que tenga la subcadena "Fer" en cualquier parte del campo, podría escribir la siguiente instrucción:

SELECT Nombre
FROM Alumnos
WHERE Nombre LIKE '*Fer*'


Lo cual me devolvería los nombres de Fernanda, Fernando, María Fernanda, MaFer, Fercho, Fermín, Ana Fernanda, Juan Fermín, etc.
Puedo jugar con el asterisco de diferentes maneras y estoy seguro que a partir de los ejemplos que escribí, no tendrás problemas en aplicarlos a diferentes consultas.

Dreamweaver: Propiedades de las imágenes y mapas de imagen

Uno de los elementos más importantes de una página web, después del contenido de la misma, son lo elementos visuales, las imágenes. Por eso, en este artículo o post o como se te pegue la gana llamarlo, voy a hablar acerca de las propiedades de las imágenes que insertamos en una página Web.

Cuando seleccionamos una imágen que incluimos en nuestra página, la barra de propiedades es algo similar a este:



Como con cualquier otro objeto, desde este barra tengo acceso a las propiedades de la imágen. En este momento solo quiero resaltar dos propiedades que podemos manipular. El primero (o los primeros) son los íconos que permiten alinear la imágen en la página. Son los botones que se encuentran a un lado de la propiedad Border (que permite especificar el tamaño de un borde que vaya alrededor de la imágen). Este de aquí permite que la imágen esté a la izquierda, al centro o a la derecha de la página. La otra propiedad que quiero resaltar es el Align, la cual nos permite alinear el texto de tal manera que fluya alrededor de la imágen. No hay que confundirlo con los botones de alineación ya que estos sirven para posicionar la imagen en la página y las opciones de la propiedad Align sirve para alinear el texto con respecto a la imagen. En la siguiente imagen se ven todas las opciones para alinear el texto que va alrededor de la imagen.


La opción default (que es igual al baseline) hace que la imagen sea parte del texto de tal manera que el texto que le sigue a la imagen aparece a un lado de la esquina inferior derecha de ella. Te invito a que experimentes con las otras opciones para que veas la diferencia entre ellos.

Las demás propiedades que tienen las imágenes son muy sencillas, así que voy a mencionar algunos de ellos de manera rápida. Las propiedades W y H son del ancho y alto de la imagen respectivamente. VSpace y HSpace establece el espacio que debe existir alrededor de la imágen (o sea, el espacio entre la imágen y su alrededor como el texto). Src es el nombre de la imágen, link es una dirección de otra página (o sea, hace que la imágen sea un vínculo hacia otra) y target establece dónde va a aparecer la liga (ese lo vamos a explicar más adelante).

Para tener compasión de todos aquellos que tenemos conexiones lentas al internet, podemos crear imágenes con menor resolución para que le aparezcan al navegante antes de cargar la imagen chida. El nombre de esa imagen va en la propiedad Low Src. Puedo desplegar un texto en el lugar de la imagen para que se vea mientras se carga la imagen y esto se escribe bajo la propiedad Alt.

Para editar la imagen sin tener que salir de Dreamweaver puedes presionar algunos de botones que aparecen a un lado de Edit. Puedes abrir la imagen el Fireworks (otro programa de Macromedia, ahora Adobe, que sirve para optimizar imágenes para su uso en internet), comprimir la imagen (o sea, dejarlo del menor tamaño posible), cortar la imagen (definir una parte de la imagen que es la que se va a mostrar), girar la imagen, ajustar su brillo y su nitidez.

Ahora sí, ya entrados en gastos, vamos a ver la manera de hacer un mapa sobre una imagen. Pero antes de explicar el procedimiento, ¿qué $%#"&!¡ es un mapa de imagen? Un mapa de este tipo permite definir ciertas áreas de una imagen que funcionen como ligas a otras páginas. O sea que el navegangster, al pasar su mouse sobre diferentes partes de la imagen puede dar un click sobre diferentes zonas, definidas por el diseñador de la página, para "saltar" hacia otras páginas o zonas de la misma página, ya sea que estén en el sitio o sean externos a ella.

Ahora sí, ¿cómo defino esas zonas (llamadas HotSpots, o "puntos calientes")? En primer lugar debo darle un nombre al mapa, lo cual se hace en la propiedad Map.



Abajo de la propiedad Map tengo varias herramientas. Con ellas puedo definir las zonas "calientes" de la imagen. En el ejemplo que estoy presentando, la de una página con un mapamundi, comencé a definir una zona alrededor de Oceanía. Seleccioné la herramienta que tiene forma de círculo y dibujé un círculo sobre Oceanía. Aparece un círculo en color azul claro que delimita el área la cual no aparece al visualizar la página con algún navegador. La pantalla con el círculo oceánico se ve así:

Como podrás imaginar, la propiedad link tiene la dirección de la página que se va a abrir al darle click en esa zona caliente. Abajo de esta propiedad, existe la propiedad target con la cual podemos definir donde queremos que se abra la página. Por ejemplo, si selecciono la opción _blank la página ligada aparecerá en una ventana nueva.

Algunas zonas no pueden ser definidas con un círculo o cuadrado, por la que se puede dibujar un polígono alrededor de la zona caliente que quieres definir. Para crear uno, basta seleccionar la herramienta de polígono y dar click alrededor del área que se desea definir. Mientras vas dando clicks, se va sombreando el área "caliente". Dibujé un área irregular alrededor de Groenlandia para ilustrar esto.

Y para ilustrar el funcionamiento de la herramienta rectángulo, dibujé un cuadrado alrededor de Cuba. La página quedó así:



Creo que con esta breve explicación no tendrás problema para trabajar con imágenes. ¡Nos vemos!

22 noviembre 2006

SQL: El tremendo SELECT parte 3 - El WHERE continúa

Y con el SQL sigue la buena vida...

Ahora voy a hablar (bueno escribir) acerca de otras cláusulas que tiene el WHERE.

Los operadores más fáciles de usar son los de comparación. Creo que son bastante sencillos porque todo lenguaje de programación los tienen y son simbolitos que aprendimos desde la primaria. Para que se entienda como se aplican en una sentencia SELECT, voy a escribir algunos ejemplos.

SELECT ApellidoP, ApellidoM, Nombres
FROM Alumnos
WHERE ApellidoP='López'


Esta instrucción va a desplegar los campos del apellido paterno, materno y nombres de la tabla Alumnos, pero solo de los alumnos cuyo apellido paterno sea López.

No es necesario que el o los campos que intervienen en la parte WHERE tengan que ser parte de la consulta, o sea, no tienen que ir en la lista de campos después del SELECT. Por ejemplo, si escribo

SELECT ApellidoP,Ciudad
FROM Empleados
WHERE Pais='USA'


muestra el apellido paterno y la ciudad de todos los empleados cuyo país es USA. El campo Pais no viene en la lista de campos que se están consultando (no viene después del SELECT), pero sirve para restringir los registros de la consulta.

Ahora voy a escribir un ejemplo usando otros operadores. Como son tan simples, ya no voy a explicar cada uno de ellos.

SELECT ApellidoP,ApellidoM,Nombres
FROM Alumnos
WHERE Edad >= 19


SELECT NoMaterial,Descrip
FROM Productos
WHERE Precio<1250


SELECT Platillo,Precio
FROM Menu
WHERE Platillo <> 'Hamburguesa con queso'


¿Todo bien? Si tienes dudas, deja un comentario o mándame un emilio.

21 noviembre 2006

Peint - Programa mexicanizada del Paint que viene con Windows hecho solo para demostrar el uso de funciones gráficas usando VB .NET

Si quieres hacer un programa que sea una especie de "Paint" sencillo usando VB .NET, sigue leyendo porque voy a explicar como hacerlo.

Esta es la ventana del programa en ejecución. En la parte superior de la ventana puedes elegir la herramienta con la que se va a dibujar, el color a usar y el tamaño de la "brocha". Y hay dos botones: uno que borra la ventana y la otra para salir del programa.


En primer lugar, hay que dibujar la interfaz. Este es muy sencillo, ya que solo hay que dibujar una ventana (o Form) con el fondo de color blanco, un panel color gris en la parte superior, 3 combo box de estilo lista para el color, herramienta y tamaño, y dos botones. Así se ve el ambiente de desarrollo:


Ahora vamos viendo el código. Necesito declarar una pluma (System.Drawing.Pens) y una brocha (System.Drawing.Brushes) para poder dibujar las figuras. También declaro un entero para guardar el tamaño de la figura que se dibujará.

En el Form1_Load se llenan los comboboxes de las figuras, colores y tamaños posibles.


Tengo que validar el evento Form1_MouseDown. Tiene que pintar la figura si da un solo click sobre la forma. Como en el evento MouseDown hago toda la dibujadera, lo ejecuto desde aquí sin ninguna bronca.

La función Form1_MouseMove se ejecuta cada vez que se mueve le cursor del mouse. Aquí solo reviso que esté apretando el botón izquierdo del mouse y, si es que está presionado, dibuja la figura seleccionada. Por medio de un SELECT CASE decide la figura.

Otra cosa que tengo que programar es el cambio de color. Si el usuario selecciona un elemento del Combobox de colores, hago que tanto la pluma como la brocha sean de ese color. Todo esto lo hago dentro del evento Combobox2_SelectedIndexChanged.


Cambiar el tamaño es fácil. En Combobox3_SelectedIndexChanged asigno el valor numérico del combobox a la variable global del tamaño. Aquí también programo a los botones que borran la pantalla (Button1_Click), el cual solo pinta de un color la forma (ActiveForm.BackColor tiene ese valor) y el que sale del programa (Button2_Click).
Parece que así funciona muy bien. Solo toma nota que si se tiene que redibujar la ventana, lo que se ha dibujado se pierde. Esto se debe a que las ventanas (o formas) se están redibujando constantemente: cada vez que son minimizadas, maximizadas, que otra ventana se pone arriba de ellas, etc. Para esto, se tendría que interrumpir el evento PAINT (WM_PAINT en VC++) para volver a pintar todo, lo cual implica tener alguna manera de "recordar" lo pintado. Tal vez eso lo podrás hacer más adelante...

El Tony y sus ondas...

Related Posts Plugin for WordPress, Blogger...