jueves, 19 de octubre de 2017

Creado Ribbons de forma visual -Segunda parte-

Una vez que sabemos que para crear nuestro Ribbon en access requerimos saber de xml, y conocer cuáles son los elementos y sus atributos que Microsoft establece, solo nos resta comenzar a jugar con él para integrarlo en nuestras bases de datos.
Cuando comenzamos a generar el Ribbon mucha gente prefiere hacerlo en Visual Estudio o Notepad++, pues nos marca los errores del xml al escribir el código, otros lo hacen en cualquier procesador de texto como el Word o el Block de notas, nosotros lo vamos a hacer en el mismo Access y dejar que nos indique si cometemos algún error y aparte podemos ir viendo visualmente lo que estamos haciendo.
La manera más fácil y común de agregar el Ribbon a nuestra base de datos, es guardando el XML del Ribbon en una tabla llamada UsysRibbons, la otra forma es cargar el Ribbon con el método LoadCustomUI ya sea con un archivo xml (usando msxml, DAO o ADO) o proveniente de una tabla y pasándolo a una cadena de texto.
Precisamente esto último es lo que vamos a hacer para poder ir haciendo en forma visual nuestro Ribbon solo necesitamos tener un formulario con un cuadro de texto para ir escribiendo el xml y un botón para cargarlo cada vez que hacemos cambios, mediante el método LoadCustomUI, del objeto Application que hace referencia a la aplicación activa.
Primero para usar un Ribbon personalizado necesitamos tener la librería Microsoft Office 14.0 Object Library así que lo tenemos que agregar en VBA (ALT + F11) => Herramientas => Referencias y vamos a crear nuestro formulario en Crear => Formularios => Formulario en Blanco y nos vamos a vista diseño y agregamos un cuadro de texto y lo nombramos txtRibbon y un botón cmdActualizar y en el evento al hacer click del botón ponemos:
Private Sub cmdActualizar_Click()
Dim nomForm
Randomize
nomForm = Int((1000 - 1 + 1) * Rnd + 1)
LoadCustomUI nomForm, txtRibbon
RibbonName = nomForm
End Sub

Yo agregué un botón más para salir del formulario y como soy muy original le llame salir y lo hice con el asistente de botones, ponemos el cuadro de texto lo más grande posible y nos queda algo como esto:



Solo nos resta probar a ver si funciona, primero vamos a usar algunos ribbon creados, pero antes de eso, para que Access nos avise si existe algún error tenemos que entrar en Archivo => Opciones => Configuración de cliente => General => y activar Mostrar errores de interfaz en el complemento => Aceptamos y salimos y volvemos a entrar (o compactamos y reparamos base).
Podemos probar nuestro formulario y que mejor usando una página de Microsoft sobre Ribbon:
https://msdn.microsoft.com/en-us/library/dd548010(v=office.12).aspx
Lo podemos probar pegando esto:

<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui">
  <ribbon>
    <tabs>
      <tab id="tab1" label="My Tab">
        <group id="grp1" label="My Group">
        </group>
      </tab>
    </tabs>
  </ribbon>
</customUI>

Le damos a Actualizar y debemos de ver esto:


Como vemos tenemos una nueva pestaña al final llamada My Tab y cuenta con un grupo llamado My Group sin botones, aquí es buen momento para probar y ver los mensajes que nos da Access cuando nos equivoquemos, por ejemplo quitemos unas comillas en id=tab1"  y probemos y nos da:


Observemos que la línea es exactamente la que modificamos y si contamos cada carácter nos damos cuenta que es exactamente donde deben ir las comillas faltantes y para cerciorarse más que entendemos el error, nos dice se esperaba comillas. Hagan más pruebas, cambien el nombre de algún elemento o atributo y pongance a entender los mensajes tan directos que da Access.
Vamos entonces a ver para que sirve cada Elemento, y que significa, comenzando por el customUI, como su nombre lo indica custom = Personalizar y UI son las abreviaciones de Interfaz de Usuario, es el elemento raíz y debemos siempre de abrir y cerrar con él, si observamos en el elemento raíz customUI tenemos un xmlns seguido de una dirección de internet, el xmlns separamos xml ns = namespaces y en español y todo junto Espacio de Nombre del xml.
El espacio de nombre del xml por lo general es una dirección de internet, aunque esta no necesariamente existe, por ejemplo Microsoft tiene dos espacios de nombre uno para Access 2007 y otro para 2010/2013/2016 en Access 2007:
http://schemas.microsoft.com/office/2006/01/customui
Y en Access 2010/2013/2016
http://schemas.microsoft.com/office/2009/07/customui
En ambos casos no existe en internet la dirección, pero al instalar Office está integrado el esquema del xml dirigido al espacio de nombre que valida cada Elemento y Atributo para poder desplegar el Ribbon.
El elemento raíz customUI aparte del espacio de nombre dispone de dos atributos el onLoad es el que controla como reacción cada elemento de la cintas personalizadas e invalida la cinta personalizada cuando existe algún cambio y el loadImage sirve para cargar las imágenes personalizadas que tenemos en el ribbon y las almacena en cache, es la forma más rápida de cargar las imágenes pero no nos permite cambiarlas dinámicamente, en un futuro ya hablaremos de eso.

Si observamos Microsoft establece una convención de nombres para su esquema del Ribbon y consiste en que los elementos y atributos comienzan en minúsculas y si son palabras compuestas, ya sea una o varias van juntas pero cada palabra posterior a la primera su primer letra va con mayúsculas, ejemplo:

customUI
onLoad
loadImage
dialogBoxLauncher

Ya vimos en la primera parte que después del elemento raíz sigue los commands vamos a dejarlos a un lado y concentrarnos en el Ribbon, que como recordaremos el customUI tiene tres hijos que son: commands, ribbon y backstage, solo nos concentramos en el ribbon pues no necesitamos a los otros por el momento.

El elemento ribbon tiene un solo atributo startFromScratch que es un booleano, y debe ser true o false por default es falso si no lo ponemos estamos diciendo que es falso y startFromScratch significa iniciar desde el inico, así que podemos poner startFromScratch = ”true” y le decimos que nuestro Ribbon es único y startFromScratch = ”false” significa que nuestro ribbon aparece después del ribbon normal de Microsoft.

El elemento ribbon tiene tres hijos la cinta de acceso rápido (Qat), el botón office y las pestañas (tabs) por vamos a concentrarnos solo en el tabs. El elemento tabs es un elemento que sirve para contener cada una de las pestañas que se crean, es uno de los elementos más simples y no tiene ningún atributo, sus hijos son cada una de las pestañas (tab).

El elemento tabs (Pestañas) engloba a cada pestaña y no tiene atributos así que es muy fácil usarlo y finalmente ya podemos comenzar a ver algo que aparecerá en nuestro ribbon con el siguiente elemento. 

El elemento tab es el que crea cada pestaña y pueden existir todos los tab que sean necesarios, este dispone de un identificador que puede ser cualquiera de los siguientes: id, idMso o idq, finalmente ya podemos comenzar a ver algo que aparecerá en nuestro ribbon.
Con la pestaña tab debemos poner ya sea un id que es un identificador único e irrepetible, o un idMso que son todos los identificadores que podemos reciclar de Microsoft o el idq es una pestaña compartida con espacio de nombre.

También tab debe llevar una etiqueta (label) para asignar el nombre de cada pestaña alfanumérico y es opcional si lleva otros atributos como visibles o getvisible, tag, keytip o getkeytip y podemos decidir la posición donde poner la pestaña dentro del ribbon con insertAfterMso, insertBeforeMso.

El elemento tab solo puede tener un elemento group ya sea personalizado o un grupo incorporado de Microsoft.

El elemento group al igual que tab debe de llevar un identificador (id, idMso, idq), una etiqueta (label) y si hay necesidad de esconder el grupo y hacerlo visible en forma dinámica se requiere getVisible.

El elemento Group tiene elementos hijos los cuales pueden ser: box, button, buttonGroup, checkBox, comboBox, dialogBoxLauncher, dropDown, editBox, gallery, labelControl, menú, separator, splitButton y el toggleButton.

Es aquí donde ya podemos comenzar a sacarle partida a los ribbons con los controles, vamos a comenzar con el control más tradicional  que es el elemento button, y desde luego que tienen que llevar atributos estáticos: description, enabled, id, idMso, idQ, image, imageMso, insertAfterMso, insertAfterQ, insertBeforeMso, insertBeforeQ, keytip, label, screentip, showImage, showLabel, size, supertip, tag, visible, y los dinámicos getDescription, getEnabled, getImage getKeytip, getLabel, getScreentip, getShowImage, getShowLabel, getSize, getSupertip,  getVisible, onAction.

Vamos a hacer un Ribbon con una pestaña, un grupo y dos botones, si usamos identificadores de Microsoft (idMso) o imágenes de Microsoft no necesitamos gran cosa y podemos hacer que funcione sin programar código en un Módulo.
Comenzamos con dos controles de Microsoft ExportExcel e ImportExcel todos los id o las imágenes de Microsoft las podemos obtener de un Excel en las diferentes versiones el cual se instala bajando un pequeño instalador solo tienes que buscar en internet: Office 2xxx: Developer References

Donde xxx es la versión de offices que requieres. Por ejemplo para la 2010 la página es :
https://www.microsoft.com/en-us/download/details.aspx?id=2451 y al instalarlo te crea una carpeta en C:\Office 2010 Developer Resources

O lo sacamos de Archivo => Opciones de Access => Personalizar cinta de opciones => Comandos Disponibles en => Todas.
Escribimos nuestro ribbon:

<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui">
<ribbon startFromScratch="true">
<tabs>
<tab id="pestana01" label="MyPestaña">
<group id="grp1" label="My Grupo">
<button idMso="ExportExcel"/>
<button idMso="ImportExcel"/>
</group>
</tab>
</tabs>
</ribbon>
</customUI>

Y le damos a actualizar y nos aparece:

Si le damos a cualquiera de los dos botones nos abre el asistente para importar o exportar a Excel dependiendo cual seleccionamos, así que prácticamente no requerimos mucho más si usamos los idMso que tiene Micrososft.

Pero por ejemplo yo quiero ver los botones grandes no pequeños, para eso tenemos un atributo llamado size, que como ya se dieron cuenta, tiene un estado por default que es  precisamente “normal” y otro “large” vamos a poner el “large” para ver la diferencia y vamos a ponerle un nombre a la etiqueta para diferenciar uno de otro.

<button idMso="ExportExcel" label="Exportar a Excel" size="large"/>
<button idMso="ImportExcel" label="Importar a Excel" size="large"/>



En la próxima entrega vamos a continuar viendo más controles y nos centraremos con las devoluciones de llamadas (Callback), les dejo archivo.


jueves, 9 de febrero de 2017

El Ribbon de Access y el XML primera parte


Si estoy aprendiendo Access, por que debo también saber XML, en un principio creía que solo debía ocuparme de Access, pero como vivimos en un mundo globalizado, prácticamente las fronteras están a un solo click de distancia y como el principal problema es que la información que puedo acceder son de otras fuentes no necesariamente están en Access y una de las mejores forma de intercambiar información es mediante archivos XML, así que tarde o temprano nos vamos a enfrentar con él. Aparte que Access ya lo usa en las macros, internamente las convierte a XML y en el Ribbon, también cuenta con un excelente asistente para exportar e importar archivos xml, pero claro eso no es suficiente, es por eso que Microsoft nos dio la librería msxml, con la cual podemos interactuar de una manera muy trasparente con un archivo XML.
Xml es un metalenguaje de marcado, es decir con el creamos nuestros propios lenguajes de programación, por ejemplo para crear un Ribbon el lenguaje xml tiene un esquema en el que encontramos todos los elementos y valores que podemos tener.
El xml sirve para almacenada, transmitir y trasladar información sin importar la aplicación o plataforma, eso hace que prácticamente lo usen todos los lenguajes y sistemas, la otra gran ventaja es que el W3C que el órgano encargado de realiza las normas de internet es la encargada del xml, por lo tanto es libre y gratis.
Que debo de saber sobre la sintaxis:
·    Todo elemento tiene que tener su correspondiente etiqueta de inicio y de cierre, o una sola etiqueta vacía.

·    Todo documento, debe haber un elemento (llamado raíz de documento) que contenga a los demás.

·    Todos los elementos deberán estar correctamente anidados.

·    Todos los valores de los atributos deberán ir entre comillas.

En el caso específico del lenguaje xml del Ribbon, Microsoft creo un esquema en el que establece como nombrar a cada elemento y que atributos y valores pueden tener, no podemos inventarnos nada del XML del Ribbon, cualquier cosa que este mal escrita o errónea sencillamente no se despliega el Ribbon.


Pero me preguntaría de donde saco el nombre de cada elemento (Controles en el Ribbon) y sus atributos, Microsoft nos da unos pdf con toda la información




Aparte existe una página web de accessRibbon fantástica donde podemos ver prácticamente todos los Elementos, Atributos y su devolución de llamadas a la función que le corresponde y los elementos que la componen.

Vamos a ir viendo como ir creando el Ribbon en access e ir integrando sus atributos y elementos en forma gráfica desde access, en la segunda parte.


miércoles, 23 de septiembre de 2015

La Tabla Números

Primero agradecer a José Bengoechea Ibaceta (Chea) y Emilio Sancha, por haber contribuido en el mejoramiento, revisión y agrandado de estas líneas.

En Access como en la vida, siempre hay varias formas de llegar a un resultado, pero para los amantes de las consultas, la tabla números más que un aliado es una herramienta indispensable para resolver problemas que sin ellas sería realmente difícil de hacer, aunque en lugar de hacerlo en la consulta lo podemos hacer en vba, siempre se puede generar un bucle en un recordset o hacer alguna función para hacer lo mismo.

Si observamos muchos Maestros como Chea, Raipon, Julián Sánchez, Emilio Sancha, Patxi Sanz, Mihura por mencionar solo algunos, la utilizan en sus bases de datos, de hecho hay algunos que afirman que la tabla números siempre debe estar presente en una base de datos, y esta solo consta de un campo numérico, que comienza en 0 o 1 y llega hasta donde lo requerimos,

En todos estos casos podemos usar la tabla números para obtener una solución:

Generar un rango de fechas
Encontrar la fecha que falta
Contar fechas
Repetir registros
Generar segmentos de hora
Separar una cadena
Calendario Mensual

La gran mayoría de estos ejemplos son sacados del Foro de Access y VBA, y fue realizado por alguno de los Maestros antes mencionados.

Lo primero es generar la tabla números, Raipon tiene una forma de generarla como consulta a partir una tabla del sistema y Emilio Sancha con solo del 1 al 31 genera con DateSerial todos los días desde 2001 hasta el 2027. Yo en Excel puse 0, 1 y 2 y los copio como serie hasta el número que quiero en este caso 1999 pero dependiendo de lo que hagan con ella, 2000 puede quedarse muy corto, selecciono y copio la columna y la pego en el campo Num de Access.

Nums
Num
0
1
2
3
. .
1999

Algunos comienzan la tabla números con el 0 otros desde el 1 la verdad no es importante depende lo que vamos a hacer después, por ejemplo si comenzamos con el 0 y queremos agregar registros tenemos que agregar al número+1 para obtener lo que deseamos.

Cuando ponemos en una consulta dos tablas sin relación, lo que obtenemos es un producto cartesiano que es la totalidad de los pares ordenados, en otras palabras obtenemos todas las posibles combinaciones de filas de ambas tablas, por lo tanto si nuestra dos tablas tienen 1,000 registros cada una, la tabla resultante es de 1 millon de registros, por lo tanto cuando usemos la tabla de números es preferible usar una tabla intermedia con solo los rangos que necesitemos, si no lo hacemos así corremos el riesgo que la consulta puede tardar en ejecutarse.

Generar un rango de fechas:

Partimos que tenemos una tabla llamada RangoFechas y queremos que nos muestre todas las fechas que están dentro del rango.

RangoFechas
Id
Inicio
Fin
1
01/09/2015
04/09/2015
2
02/09/2015
06/09/2015


Para eso hacemos una consulta con las dos tablas sin relacionar, esta nos dará la multiplicación de la tabla números x 2 registros de la tabla RangoFechas, si por ejemplo agregamos de la tabla Nums el campo Num tenemos dos veces repetidos todos los campos Num.

Siempre que tratemos con fechas vamos a agregar CDate o CVDate, para que Access convierta a fechas el campo, además CDate reconoce la configuración regional del sistema, así Access reconoce que 5/1/2015 es 5 de enero.

En base a esto es muy fácil agregar nuestra serie de fechas si en un campo ponemos [Inicio]+[Num] podemos observar que ya tenemos nuestras fechas iniciales y cada dia subsiguiente, solo nos falta detenerlo en la fecha fin, que en criterios tendríamos que agregar <=[Fin] la consulta completa queda así:

SELECT Nums.Num, RangoFechas.Id, CVDate([Inicio])+[Num] AS Rango
FROM Nums, RangoFechas
WHERE (((CVDate([Inicio])+[Num])<=CVDate([Fin])))
ORDER BY RangoFechas.Id, CVDate([Inicio])+[Num];

CtaRangoFechas
Num
Id
Rango
0
1
01/09/2015
1
1
02/09/2015
2
1
03/09/2015
3
1
04/09/2015
0
2
02/09/2015
1
2
03/09/2015
2
2
04/09/2015
3
2
05/09/2015
4
2
06/09/2015

Encontrar las fechas que faltan:

Ahora vamos a basarnos en una tabla que llamaremos fechaFaltante que contiene:

fechafaltante
Id
Nombre
Fecha
1
1
01/09/2015
2
1
02/09/2015
3
1
03/09/2015
4
1
06/09/2015
5
2
02/09/2015
6
2
03/09/2015
7
2
04/09/2015
8
2
06/09/2015
9
2
08/09/2015

Nos faltan las fechas 4/09/15 y 5/09/15 del Nombre 1 y el 5/09/15 y 7/09/15 del Nombre 2. Para hacerlo necesitamos obtener la fecha Mínima y la Máxima por Nombre, después crear con esas fechas una consulta de rangos por fechas y posteriormente comparar la consulta creada con la de fechasfaltante diciéndole que solo me de las nulas de la fechafaltante, ¿fácil verdad?

Creamos una consulta de totales de la tabla fechafaltante con tres campos Inicio:[Fecha] en totales como Min, Fin:[Fecha] en totales como Max y el campo Nombre y obtenemos :

SELECT Min(CVDate([Fecha])) AS Inicio, Max(CVDate([Fecha])) AS Fin, fechafaltante.Nombre
FROM fechafaltante
GROUP BY fechafaltante.Nombre;

Consulta1
Inicio
Fin
Nombre
01/09/2015
06/09/2015
1
02/09/2015
08/09/2015
2

Ahora con la consulta1 generamos un rango de fechas como en el punto anterior:

SELECT CVDate([Inicio])+[num] AS Lasfechas, Consulta1.Nombre
FROM Nums, Consulta1
WHERE (((CVDate([Inicio])+[num])<=CVDate([Fin])))
ORDER BY Consulta1.Nombre, CVDate([Inicio])+[num];

Consulta2
Lasfechas
Nombre
01/09/2015
1
02/09/2015
1
03/09/2015
1
04/09/2015
1
05/09/2015
1
06/09/2015
1
02/09/2015
2
03/09/2015
2
04/09/2015
2
05/09/2015
2
06/09/2015
2
07/09/2015
2
08/09/2015
2

Y estamos listos para generar la consulta de nuestras fechasfaltantes, vamos a agregar la consulta2 y la tabla de fechasfaltantes y relacionar Lasfechas=> Fecha y Nombre=> Nombre vamos a hacer un LEFT JOIN, es decir incluir todos los registros de Consulta2 y solo los registros de fechafaltante donde los campos combinados sean iguales y agregamos los campos Lasfechas y Nombre de la consulta2 y Fecha de la tabla fechafaltante y le decimos en criterio Es Nulo, algo así:

SELECT Consulta2.Lasfechas, Consulta2.Nombre
FROM Consulta2 LEFT JOIN fechafaltante ON (Consulta2.Nombre = fechafaltante.Nombre) AND (Consulta2.[Lasfechas] = fechafaltante.Fecha)
WHERE (((fechafaltante.Fecha) Is Null))
ORDER BY Consulta2.Lasfechas, Consulta2.Nombre;


Ctafechafaltante
Lasfechas
Nombre
04/09/2015
1
05/09/2015
1
05/09/2015
2
07/09/2015
2

En la base de datos del ejemplo para no tener consultas extras al más puro estilo de Julián, agregué las consulta1 y consulta2 dentro de la Ctafechafaltante y así lo seguiré haciendo en el resto del artículo.

Contar fechas:
Si tenemos una serie de fechas y requerimos saber cuántos días son de cada mes, por ejemplo tenemos la siguiente tabla:

Contarfechas
Id
Desde
Hasta
1
16/07/2015
26/10/2015
2
23/01/2015
17/05/2015

Ya sabemos que tenemos que crear nuestro rango de fechas, con la tabla números, tal como lo hemos hecho en los ejemplos anteriores y crear un campo que saque el mes y el año por si son varios años y el campo id, teniendo todo la convertimos a una de consulta de totales, el campo CVFecha([Desde])+[Num] le debemos indicar en totales: Dónde pues de lo contrario fallará, recordemos que "Dónde" sirve para indicar que ese campo no lo vas a usar para recoger, agrupar o calcular, sino únicamente para filtrar los registros que debe devolver la consulta.
Hecho esto agregamos un campo extra que llamaremos Días: CVFecha([Desde])+[num] y en totales: Cuenta y nuestra consulta esta lista:

SELECT Format(CVDate([Desde])+[num],"yyyy-mm") AS Periodo, Contarfechas.Id, Count(CVDate([Desde])+[num]) AS Días
FROM Nums, Contarfechas
WHERE (((CVDate([Desde])+[Num])<=CVDate([Hasta])))
GROUP BY Format(CVDate([Desde])+[num],"yyyy-mm"), Contarfechas.Id
ORDER BY Contarfechas.Id;


CtaContardias
Periodo
Id
Días
2015-07
1
16
2015-08
1
31
2015-09
1
30
2015-10
1
26
2015-01
2
9
2015-02
2
28
2015-03
2
31
2015-04
2
30
2015-05
2
17

Repetir registros
Una posibilidad muy interesante es cuando tenemos la necesidad de duplicar registros, por ejemplo imaginemos que tenemos una tabla:

RepetirRegistros
Id
Nombre
Cantidad
1
Emilio
5
2
Rolando
3
Y queremos repetir tantas veces los registros como el número en cantidad, agregamos las dos tablas y todos los campos de Repetir Registro y también el campo Num, como nuestra tabla números comienza en 0 tenemos que agregar +1 queda [Num]+1, y en criterio le ponemos <=[Cantidad] y obtenemos esto:
SELECT RepetirRegistros.Id, RepetirRegistros.Nombre, RepetirRegistros.Cantidad
FROM RepetirRegistros, Nums
WHERE ((([Num]+1)<=[Cantidad]))
ORDER BY RepetirRegistros.Id;


CtaRepetirRegistros
Id
Nombre
Cantidad
1
Emilio
5
1
Emilio
5
1
Emilio
5
1
Emilio
5
1
Emilio
5
2
Rolando
3
2
Rolando
3
2
Rolando
3

Generar segmentos de hora

Si tenemos que generar segmentos de horas de digamos 15 minutos de algunas horas en la mañana y otras en la tarde, la tabla de números puede hacernos el trabajo. Vamos a generarlo del día de hoy, para eso necesitamos una consulta con la tabla números y con un único campo del día de hoy y la tabla números: Fechahoy: CVFecha(Fecha())+[num] y en criterios <=CVFecha(Fecha()) para que solo tenga un registro.

SELECT CVDate(Date())+[num] AS fechahoy
FROM Nums
WHERE (((CVDate(Date())+[num])<=CVDate(Date())));


Consulta1
Fechahoy
23/09/2015

Con la ayuda de Chea, que me indico la forma, podemos simplificar, y solo hacer la consulta final, la podemos hacer de dos formas con CVFecha([Fechahoy]+([Num]*(1/24)*0.25)), donde 1/24 me da las horas y .25 cada 15 minutos o lo hacer con AgregFecha("n",([Num]*15),[Fechahoy]), donde “n” son minutos y queremos múltiplos de 15, de cualquier forma funciona y le agregamos la cláusula “In”  para que me de solo las horas que requiero, y queda así:

SELECT DISTINCT Consulta1.Fechahoy, CVDate([Fechahoy]+([Num]*(1/24)*0.25)) AS Expr1
FROM Nums, (SELECT Date()+[Num] AS Fechahoy FROM Nums WHERE (((Date()+[Num])<=CVDate(Date()))))  AS Consulta1
WHERE (((Nums.Num)<96) AND ((Hour(CVDate([Fechahoy]+([Num]*(1/24)*0.25)))) In (8,9,10,15,16,17)));

Y con la función de Agregar fecha:


SELECT DISTINCT Consulta1.Fechahoy, DateAdd("n",([Num]*15),[Fechahoy]) AS FechaMinutos, Hour(DateAdd("n",([Num]*15),[Fechahoy])) AS Expr1
FROM Nums, (SELECT Date()+[Num] AS Fechahoy FROM Nums WHERE (((Date()+[Num])<=CVDate(Date()))))  AS Consulta1
WHERE (((Hour(DateAdd("n",([Num]*15),[Fechahoy]))) In (8,9,10,15,16,17)) AND ((Nums.Num)<96));



Consulta3
FechaMinutos
Expr1
23/09/2015 08:00:00 a.m.
8
23/09/2015 08:15:00 a.m.
8
23/09/2015 08:30:00 a.m.
8
23/09/2015 08:45:00 a.m.
8
23/09/2015 09:00:00 a.m.
9
23/09/2015 09:15:00 a.m.
9
23/09/2015 09:30:00 a.m.
9
23/09/2015 09:45:00 a.m.
9
23/09/2015 10:00:00 a.m.
10
23/09/2015 10:15:00 a.m.
10
23/09/2015 10:30:00 a.m.
10
23/09/2015 10:45:00 a.m.
10
23/09/2015 03:00:00 p.m.
15
23/09/2015 03:15:00 p.m.
15
23/09/2015 03:30:00 p.m.
15
23/09/2015 03:45:00 p.m.
15
23/09/2015 04:00:00 p.m.
16
23/09/2015 04:15:00 p.m.
16
23/09/2015 04:30:00 p.m.
16
23/09/2015 04:45:00 p.m.
16
23/09/2015 05:00:00 p.m.
17
23/09/2015 05:15:00 p.m.
17
23/09/2015 05:30:00 p.m.
17
23/09/2015 05:45:00 p.m.
17


Separar una cadena
Una pequeña joya es lo que regalo raipon para separar cadenas, imaginemos que importamos una tabla y nos sale así:

Separar
id
Nombre
1
Emilio, José, Alberto, Pablo
2
María, Jacinta, Tamara
Y nosotros necesitamos un registro por cada nombre identificando el id, digamos que lo inverso a una consulta de unión. Con la tabla números y haciendo uso de las funciones de texto podemos conseguir lo que queremos.
Vamos a jugar un rato con las funciones de texto y después lo integramos para lograrlo
Si tenemos el texto “Emilio, José, Alberto, Pablo” y queremos separar Emilio, podríamos usar en la ventana inmediata:
?Left(“Emilio, José, Alberto, Pablo”,6) y el resultado es Emilio y si quiere obtener José tendría que usar Mid algo así: Mid(“Emilio, José, Alberto, Pablo”,8,4) el resultado es José, como puedo ir variando ese 8,4 por un 13,7 para el siguiente y por un 21,5 el restante, y mejor aún, que Access lo realice solo. Ocupamos usar InStr para encuentre la posición de cada coma.
Pero primero debemos ver cuantos registros hemos de repetir, ya sabemos que con el campo Num y poniendo en criterios la cantidad lo hacemos, para poner la cantidad podemos contar los caracteres con Len, y si agregamos un campo para que localice todos “/” y también los que comienzan con 0 para los dos primeros, tendríamos esto:

SELECT tbl_musica.Campo1, tbl_musica.Artista, Nums.Num
FROM Nums, tbl_musica
WHERE (((Nums.Num)<=Len([Artista])) AND ((Mid([Artista],IIf([Num]=0,1,[Num]),1))="/")) OR (((Nums.Num)=0 And (Nums.Num)<=Len([Artista])));

Consulta1
Campo1
Artista
Num
1
José José
0
2
Luis/Pepe/Alberto
0
2
Luis/Pepe/Alberto
5
2
Luis/Pepe/Alberto
10

Ya casi funciona solo resta agregar la función Mid para separar el nombre en cada “/” , la consulta completa queda:

SELECT Separar.id, Mid([Nombre],[Num]+1,Abs(InStr([Num]+1,[Nombre] & ",",",")-[Num]-1)) AS Expr1
FROM Nums, Separar
WHERE (((Mid([Nombre],IIf([Num]=0,1,[Num]),1))=",") AND ((Nums.Num)<=Len([Nombre]))) OR (((Nums.Num)=0 And (Nums.Num)<=Len([Nombre])))
ORDER BY Separar.id;

CtaSeparar
id
Expr1
1
Pablo
1
Alberto
1
José
1
Emilio
2
Tamara
2
Jacinta
2
María
Como observamos podemos construir consultas muy poderosas y en muy poco tiempo gracias a la tabla números, estos son solo unos pequeños ejemplos de lo que podemos conseguir con ella, aplicaciones muchas por ejemplo podemos crear en el año los días laborales incluidos los festivos, por poner otro ejemplo.

Calendario Mensual

Por último Emilio Sancha, de una idea de raipon nos enseña a hacer un calendario mensual, vamos a comenzar creando nuestra primer consulta que solo va a contener la tabla números, pero como nuestra tabla números tiene del 0 al 2000, nos dará error si intentamos hacerlo con tantos números, así que lo primero es reducir la cantidad, por ejemplo Emilio usa del 0 al 31 y nos da del año 2001 al 2029, así que primero hacemos nuestra consulta hasta el 31 y de esta la repetimos tres veces:

SELECT Nums.Num
FROM Nums
WHERE (((Nums.Num)<32));

Le vamos a cambia el nombre, ya que Access las renombra como Consulta4, Consulta4_1 y Consulta4_2 lo podemos  haces de dos formas en la vista diseño en la parte superior de cada tabla con el ratón derecho le damos un click y otro a propiedades y en alias le cambiamos el nombre.


La otra forma es en la SQL cambiarlo, le ponemos Dias, Meses y Años, y en sql queda:
SELECT
FROM Consulta4 AS Dias, Consulta4 AS Meses, Consulta4 AS Años;

Vamos a agregar los campos necesarios ahora:

SELECT DISTINCT DateSerial([Años].[Num],[Meses].[Num],[Dias].[Num]) AS Fecha, Month(DateSerial([Años].[Num],[Meses].[Num],[Dias].[Num])) AS Mes, Year(DateSerial([Años].[Num],[Meses].[Num],[Dias].[Num])) AS Año, Format$(DateSerial([Años].[Num],[Meses].[Num],[Dias].[Num]),"ww",2) AS Semana, Weekday(DateSerial([Años].[Num],[Meses].[Num],[Dias].[Num]),2) AS DiaSemana
FROM Consulta4 AS Dias, Consulta4 AS Meses, Consulta4 AS Años
WHERE (((Month(DateSerial([Años].[Num],[Meses].[Num],[Dias].[Num])))=[Mes]) AND ((Year(DateSerial([Años].[Num],[Meses].[Num],[Dias].[Num])))=[Año]) AND ((Meses.Num)>=12) AND ((Dias.Num)<=31))
ORDER BY DateSerial([Años].[Num],[Meses].[Num],[Dias].[Num]);
Ahora la hacemos de referencia cruzada, agregando los parámetros del mes y el año:

PARAMETERS Mes Byte, Año Short;
TRANSFORM First(Day([Fecha])) AS Dia
SELECT Consulta7.Semana
FROM Consulta7
GROUP BY Consulta7.Semana
PIVOT Consulta7.DiaSemana;

Y la hacemos toda junta:

PARAMETERS Mes Byte, Año Short;
TRANSFORM First(Day([Fecha])) AS Dia
SELECT Consulta7.Semana
FROM (SELECT DISTINCT DateSerial([Años].[Num],[Meses].[Num],[Dias].[Num]) AS Fecha, Month(DateSerial([Años].[Num],[Meses].[Num],[Dias].[Num])) AS Expr1, Year(DateSerial([Años].[Num],[Meses].[Num],[Dias].[Num])) AS Expr2, Format$(DateSerial([Años].[Num],[Meses].[Num],[Dias].[num]),"ww",2) AS Semana, Weekday(DateSerial([Años].[Num],[Meses].[Num],[Dias].[Num]),2) AS DiaSemana
FROM Consulta4 AS Años, Consulta4 AS Meses, Consulta4 AS Dias
WHERE (((Month(DateSerial([Años].[Num],[Meses].[Num],[Dias].[Num])))=[Mes]) AND ((Year(DateSerial([Años].[Num],[Meses].[Num],[Dias].[Num])))=[Año]) AND ((Meses.Num)>=12) AND ((Dias.Num)<=31))
ORDER BY DateSerial([Años].[Num],[Meses].[Num],[Dias].[Num])) As Consulta7
GROUP BY Consulta7.Semana
PIVOT Consulta7.DiaSemana;

Si no quieren tener limitante en cuanto a los años y quisieran poner cualquier fecha, Chea tiene un ejemplo similar al de Emilio, pero él utiliza además de la tabla números una variable TempVars y en el formulario o informe puede poner cualquier fecha, sin ningún limitante.

Gracias.

BajarArchivo