¡Hola, encargados del scripting!El regreso del scripting de base de datos

Los encargados del scripting

Descargar el código de este artículo: HeyScriptingGuy2008_04.exe (151KB)

¿Sabe?, si los Chicos del scripting tienen una debilidad (venga ya, hemos dicho "si"), debe ser ésta: Nos preocupamos demasiado de las posibles consecuencias de las acciones que realizamos o dejamos de realizar. En vez de actuar en función de nuestros propios intereses, generalmente hacemos los que pensamos que satisface el interés general. Y lo hacemos a pesar de las consecuencias que estas acciones puedan tener para nosotros.

Por ejemplo, piense en los artículos acerca del scripting de base de datos. Con toda honestidad, la redacción de los artículos acerca del scripting de base de datos es muy difícil para los Chicos del scripting. Y no porque el scripting de base de datos sea difícil. Al contrario, es bastante fácil. El problema es que los Chicos del scripting no tienen muchas oportunidades de trabajar con bases de datos. Por eso, cada vez que escribimos sobre bases de datos tenemos que pararnos a pensar qué estamos haciendo. Seguramente ya se habrá dado cuenta de que pensar no es un requisito para llegar a ser un Chico del scripting.

De hecho, si cualquiera de nosotros hubiera pensado sobre el tema, probablemente no existirían los Chicos del scripting.

Entonces, si los artículos acerca del scripting de base de datos son tan difíciles para los Chicos del scripting, ¿por qué seguimos escribiéndolos? La respuesta es sencilla: nos preocupa lo que podría sucederle al resto del mundo si dejamos de escribir estos artículos. Cuándo Sir Arthur Conan Doyle decidió acabar con Sherlock Holmes (tirándolo por un precipicio en la capítulo "El Problema Final"), pensó que se deshacía simplemente de un personaje sobre el que ya no deseaba escribir más. Lo que ocurrió es que se produjo una protesta inmensa en todo el mundo. Según algunos informes, los londinenses se pusieron brazaletes y telas en los sombreros en señal de luto cuando se enteraron de la noticia. "El Problema Final" se publicó en The Strand Magazine en 1893 y 20.000 personas cancelaron sus suscripciones como protesta.

Vaya.

A los Chicos del scripting nos preocupa que la interrupción de nuestros artículos acerca del scripting de base de datos tenga un efecto similar a nivel planetario. No deseamos ser la causa de tanto dolor y desesperación y, desde luego, no queremos que 20.000 de nuestros lectores cancelen su suscripción a TechNet Magazine. Por lo tanto, queremos anunciar que los Chicos del scripting continuarán escribiendo artículos acerca del scripting de base de datos. ¿Cuándo? ¿Qué tal ahora mismo?

Adición de registros a una base de datos

Este mes hemos pensado mostrarle unas cuantas artimañas para trabajar con bases de datos. Y sí, estos trucos son una adición a los que le mostramos en nuestra columna de septiembre de 2007 (technetmagazine.com/issues/2007/09/HeyScriptingGuy). Ni siquiera los Chicos del scripting caerían tan bajo como para sacar el mismo artículo dos veces.

Bueno, no a menos que pensáramos que podríamos hacerlo e irnos de rositas.

Permítanos comenzar mostrándole una manera sencilla de agregar un registro a una base de datos. Asuma que tenemos una base de datos denominada C: \Scripts\Inventario.mdb, y que esta base de datos incluye una tabla llamada Equipos. La tabla Equipos incluye los siguientes campos:

  • NombreEquipo
  • NúmeroSerie
  • NombreUsuario
  • Departamento

¿Cómo agregamos un nuevo equipo a esa base de datos? Observe el código de la Figura 1.

Figure 1 Adición de un registro a la tabla Equipos

Const adOpenStatic = 3
Const adLockOptimistic = 3

Set objConnection = CreateObject("ADODB.Connection")
Set objRecordSet = CreateObject("ADODB.Recordset")

objConnection.Open _
    "Provider = Microsoft.Jet.OLEDB.4.0; " & _
        "Data Source = C:\Scripts\Inventory.mdb"

objRecordSet.Open "INSERT INTO Computers (ComputerName, SerialNumber, UserName, Department)" &  _
    "VALUES ('atl-ws-07', '11234', 'Ken Myer', 'Finance')", _
        objConnection, adOpenStatic, adLockOptimistic

No vamos a hablar en detalle de las primeras líneas de este script. Si necesita esa información puede echar un vistazo a algunos de los recursos que tenemos en el Script Center en microsoft.com/technet/scriptcenter. Baste con decir que usamos las constantes adOpenStatic y adLockOptimistic para controlar de tipo de cursor y el bloqueo de registros para nuestro conjunto de registros. Sí, eso suena impresionante, pero el tema es bastante sencillo. Cuando acabemos de crear instancias de los objetos ADODB.Connection y ADODB.Recordset, usaremos este comando para abrir nuestra base de datos:

objConnection.Open _
"Provider = Microsoft.Jet.OLEDB.4.0; " & _
"Data Source = C:\Scripts\Inventory.mdb"

A propósito, este comando abre una base de datos de Microsoft® Access® 2003. Para abrir una base de datos de Access 2007, use este otro comando:

objConnection.Open _
"Provider = Microsoft.ACE.OLEDB.12.0; " & _
"Data Source = C:\Scripts\Inventory.accdb"

Y, aunque comenzamos a desviarnos un poco del tema principal, puede usar un comando similar a éste para abrir una base de datos de SQL Server®:

objConnection.Open _
"Provider=SQLOLEDB;" & _
"Data Source=atl-sql-01;" & _
"Trusted_Connection=Yes;" & _ "InitialCatalog=Inventory;" & _
"User ID=fabrikam\kenmyer;Password=34DE6t4G!;"

Hasta este punto, todo lo que le hemos mostrado es código reutilizable. Como verá muy pronto, prácticamente todos sus scripts de base de datos comenzarán con estas mismas líneas de código. La parte que realmente deseamos enfatizar es esta línea:

objRecordSet.Open _
  "INSERT INTO Computers " & _
  "(ComputerName, SerialNumber, " & _
  "UserName, Department) " & _
    "VALUES ('atl-ws-07', 'A14B1234', " & _
      "'Ken Myer', 'Finance')", _
        objConnection, adOpenStatic, _
        adLockOptimistic

Vamos a usar una consulta INSERT INTO para insertar un nuevo registro en la tabla Equipos. Advierta que, siguiendo el nombre de la tabla, incluimos los nombres de todos los campos para los que tenemos valores, separando esos nombres de campo con comas y cerrando todos los nombres entre paréntesis.

Quizás se esté preguntando: ¿que quieren decir los Chicos del scripting con "todos los campos para los que tenemos valores"? Es una buena pregunta. Bien, supongamos que todavía no se ha asignado un Departamento a este nuevo equipo. Asumiendo que la base de datos nos permita poner un valor Null en el campo Departamento, podemos simplemente dejar el valor Departamento fuera de la lista y no asignarle ningún valor. Es decir, podríamos escribir una consulta como esta:

objRecordSet.Open _
  "INSERT INTO Computers " & _
  "(ComputerName, SerialNumber, " & _
  "UserName) " & _
    "VALUES ('atl-ws-07', 'A14B1234', " & _
      "'Ken Myer')", _
        objConnection, adOpenStatic, _
        adLockOptimistic

Y hablando de asignar valores, mire lo que viene después de los nombres de campo: la palabra clave VALUES seguida por los valores que deseamos asignar a cada campo (que también están cerrados entre paréntesis). Tenga presente que cuando incluimos en una lista los nombres de campo, éstos pueden estar en cualquier orden. Por ejemplo, ponemos NombreEquipo primero aunque quizás no sea el primer campo en la base de datos. Eso está bien para los nombres de campo, pero no tanto para los valores. Los valores se deben listar exactamente en el mismo orden que los campos. Si nuestro primer campo incluido en una lista es NombreEquipo, entonces nuestro primer valor debe ser el nombre del equipo. De lo contrario, vamos a tener problemas. Por ejemplo, acabaremos asignando el nombre de usuario o el número de serie al campo NombreEquipo.

Como puede ver, asignar valores no es muy complicado, pero debe asegurarse de que da formato a los valores en función del tipo de datos: Los valores de fecha y de cadena deben cerrarse entre comillas. Por el contrario, los valores numéricos y booleanos no deben cerrarse entre comillas.

Ah, y si tiene un valor que cuenta con sus propias comillas simples (como el apellido O'Brien), entonces tendrá que "escapar" de esa comilla simple duplicándola:

'O''Brien'

Una locura, pero cierto.

Eliminación de registros de una base de datos

La verdad es que esto ha estado muy bien: podemos agregar un registro a una base de datos ejecutando una sola consulta de SQL. ¿Podría haber algo mejor que eso? No que nosotros sepamos.

Bueno, salvo poder eliminar varios registros de una base de datos mediante una sola consulta.

Suponga que su compañía decide eliminar el departamento de recursos humanos. (No, no es ninguna broma; ya tenemos bastantes problemas con el departamento de recursos humanos de Microsoft tal y como están las cosas). ¿Cómo puede eliminar todos los equipos de recursos humanos de la base de datos de inventario? La Figura 2 muestra una forma de hacerlo.

Figure 2 Cómo eliminar varios registros

Const adOpenStatic = 3
Const adLockOptimistic = 3

Set objConnection = CreateObject("ADODB.Connection")
Set objRecordSet = CreateObject("ADODB.Recordset")

objConnection.Open _
    "Provider = Microsoft.Jet.OLEDB.4.0; " & _
        "Data Source = C:\Scripts\Inventory.mdb"

objRecordSet.Open "DELETE * FROM Computers Where Department='Human Resources'", _
    objConnection, adOpenStatic, adLockOptimistic

Como puede ver, este script empieza exactamente igual que el script para agregar un registro nuevo: definiendo algunas constantes, creando un par de objetos y conectándose a la base de datos Inventario.mdb. Después de esto simplemente usamos una consulta DELETE para eliminar todos los registros (*) de la tabla Equipos, o por lo menos todos los registros en los que el campo Departamento es igual a Recursos humanos:

objRecordSet.Open _
  "DELETE * FROM Computers " & _
   "Where Department='Human Resources'", _
    objConnection, adOpenStatic, _
    adLockOptimistic

Es tan fácil como caerse por un precipicio.

No se ofenda, Sherlock Holmes.

Actualización de registros en una base de datos

Sin duda, todos estamos de acuerdo en que sería muy divertido eliminar todo rastro del departamento de recursos humanos. Nota para el departamento de Recursos humanos de Microsoft: queremos decir divertido para otras personas y sus respectivos departamentos de recursos humanos. No nosotros. No nos lo pasaríamos bien en absoluto. No obstante, puede que eliminar todos los equipos de recursos humanos de la base de datos de inventario no sea lo mejor que podría hacer. ¿Por qué no? Bien, a menos que sus empleados de recursos humanos huyeran con todo el hardware (que parece ser algo que harían; vale, vale, hemos dicho que no gastaríamos bromas), esos equipos aún pertenecerían a la compañía. Esto significa que esos equipos deben seguir en la lista de la base de datos. Con esta idea en mente, hagamos esto. En vez de eliminar esos registros de la base de datos, actualicemos simplemente todos los registros. Sólo cambiaremos el nombre del departamento para cada uno de estos equipos de Recursos humanos a Ninguno. Es decir, ejecutaremos el script que se muestra en la Figura 3.

Figure 3 Actualización de registros

Const adOpenStatic = 3
Const adLockOptimistic = 3

Set objConnection = CreateObject("ADODB.Connection")
Set objRecordSet = CreateObject("ADODB.Recordset")

objConnection.Open _
    "Provider = Microsoft.Jet.OLEDB.4.0; " & _
        "Data Source = C:\Scripts\Inventory.mdb"

objRecordSet.Open "UPDATE Computers Set Department = 'None' " & _
    "Where Department = 'Human Resources'", _
    objConnection, adOpenStatic, adLockOptimistic

Una vez más, la única diferencia de este script es la consulta de SQL. En este caso usamos una consulta UPDATE para actualizar los registros de la tabla Equipos. Además de la palabra clave UPDATE, necesitamos agregar dos parámetros que le dicen a la consulta cómo actualizar los registros y qué registros hay que actualizar:

  • Set Departamento = "Ninguno". Esto especifica simplemente el nuevo valor para el campo Departamento. Puede establecer más de un valor de campo a la vez usando una sintaxis como esta: Set Departamento = "Ninguno", NombreUsuario = "Ninguno". Como puede ver, esta acción establece los campos Departamento y NombreUsuario en Ninguno.
  • Where Departamento = "Recursos humanos". Ésta es una cláusula Where estándar que indica qué registros deben ser actualizados. En este caso, serían todos los registros que tienen un campo Departamento igual a Recursos humanos. Metemos esto por una sencilla razón: no queremos modificar todos los registros de la base de datos, sólo los registros (equipos) que pertenecieron anteriormente al departamento de Recursos humanos.

Aquí tiene algo interesante que puede hacer con la consultas Update. Digamos que su compañía ha decidido dar a todos un aumento del 10 por ciento por el costo de la vida. Obviamente, los Chicos del scripting son como Sir Arthur Conan Doyle en al menos una cosa: en nuestras escritos ambos tratamos con mundos de ficción. Aquí tiene un script que abre una tabla llamada Empleados y modifica el campo Salario para cada empleado. ¿Y en qué establece el campo Salario? La consulta establece este campo en el salario actual del empleado a 1.1. Es decir, le da a cada empleado un aumento del 10 por ciento. ¡Si fuera tan fácil! Aquí está la consulta:

objRecordSet.Open _
  "Update Employees " & _
  "Set Salary = (Salary * 1.1)", _
    objConnection, adOpenStatic, _
    adLockOptimistic

¿La cosa interesante? Como puede ver, puede hacer cálculos en las consultas Update. ¿Ha decidido ampliar el contrato de todos sus empleados temporales otros 60 días? En ese caso, una consulta similar a la siguiente podría venirle bien:

objRecordSet.Open _
  "Update TempEmployees " & _
  "Set ContractExpirationDate = " & _
  "(ContractExpirationDate + 60)", _
    objConnection, adOpenStatic, _
    adLockOptimistic

Formas interesantes de recuperar datos

Hablando de consultas que pueden venir bien, cerremos capítulo mirando algunas maneras interesantes de recuperar datos de una base de datos. Por ejemplo, aquí tenemos un script sencillo a la vez que muy útil. Suponga que nuestra base de datos de inventario incluye un campo llamado Precio, que en buena lógica representa el precio del equipo. ¿Desea saber cuáles son los cinco equipos más caros de su organización? El script de ejemplo de la Figura 4 se lo dirá.

Figure 4 Clasificación del conjunto de registros

Const adOpenStatic = 3
Const adLockOptimistic = 3

Set objConnection = CreateObject("ADODB.Connection")
Set objRecordSet = CreateObject("ADODB.Recordset")

objConnection.Open _
    "Provider = Microsoft.Jet.OLEDB.4.0; " & _
        "Data Source = C:\Scripts\Test.mdb" 

objRecordSet.Open "SELECT Top 5 * FROM Computers Order By Price", _
     objConnection, adOpenStatic, adLockOptimistic

objRecordSet.MoveFirst

Do Until objRecordset.EOF
    Wscript.Echo objRecordset.Fields.Item("ComputerName")
    objRecordset.MoveNext
Loop

objRecordSet.Close
objConnection.Close

Como puede ver, en esta consulta de SQL hemos hecho dos cosas: ordenamos el conjunto de registros por Precio y pedimos sólo los cinco primeros equipos (es decir, los cinco más caros). Eso es lo que hace SELECT Top 5. Si quisiéramos una lista de los 10 equipos más caros, usaríamos la siguiente consulta:

objRecordSet.Open _
  "SELECT Top 10 * FROM Computers " & _
  "Order By Price", _
    objConnection, adOpenStatic, _
    adLockOptimistic

Nota. Agregamos también código que se repite a través del conjunto de registros y muestra el nombre de cada equipo. No necesitábamos este código en los scripts anteriores porque no devolvían ni mostraban datos.

Alternativamente, podemos obtener los primeros valores de 10 por ciento usando esta consulta:

objRecordSet.Open _
  "SELECT Top 10 PERCENT * " & _
  "FROM Computers Order By Price", _
    objConnection, adOpenStatic, _
    adLockOptimistic

¿Y si deseamos obtener los equipos más baratos? Ningún problema; simplemente usamos el mismo enfoque, salvo que esta vez ordenamos el conjunto de registros en orden descendente (es decir, desde el precio más bajo al más alto). En otras palabras, usamos esta consulta con DESC indicando que el conjunto de registros debe ordenarse en orden descendente:

objRecordSet.Open _
  "SELECT Top 5 * FROM Computers " & _
  "Order By Price DESC", _
    objConnection, adOpenStatic, _
    adLockOptimistic

¿Lo ve? Le dijimos que era útil.

Aquí tiene dos más que quizás encuentre interesantes. Supongamos que su base de datos tiene un campo llamado Presupuestado que hace un seguimiento de la cantidad de dinero presupuestada originalmente para el equipo. ¿Desea comparar el costo real con la cantidad presupuestada? Esta consulta devuelve una lista de equipos que cuestan más que la cantidad presupuestada:

objRecordSet.Open _
  "SELECT * FROM Computers " & _
  "Where (Budgeted < Price)", _
    objConnection, adOpenStatic, _
    adLockOptimistic

Mientras tanto, esta consulta calcula el precio medio de todos los equipos (SELECT AVG(Price) FROM Equipos) y, a continuación, devuelve una lista de todos los equipos que cuestan menos que el precio medio:

objRecordSet.Open _
  "SELECT * FROM Computers " & _
  "WHERE Price < " & _
  "(SELECT AVG(Price) FROM Computers)", _
    objConnection, adOpenStatic, _
    adLockOptimistic

Y sí, tiene razón, usamos una consulta SELECT en nuestra cláusula Where. ¿Cómo funciona? Bien, eso es otro cuento para otro día. Pero, como puede ver, abre la puerta a la recuperación de toda clase de información interesante mediante consultas de SQL.

Moraleja:

Como apuntamos al principio de la columna de este mes, ni siquiera estábamos seguros de querer escribir otro artículo acerca del scripting de base de datos. Sin embargo, nos alegramos de haberlo hecho, en parte porque pensamos que algunas de estas consultas les serán muy útiles y en parte por los siguiente: Si no hubiéramos escrito esta columna ahora, habríamos acabado haciéndolo tarde o temprano. A fin de cuentas, hasta Sir Arthur Conan Doyle fue forzado a resucitar a Sherlock Holmes de entre los muertos. Sir Arthur dio los primeros pasos en esa dirección escribiendo otro capítulo de Sherlock Holmes que supuestamente tuvo lugar antes de la muerte del detective. Después de eso, sin embargo, simplemente cedió a la presión de sus lectores, confeccionando un cuento algo dudoso explicando que Sherlock Holmes había fingido su propia muerte. Resulta que todos, y por lo tanto todas las cosas, estaban perfectamente.

Lo que resulta interesante es que cuando apareció esa última historia de Sherlock Holmes, The Strand Magazine se hizo con 30.000 nuevos suscriptores. Los agradecidos editores de Sir Arthur en Gran Bretaña y EE.UU. respondieron convirtiéndole en el escritor mejor pagado del mundo.

Mmmm, Sir Arthur Conan Doyle devolvió un personaje adorado por sus lectores y se convirtió en el escritor mejor pagado del mundo. Ahora, los Chicos del scripting han devuelto un tema adorado por sus lectores (y sin tener que fingir la muerte de nadie). Me pregunto si las buenas gentes de TechNet Magazine se han dado cuenta.

Dr. Scripto's Scripting Perplexer

El desafío mensual que prueba no sólo sus habilidades para resolver rompecabezas, también sus habilidades para el scripting.

Abril de 2008: Escoja una carta

En el enigma de este mes, necesita insertar todas las letras de la A a la O en los espacios azules para revelar el nombre de una función de VBScript. Cada letra (de la A a la O) debe usarse sólo una vez y las letras no aparecen por orden alfabético. La letra insertada podría estar al principio, al final o en el medio del nombre de la función. Por ejemplo, en la siguiente línea insertaría la letra D para completar el nombre de la función IsDate:

  (Hacer clic en la imagen para ampliarla)

Ahora pruebe usted. Inserte las letras de la A a la O para revelar nombres de la función VBScript en esta cuadrícula:

**** (Hacer clic en la imagen para ampliarla)

ANSWER:

Dr. Scripto's Scripting Perplexer

Respuesta: Escoja una carta, abril de 2008

  (Hacer clic en la imagen para ampliarla)

Los encargados del scripting trabajan para Microsoft, mejor dicho, están contratados por Microsoft. Cuando no juegan al béisbol, ni entrenan ni lo ven (u otras actividades varias), dirigen el TechNet Script Center. Visite la página web www.scriptingguys.com.

© 2008 Microsoft Corporation and CMP Media, LLC. Reservados todos los derechos; queda prohibida la reproducción parcial o total sin previa autorización.