Boletín Pascal #8
INDICE
1. UNAS PALABRAS DEL EDITOR
2. KYLIX FAQ (PREGUNTAS FRECUENTES)
- ¿Cuándo estará listo?
- ¿En cuáles distribuciones de Linux funcionará?
- ¿KDE o GNOME?
- ¿Estará disponible para otras plataformas?
- ¿Cambiará el lenguaje?
- ¿Qué tipo de binarios producirá?
- ¿Podré enlazar archivos objeto compilados con gcc o gpp a mis
proyectos Delphi?
- ¿Qué es CLX?
3. PROGRAMACION DE BASES DE DATOS EN CODIGO (II)
- SET RELATION
- INDEX ON
- CONSULTAS
- SELECT
- SELECT
- FROM
- WHERE
- GROUP BY
- HAVING
- ORDER BY
- JOINs
- PARAMETROS
- EJEMPLOS
4. ENLACES
________________________________________________________________________
1. UNAS PALABRAS DEL EDITOR
Hay nuevos trucos en nuestra sección de Trucos Delphi: ¿Cómo ...
* Determinar la longitud real de una cadena?
* Buscar archivos recursivamente en el disco duro?
* Obtener el icono de una aplicación o documento?
* Establecer el color invisible de una imagen transparente?
* Imitar a Deltree.exe?
* Obtener los directorios importantes de Windows?
* Comparar una cadena con un patrón?
* Determinar si un nombre de archivo coincide con una especificación?
* Establecer el papel tapiz del escritorio?
* Usar cursores propios?
* Crear automáticamente un formulario bajo petición?
* Hacer que MessageDlg ejecute el sonido correspondiente?
* Ejecutar un sonido propio?
* Establecer la impresora predeterminada?
* Impedir que el usuario cierre un formulario?
* Crear un archivo temporal único?
* Saber si hay un disco/diskette/CD en una unidad de discos removibles?
* Obtener las fechas del primer y último día del mes de una fecha dada?
* Mostrar el Panel de Control o ejecutar una "applet" del Panel de
Control?
Aquí están las respuestas:
http://www.latiumsoftware.com/es/delphi/index.php
Además ponemos a disposición de los interesados el archivo de recursos
traducido al español del componente TZipMaster que presentáramos en el
Boletín Pascal #5.
http://www.latiumsoftware.com/download/zipmsges.zip
Para usarlo deben descomprimir el fichero ZipMsgES.res que se encuentra
dentro del Zip y ponerlo en el directorio donde pusieron los demás
archivos de la VCL que venían con el componente (por ejemplo en
"C:\Delphi\Zip\VCL" si siguieron el ejemplo). En los programas deben
cambiar el nombre del archivo de recursos (que antes era ZipMsgUS.res
para los mensajes en inglés y ahora será ZipMsgES.res).
Saludos,
Ernesto De Spirito
eds2008 @ latiumsoftware.com
________________________________________________________________________
JfControls Lib. Multilenguaje. Multiapariencia. Skins. Privilegios. Más
de 40 componentes integrados y personalizables. Múltiples problemas de
programación resueltos. Administración centralizada de recursos. Para
Delphi 3-2006 y C++ Builder 3-6. http://www.jfactivesoft.com/spindex.htm
________________________________________________________________________
2. KYLIX FAQ (PREGUNTAS FRECUENTES)
* ¿Cuándo estará listo?
Cuando Borland estaba por juntar fuerzas con Corel, el lanzamiento de
Kylix estaba planeado para mediados de año. Como ya saben, la fusión
se canceló y Borland continuó trabajando solo en Kylix. Ahora el
lanzamiento probablemente sucederá antes de fin de año (eso
esperamos). Mientras tanto, la versión beta ya ganó dos premios:
http://www.borland.com/kylix/
* ¿En cuáles distribuciones de Linux funcionará?
En todas.
* ¿KDE o GNOME?
Kylix funcionará con KDE, GNOME y simplemente fvwm. Sin embargo, la
primera versión está mejor preparada para KDE, pero correrá también en
GNOME, sólo que no hará uso completo de las características especí-
ficas de GNOME.
* ¿Estará disponible para otras plataformas?
No por ahora (solamente PCs Intel o compatible con Windows o Linux).
En el futuro puede que se expanda a otras plataformas de hardware y/o
sistemas operativos.
* ¿Cambiará el lenguaje?
Sólo mínimamente.
* ¿Qué tipo de binarios producirá?
Programas y objetos compartidos ELF estándar nativos e independientes.
* ¿Podré enlazar archivos objeto compilados con gcc o gpp a mis
proyectos Delphi?
Sí para los archivos objeto de gcc y no para los de g++ (tendrán que
recompilarse en C++ Builder para Linux cuando esté disponible).
* ¿Qué es CLX?
CLX (que se pronuncia "kliks") viene de "biblioteca de componentes
para plataformas intercambiables" y es una nueva estructura fácilmente
extensible basada en componentes para objetos gráficos, componentes de
base de datos, componentes de red, etc. Suena bastante parecido a la
VCL, ¿verdad? Ciertamente que sí, excepto que CLX será independiente
de la plataforma, de modo que las aplicaciones que usen CLX serán más
portables.
________________________________________________________________________
3. PROGRAMACION DE BASES DE DATOS EN CODIGO (II)
SET RELATION
============
En xBase:
SELECT 0
USE customer ALIAS Clientes INDEX CustNo
SELECT 0
USE orders ALIAS Ordenes
SET RELATION TO CustNo INTO Clientes
En Delphi es un poco más complicado:
var
Ordenes, Clientes: TTable;
Maestra: TDataSource;
begin
// Abrir las tablas
CreateAndOpen(Ordenes, 'DBDEMOS', 'orders.db');
CreateAndOpen(Clientes, 'DBDEMOS', 'customer.db');
// Crear un DataSource para la tabla maestra
Maestra := TDataSource.Create(nil);
Maestra.DataSet := Ordenes;
// Establecer la relación maestro-detalle
Clientes.MasterSource := Maestra;
Clientes.MasterFields := 'CustNo';
Para crear una relación maestro-detalle, primero necesitamos un
DataSource (origen de datos) de la tabla que será la "tabla maestra"
(Ordenes en el ejemplo de arriba) y luego establecemos las propiedades
MasterSource y MasterFields de la "tabla detalle" (Clientes en el
ejemplo de arriba). MasterSource es el DataSource de la tabla maestra,
mientras que MasterFields es una cadena listando los nombres de los
campos (separados por punto y coma) en la tabla maestra sobre los cuales
se hará la relación. El índice activo de la tabla detalle debe comenzar
al menos con los campos correspondientes en la tabla detalle (aunque
puede tener más campos). En el ejemplo, la clave primaria de Clientes
está basada en 'CustNo' y como es la clave primaria, es el índice activo
de modo predeterminado.
En una relación uno-a-muchos, normalmente la tabla del lado del "uno" es
la tabla maestra y la del lado del "muchos" es la tabla detalle, pero se
puede elegir libremente. En el ejemplo de arriba hemos considerado la
"tabla muchos" (Ordenes) como la tabla maestra. Esto significa que cada
vez que nos movamos en la tabla Ordenes, el puntero de registro de la
tabla Clientes se reposicionará en el primer (y único) registro
correspondiente que concuerde con el criterio
Clientes.CustNo = Orders.CustNo
En xBase uno puede moverse "libremente" por todos los registros de la
tabla detalle si quiere, pero en Delphi la tabla detalle está afectada
por un rango de claves, de modo que sólo aquellos registros que cumplen
el criterio son accesibles.
Aquí va un ejemplo más completo:
uses Db, DBTables;
procedure CreateAndOpen(var tabla: TTable;
DatabaseName, TableName: string);
begin
if tabla <> nil then FreeAndNil(tabla);
tabla := TTable.Create(nil);
tabla.DatabaseName := DatabaseName;
tabla.TableName := TableName;
tabla.Open;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
Ordenes, Clientes: TTable;
Maestra: TDataSource;
Total: Currency;
begin
Ordenes := nil;
Clientes := nil;
Maestra := nil;
try
// Abrir las tablas
CreateAndOpen(Ordenes, 'DBDEMOS', 'orders.db');
CreateAndOpen(Clientes, 'DBDEMOS', 'customer.db');
// Crear un DataSource para la tabla maestra
Maestra := TDataSource.Create(nil);
Maestra.DataSet := Ordenes;
// Establecer la relación maestro-detalle
Clientes.MasterSource := Maestra;
Clientes.MasterFields := 'CustNo';
ShowMessage(Format('%d=%d --> %s', [
Ordenes.FieldByName('CustNo').AsInteger,
Clientes.FieldByName('CustNo').AsInteger,
Clientes.FieldByName('Company').AsString ]));
Ordenes.Next;
ShowMessage(Format('%d=%d --> %s', [
Ordenes.FieldByName('CustNo').AsInteger,
Clientes.FieldByName('CustNo').AsInteger,
Clientes.FieldByName('Company').AsString ]));
Clientes.Next;
if Clientes.Eof then ShowMessage('EOF');
// Cancelar la relación
Clientes.MasterSource := nil;
// Establecer la relación inversa
Maestra.DataSet := Clientes;
Ordenes.IndexName := 'CustNo';
Ordenes.MasterSource := Maestra;
Ordenes.MasterFields := 'CustNo';
Clientes.First; // 'Kauai Dive Shoppe'
// Totalizar el monto pagado por este cliente
Total := 0;
while not Ordenes.Eof do begin
Total := Total + Ordenes.FieldByName('AmountPaid').AsCurrency;
Ordenes.Next;
end;
ShowMessage(Format('%s %m', [
Clientes.FieldByName('Company').AsString,
Total ])); // Debería ser 'Kauai Dive Shoppe $ 51.450,80'
except
Ordenes.Free;
Clientes.Free;
Maestra.Free;
raise;
end;
Ordenes.Free;
Clientes.Free;
Maestra.Free;
end;
INDEX ON
========
En la edición pasada mostramos cómo agregar un índice a una tabla nueva.
Aquí mostraremos cómo agregar un índice a una tabla existente, y también
como removerlo:
procedure TForm1.Button2Click(Sender: TObject);
var
Clientes: TTable;
begin
Clientes := nil;
try
// Abrir las tablas
CreateAndOpen(Clientes, 'DBDEMOS', 'customer.db');
with Clientes do begin
AddIndex('Ubicacion', 'Country;State',
[ixCaseInsensitive, ixNonMaintained]);
IndexName := 'Ubicacion';
First;
ShowMessage(FieldByName('Company').AsString); // Unisco
IndexName := '';
Close;
Exclusive := True;
Open;
DeleteIndex('Ubicacion');
end;
except
Clientes.Free;
raise;
end;
Clientes.Free;
end;
Un índice se agrega con el método AddIndex. En xBase, después de crear
un índice, éste se convierte en el índice activo, pero aquí debemos
hacerlo explícitamente si eso es lo que se quiere.
La opción ixNonMaintained se usa normalmente para índices temporarios y
significa que este índice no será actualizado cuando agregue, borre y
modifique registros a menos que sea el índice activo. Estos índices se
reindexan automáticamente cada vez que se convierten en el índice
activo si es que están desactualizados.
Para remover un índice usamos el método DeleteIndex. La tabla tiene que
estar abierta con acceso exclusivo para que funcione. No se puede
remover una clave primaria.
CONSULTAS
=========
Hay dos tipos de consultas:
* Consultas de selección o de resultados: Se usan para obtener ciertas
filas (registros) y columnas (campos) de una o más tablas. Puede
trabajar con el resultado de una consulta de modo similar al que
trabaja con una tabla.
* Consultas de acción: Se usan para realizar una acción sobre una o
más tablas de una base de datos, como actualizar registros, borrar
registros, etc.
En esta edición vamos a tratar las consultas de selección y dejaremos
las consultas de acción para el próximo número.
Comencemos con un ejemplo. Supongamos que queremos ver el número de
cliente, nombre del cliente y monto total abonado por nuestros clientes
de los EE.UU. con montos totales superiores a los $ 100.000, y que
queremos el listado ordenado alfabéticamente (por nombre de compañía)...
procedure TForm1.btnQueryClick(Sender: TObject);
var
Consulta: TQuery;
begin
Consulta := nil;
try
Consulta := TQuery.Create(nil);
with Consulta do begin
DatabaseName := 'DBDEMOS';
with SQL do begin
Add('SELECT Orders.CustNo, Company,');
Add(' SUM(AmountPaid) As TotalAmount');
Add('FROM Orders, Customer');
Add('WHERE Orders.CustNo = Customer.CustNo');
Add(' AND Country = "US"');
Add('GROUP BY Orders.CustNo, Company');
Add('HAVING SUM(AmountPaid) > 100000');
Add('ORDER BY Company');
end;
Open;
while not Eof do begin
ShowMessage(Format('%d %s %m', [
FieldByName('CustNo').AsInteger,
FieldByName('Company').AsString,
FieldByName('TotalAmount').AsCurrency ]));
Next;
end;
end;
except
Consulta.Free;
raise;
end;
Consulta.Free;
end;
Como puede ver, una consulta de resultados es como una tabla, siendo la
principal diferencia que en lugar de tener una propiedad TableName tiene
una propiedad SQL que es una lista de cadenas donde puede escribir una
sentencia SQL (más sobre esto después). En el ejemplo, el resultado de
esta sentencia SQL es como una tabla con tres campos y cuatro registros:
CustNo Company TotalAmount
3053 American SCUBA Supply $ 183.094,40
1563 Blue Sports $ 165.245,45
3042 Gold Coast Supply $ 132.233,00
1560 The Depth Charge $ 126.889,35
SELECT
======
Toda la magia la hace la sentencia SQL SELECT. Si no le es familiar,
aquí le daremos una introducción rápida para iniciarlo. Puede encontrar
más información en el archivo de ayuda Local SQL Help que viene con la
BDE.
SELECT
------
La sentencia comienza con la palabra reservada SELECT y a continuación
debemos especificar la lista de columnas (campos) que queremos de la/s
tabla/s base. En nuestro ejemplo queríamos tres columnas:
Orders.CustNo, Company, SUM(AmountPaid) AS TotalAmount
Hay un campo llamado CustNo en ambas tablas que queremos acceder, así
que tuvimos que calificar el nombre especificando de cuál tabla lo
queremos tomar.
Las columnas pueden ser campos o expresiones, incluyendo expresiones
"agregadas" -como SUM- que funcionan con un grupo de registros (ver
GROUP BY). La palabra "AS" sirve para especificar el nombre de una
columna en el cursor resultado.
Si quisiéramos todos los campos de una tabla podemos usar una sintaxis
como la siguiente:
Orders.*
FROM
----
Especifica la lista de tablas de las cuales se tomarán los datos.
No está restringido a las tablas de una sola base de datos ni a tablas
del mismo tipo. Por ejemplo:
SELECT ...
FROM "C:\MyDB\Tabla1.dbf" T1, ':DBDEMOS:Tabla2.db' T2
Nota: SQL admite comillas simples o dobles.
T1 y T2 son alias locales para referirse a estas tablas. Por ejemplo
si ambas tablas tuvieran un campo llamado Codigo, puede referirse al
de la primera tabla calificándolo con el alias de la tabla: T1.Codigo.
WHERE
-----
Especifica la condición para combinar y/o filtrar los registros de las
tablas.
Si no especifica una cláusula WHERE, el resultado de combinar dos o más
tablas es el producto cartesiano de las mismas, es decir, todos los
registros de una tabla se combinan con todos los registros de las
otras. Por ejemplo, si T1 tuviera 3 registros y T2 tuviera 2 registros,
entonces el resultado tendría 6 registros:
T1 T2
--------------------- ---------------------------
C11 C12 C13 C14 C21 C22 C23 C24 C25
--------------------- ---------------------------
5 ... ... ... 5 ... ... ... ...
7 ... ... ... 8 ... ... ... ...
8 ... ... ...
T1 x T2
---------------------------------------------------
C11 C12 C13 C14 C21 C22 C23 C24 C25
---------------------------------------------------
5 ... ... ... 5 ... ... ... ...
5 ... ... ... 8 ... ... ... ...
7 ... ... ... 5 ... ... ... ...
7 ... ... ... 8 ... ... ... ...
8 ... ... ... 5 ... ... ... ...
8 ... ... ... 8 ... ... ... ...
Usualmente esto no es lo que queremos, sino que sólo queremos los
registros cuyos valores de clave coinciden, así que la primer condición
de una cláusula WHERE generalmente es una "condición de combinación"
como por ejemplo C11 = C21, que debería dar el siguiente resultado:
T1 x T2 WHERE C11 = C21
---------------------------------------------------
C11 C12 C13 C14 C21 C22 C23 C24 C25
---------------------------------------------------
5 ... ... ... 5 ... ... ... ...
8 ... ... ... 8 ... ... ... ...
En el ejemplo de clientes y órdenes teníamos tanto una "condición de
combinación" como una "condición de filtro":
Orders.CustNo = Customer.CustNo AND Country = "US"
GROUP BY
--------
Especifica la lista de campos sobre los cuáles se agruparán los
registros. El agrupamiento consiste en hacer que un grupo de registros
se hagan uno.
En nuestro ejemplo de clientes y órdenes, sin el agrupamiento tendríamos
muchos registros por cada cliente. Por ejemplo el cliente 3053 tendría
tres registros:
CustNo Company AmountPaid
3053 American SCUBA Supply $ 10.263,75
3053 American SCUBA Supply $ 158.922,65
3053 American SCUBA Supply $ 13.908,00
Cuando en realidad lo que queremos es un solo registro resumiendo los
tres:
CustNo Company TotalAmount
3053 American SCUBA Supply $ 183,094.40
¿Cómo realizamos el agrupamiento? Con la cláusula GROUP BY listando
todos los campos no agregados (SUM(AmountPaid) es un campo agregado)
que están en la cláusula SELECT, por ejemplo:
GROUP BY Orders.CustNo, Company
Si usa una función agregada (AVG, COUNT, MAX, MIN o SUM) en la cláusula
SELECT, entonces se requiere que GROUP BY esté presente (otros motores
de bases de datos asumen que todos los registros deben agruparse si usó
una función agregada y no especificó GROUP BY).
HAVING
------
Especifica una condición del filtro a aplicarse después del agrupamiento
y se usa para filtrar registros basándose en los valores de campos
agregados (valores que obviamente no están disponibles antes del
agrupamiento). Por ejemplo:
HAVING SUM(AmountPaid) > 100000
Esta cláusula es opcional.
ORDER BY
--------
Especifica la lista de campos sobre los cuales se ordenarán los
registros. Por ejemplo:
ORDER BY TotalAmount DESC, Company;
Esto ordenaría los registros en forma descendente por monto total (el
más grande primero) y si dos registros tienen el mismo valor para este
campo, serán ordenados en forma ascendente por sus nombres.
Esta cláusula es opcional.
JOINs
-----
La condición de combinación se puede especificar en la cláusula WHERE,
pero también se puede hacer en la cláusula FROM. Por ejemplo, estas dos
sentencias SQL son equivalentes:
SELECT Orders.CustNo, Company;
FROM Orders, Customer
WHERE Orders.CustNo = Customer.CustNo
SELECT Orders.CustNo, Company;
FROM Orders INNER JOIN Customer ON Orders.CustNo = Customer.CustNo
La palabra "INNER" es opcional. La primera se llama habitualmente
equi-combinación, mientras que a la segunda forma se la llama
combinación interna, pero a pesar de los diferentes nombres producen el
mismo resultado.
El INNER JOIN descarta los registros en una tabla que no tienen clave
correspondiente en la otra. Puede ver esto en el ejemplo que presen-
tamos con la cláusula WHERE: El registro con la clave 7 en T1 fue
descartado del resultado porque no había 7 en T2. A veces sí queremos
estos registros "huérfanos" y esa es la razón por la que hay un OUTER
JOIN.
Hay tres clases de OUTER JOIN:
- LEFT JOIN: Es como INNER JOIN pero también incluye registros en la
primera tabla que no tienen pareja en la segunda tabla.
- RIGHT JOIN: Es como INNER JOIN pero también incluye registros en la
segunda tabla que no tienen pareja en la primera tabla.
- FULL JOIN: Una combinación de LEFT JOIN y RIGHT JOIN, es decir, es
como INNER JOIN pero incluye registros de ambas tablas que no tienen
su correspondiente en la otra.
Cuando no se encuentra coincidencia en una tabla, sus campos valen NULL.
En el ejemplo de T1 y T2, la siguiente sentencia
SELECT T1.*, T2.*
FROM T1 LEFT OUTER JOIN T2 ON C11 = C21
arrojaría el siguiente resultado:
---------------------------------------------------
C11 C12 C13 C14 C21 C22 C23 C24 C25
---------------------------------------------------
5 ... ... ... 5 ... ... ... ...
7 ... ... ... NULL NULL NULL NULL NULL
8 ... ... ... 8 ... ... ... ...
Debemos advertirles que hemos probado unas pocas consultas usando OUTER
JOIN y no parece que funcione (actúa como INNER JOIN).
Se pueden combinar dos tablas por más de un campo. Por ejemplo:
SELECT t1.*, t2.*
FROM t1 JOIN t2 ON t1.c1 = t2.c1 AND t1.c2 = t2.c2
También se pueden combinar dos tablas sobre la coincidencia de un campo
y una concatenación de campos:
SELECT t1.*, t2.*
FROM t1 JOIN t2 ON t1.campo1 = t2.campo1 || t2.campo2
Se puede combinar más de dos tablas. Por ejemplo:
SELECT t1.*, t2.*, t3.*
FROM (t1 JOIN t2 ON t1.campo1 = t2.campo1)
JOIN t3 ON t3.campo1 = t1.campo2 AND t3.campo2 = t1.campo2
SELECT t1.*, t2.*, t3.*
FROM ((t1 JOIN t2 ON t1.campo1 = t2.campo1)
JOIN t3 ON t3.campo1 = t1.campo2 AND t3.campo2 = t1.campo2)
JOIN t4 ON t4.campo1 = t1.campo3
PARAMETROS
----------
En la primer consulta de ejemplo obtuvimos los montos de los clientes de
los EE.UU.,
Add('WHERE Orders.CustNo = Customer.CustNo AND Country = "US"');
pero qué pasaría si quisiéramos ver aquellos de nuestros clientes
Canadienses? Podemos hacer dos cosas: concatenar cadenas o declarar un
parámetro precediendo su nombre por dos puntos (':') como puede ver
abajo:
Add('WHERE Orders.CustNo = Customer.CustNo AND Country = :Country');
Antes de abrir la consulta debemos proveer el valor para el parámetro.
Por ejemplo:
Query1.Params[0].AsString := Edit1.Text;
o
Query1.ParamByName('Country').AsString := 'Canada';
Esto permite escribir la consulta SQL una vez y luego solamente cambiar
el parámetro como se necesite.
Debido a que usamos AsString, el valor del parámetro será entrecomillado
en la sentencia SQL.
EJEMPLOS
--------
1) Obtener las partes en orden decreciente de margen de ganancias neto
unitario.
SELECT parts.*, (ListPrice - Cost) AS profit
FROM "parts.db" parts ORDER BY profit DESC
2) Obtener las partes que necesitan reposición inmediata para cumplir
las órdenes. Por cada parte listar su número, descripción cantidad
necesaria, costo unitaro, monto total, nombre del proveedor y número
de teléfono. Ordenar los resultados por nombre de vendedor y número
de parte.
SELECT PartNo, Description, (OnOrder - OnHand) As Quantity,
Cost, (Quantity * Cost) As Total, VendorName, Phone
FROM "parts.db" parts JOIN "vendors.db" vendors
ON parts.VendorNo = vendors.VendorNo
WHERE OnOrder > OnHand ORDER BY VendorName, PartNo
3) Obtener la parte más vendida.
SELECT PartNo, Description, SUM(Qty) As Quantity
FROM "parts.db" parts JOIN "items.db" items
ON parts.PartNo = items.PartNo
GROUP BY PartNo, Description ORDER BY Quantity DESC
El primer registro de esta consulta corresponde a la parte más
vendida. Con otros motores de bases de datos (como el JET) se
puede usar SELECT TOP 1 ... para obtener sólo el primer registro.
4) Listar todos los empleados y sus ventas totales en 1994 y 1995
SELECT employee.EmpNo, LastName, FirstName,
SUM(ItemsTotal) AS Sales
FROM employee LEFT JOIN orders
ON employee.EmpNo = orders.EmpNo
WHERE SaleDate BETWEEN "01/01/1994" AND "12/31/1995"
GROUP BY employee.EmpNo, LastName, FirstName
La cláusula WHERE también pudo haber sido por ejemplo:
* WHERE SaleDate >= "01/01/1994" AND SaleDate <= "12/31/1995"
* WHERE EXTRACT(YEAR FROM SaleDate) BETWEEN 1994 AND 1995
* WHERE EXTRACT(YEAR FROM SaleDate) IN (1994, 1995)
Las fechas siempre se expresan en formato "MM/DD/AAAA"
5) Listar todos los empleados que han tratado con clientes en Fiji
SELECT employee.EmpNo, LastName, FirstName
FROM (employee JOIN orders
ON employee.EmpNo = orders.EmpNo)
JOIN customer ON orders.CustNo = customer.CustNo
WHERE customer.Country = "Fiji"
6) Por cada empleado, totalizar las ventas a cada uno de sus clientes.
SELECT employee.EmpNo, LastName, FirstName, Company,
SUM(ItemsTotal) AS Sales
FROM (employee JOIN orders
ON employee.EmpNo = orders.EmpNo)
JOIN customer ON orders.CustNo = customer.CustNo
GROUP BY LastName, FirstName, employee.EmpNo, Company
7) Obtener todas las órdenes de un cliente cuyo número debe ser provisto
por el usuario:
SELECT * FROM orders WHERE CustNo = :Custno
El código fuente completo de los ejemplos de este boletín está
disponible aquí:
http://www.latiumsoftware.com/es/file.php?id=p08
________________________________________________________________________
4. ENLACES
EN INGLES
=========
* Top 100 Delphi web sites
http://www.sandbrooksoftware.com/TS/index.shtml
* All the best from James M Sandbrook - Delphi Programming Source Code.
Free components, downloads, articles, examples etc.
http://www.sandbrooksoftware.com/DPSC/index.shtml
* DelphiLand - Online tutorials for the novice Delphi programmer. All
lessons and source code can also be downloaded.
http://www.festra.com/eng/index.html
EN ESPAÑOL
==========
* P.O.D. - Programación Orientada en Delphi
Página personal de Alirio Gavidia con tips, enlaces, etc. Lo más
interesante es TExpression, una unidad para evaluar expresiones dadas
como cadenas en tiempo de ejecución.
http://www.gavidia.org/pod
________________________________________________________________________
Si no has recibido el archivo con el código fuente completo de los
ejemplos que se presentan en este boletín, puedes descargarlo de la
siguiente dirección: http://www.latiumsoftware.com/es/file.php?id=p08
________________________________________________________________________
Página principal: http://www.latiumsoftware.com/es/pascal/index.php
Página del grupo: http://espanol.groups.yahoo.com/group/boletin-pascal/
Para suscribirse / apuntarse: boletin-pascal-subscribe@gruposyahoo.com
Para cancelar / removerse: boletin-pascal-unsubscribe@gruposyahoo.com
Para reportar problemas con la suscripción: eds2008 @ latiumsoftware.com
________________________________________________________________________
Este boletín se provee "TAL Y COMO ESTA", sin garantía de ninguna clase.
Su uso implica la aceptación de nuestros términos de licencia y de la
ausencia de garantía que puedes leer en nuestro sitio web. Allí también
encontrarás una nota sobre marcas registradas. Te animamos a que redis-
tribuyas este boletín, siempre y cuando lo hagas en forma completa
(incluyendo la información de copyright), sin modificaciones y de manera
gratuita. Los artículos son copyright de sus respectivos autores y se
reproducen aquí con el permiso de los mismos.
________________________________________________________________________
Latium Software http://www.latiumsoftware.com/es/index.php
Copyright (c) 2000 por Ernesto De Spirito. Todos los derechos reservados
________________________________________________________________________
|