Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Al trabajar con una base de datos, es habitual solicitar datos que se distribuyen entre varias tablas. Para recuperar datos de dos tablas diferentes, podemos usar una subconsulta correlacionada o una operación JOIN. En este tutorial se comparan las subconsultas correlacionadas y la sintaxis JOIN antes de examinar cómo crear un TableAdapter que incluya una JOIN en su consulta principal.
Introducción
Con las bases de datos relacionales, los datos con los que estamos interesados en trabajar a menudo se distribuyen entre varias tablas. Por ejemplo, al mostrar información del producto, es probable que deseemos enumerar los nombres correspondientes de categoría y proveedor de cada producto. La Products
tabla tiene CategoryID
valores, y SupplierID
, pero los nombres reales de categoría y proveedor se encuentran en la tabla Categories
y la Suppliers
, respectivamente.
Para recuperar información de otra tabla relacionada, podemos usar subconsultas correlacionadas o JOIN
s. Una subconsulta correlacionada es una consulta SELECT
anidada que hace referencia a las columnas de la consulta externa. Por ejemplo, en el tutorial Creación de una capa de acceso a datos usamos dos subconsultas correlacionadas en la ProductsTableAdapter
consulta principal para devolver los nombres de categoría y proveedor de cada producto. Es JOIN
una construcción SQL que combina filas relacionadas de dos tablas diferentes. Usamos un elemento JOIN
en el tutorial Consulta de datos con el control SqlDataSource para mostrar información de categorías junto con cada producto.
La razón por la que nos hemos abstenido de usar JOIN
s con los TableAdapters se debe a las limitaciones del asistente de TableAdapter para generar automáticamente las instrucciones correspondientes INSERT
, UPDATE
y DELETE
. Más concretamente, si la consulta principal de TableAdapter contiene algún JOIN
s, TableAdapter no puede crear automáticamente las instrucciones SQL ad hoc ni los procedimientos almacenados para sus InsertCommand
, UpdateCommand
, y DeleteCommand
propiedades.
En este tutorial, compararemos brevemente y contrastamos las subconsultas correlacionadas y JOIN
antes de explorar cómo crear un TableAdapter que incluya JOIN
s en su consulta principal.
Comparación y contraste de subconsultas correlacionadas yJOIN
s
Recuerde que el ProductsTableAdapter
elemento creado en el primer tutorial de Northwind
DataSet usa subconsultas correlacionadas para devolver la categoría y el nombre de proveedor correspondientes de cada producto. A continuación se muestra la ProductsTableAdapter
consulta principal.
SELECT ProductID, ProductName, SupplierID, CategoryID,
QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder,
ReorderLevel, Discontinued,
(SELECT CategoryName FROM Categories WHERE Categories.CategoryID =
Products.CategoryID) as CategoryName,
(SELECT CompanyName FROM Suppliers WHERE Suppliers.SupplierID =
Products.SupplierID) as SupplierName
FROM Products
Las dos subconsultas correlacionadas, (SELECT CategoryName FROM Categories WHERE Categories.CategoryID = Products.CategoryID)
y (SELECT CompanyName FROM Suppliers WHERE Suppliers.SupplierID = Products.SupplierID)
, son consultas que devuelven un único valor por producto como una columna adicional en la lista de columnas de la instrucción SELECT
externa.
Como alternativa, se puede usar un JOIN
para devolver el proveedor y el nombre de categoría de cada producto. La consulta siguiente devuelve la misma salida que la anterior, pero usa JOIN
s en lugar de subconsultas:
SELECT ProductID, ProductName, Products.SupplierID, Products.CategoryID,
QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder,
ReorderLevel, Discontinued,
Categories.CategoryName,
Suppliers.CompanyName as SupplierName
FROM Products
LEFT JOIN Categories ON
Categories.CategoryID = Products.CategoryID
LEFT JOIN Suppliers ON
Suppliers.SupplierID = Products.SupplierID
Combina JOIN
los registros de una tabla con registros de otra tabla en función de algunos criterios. En la consulta anterior, por ejemplo, indica LEFT JOIN Categories ON Categories.CategoryID = Products.CategoryID
a SQL Server que combine cada registro de producto con el registro de categoría cuyo CategoryID
valor coincida con el valor del CategoryID
producto. El resultado combinado nos permite trabajar con los campos de categoría correspondientes para cada producto (como CategoryName
).
Nota:
JOIN
Se usan normalmente al consultar datos de bases de datos relacionales. Si no está familiarizado con la JOIN
sintaxis o necesita refrescar un poco su uso, recomiendo el tutorial de uniones en SQL en W3 Schools. También merece la pena leer las JOIN
secciones Fundamentos y Fundamentos de la subconsulta de los Libros en línea de SQL.
Dado que las subconsultas y las subconsultas correlacionadas se pueden usar para recuperar datos relacionados de otras tablas, muchos desarrolladores se quedan confundidos y se preguntan qué enfoque utilizar. Todos los gurús de SQL que he hablado han dicho aproximadamente lo mismo, que realmente no importa el rendimiento, ya que SQL Server producirá planes de ejecución aproximadamente idénticos. Su consejo, entonces, es usar la técnica con la que usted y su equipo están más cómodos. Merece la pena señalar que, tras impartir este consejo, estos expertos expresan inmediatamente su preferencia por JOIN
frente a las subconsultas correlacionadas.
Al crear una capa de acceso a datos utilizando DataSets tipados, las herramientas funcionan mejor cuando se emplean subconsultas. En concreto, el asistente de TableAdapter no generará automáticamente las instrucciones correspondientes INSERT
, UPDATE
, y DELETE
si la consulta principal contiene algún JOIN
, pero generará automáticamente estas instrucciones cuando se usen subconsultas correlacionadas.
Para explorar esta deficiencia, cree un DataSet tipado temporal en la carpeta ~/App_Code/DAL
. Durante el Asistente para configuración de TableAdapter, elija usar instrucciones SQL ad hoc y escriba la consulta siguiente SELECT
(vea la figura 1):
SELECT ProductID, ProductName, Products.SupplierID, Products.CategoryID,
QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder,
ReorderLevel, Discontinued,
Categories.CategoryName,
Suppliers.CompanyName as SupplierName
FROM Products
LEFT JOIN Categories ON
Categories.CategoryID = Products.CategoryID
LEFT JOIN Suppliers ON
Suppliers.SupplierID = Products.SupplierID
Figura 1: Escriba una consulta principal que contenga JOIN
s (haga clic para ver la imagen de tamaño completo)
De forma predeterminada, TableAdapter creará automáticamente las instrucciones INSERT
, UPDATE
y DELETE
basadas en la consulta principal. Si hace clic en el botón Avanzadas, puede ver que esta característica está habilitada. A pesar de esta configuración, TableAdapter no podrá crear las declaraciones INSERT
, UPDATE
y DELETE
porque la consulta principal contiene un JOIN
.
Figura 2: Escribir una consulta principal que contiene JOIN
s
Haga clic en Finalizar para completar el asistente. En este momento, el Diseñador de DataSets incluirá un solo TableAdapter con una DataTable que tiene columnas para cada uno de los campos devueltos en la lista de columnas de la SELECT
consulta. Esto incluye el CategoryName
y SupplierName
, como muestra la figura 3.
Figura 3: DataTable incluye una columna para cada campo devuelto en la lista de columnas
Aunque DataTable tiene las columnas adecuadas, TableAdapter carece de valores para sus propiedades InsertCommand
, UpdateCommand
y DeleteCommand
. Para confirmarlo, haga clic en TableAdapter en el Diseñador y, a continuación, vaya a la ventana Propiedades. Allí verá que las InsertCommand
, UpdateCommand
, y DeleteCommand
propiedades se establecen en (Ninguno).
Figura 4: Las InsertCommand
propiedades , UpdateCommand
y DeleteCommand
se establecen en (Ninguno) (Haga clic para ver la imagen de tamaño completo)
Para solucionar este problema, podemos proporcionar manualmente las instrucciones SQL y los parámetros de las InsertCommand
propiedades , UpdateCommand
y DeleteCommand
a través de la ventana Propiedades. Como alternativa, podríamos empezar configurando la consulta principal de TableAdapter para que no incluya ninguna JOIN
. Esto permitirá que las INSERT
, UPDATE
y DELETE
declaraciones se generen automáticamente para nosotros. Después de completar el asistente, podríamos actualizar manualmente tableAdapter s SelectCommand
desde la ventana Propiedades para que incluya la JOIN
sintaxis.
Aunque este enfoque funciona, es muy frágil cuando se utilizan consultas SQL ad hoc porque cada vez que la consulta principal del TableAdapter se vuelve a configurar mediante el asistente, las instrucciones generadas automáticamente INSERT
, UPDATE
y DELETE
se recrean. Esto significa que todas las personalizaciones realizadas más adelante se perderán si hacemos clic con el botón derecho en el TableAdapter, elegimos Configurar en el menú contextual y completemos de nuevo el asistente.
La frágilidad de las instrucciones generadas automáticamente por TableAdapter INSERT
, UPDATE
y DELETE
es, afortunadamente, limitada a instrucciones SQL ad hoc. Si TableAdapter usa procedimientos almacenados, puede personalizar los SelectCommand
procedimientos almacenados , InsertCommand
, UpdateCommand
o DeleteCommand
y volver a ejecutar el Asistente para configuración de TableAdapter sin tener que temer que se modifiquen los procedimientos almacenados.
En los siguientes pasos crearemos un TableAdapter que, inicialmente, usa una consulta principal que omite cualquier JOIN
s para que se generen automáticamente los procedimientos almacenados de inserción, actualización y eliminación correspondientes. A continuación, actualizaremos el SelectCommand
para que utilice un JOIN
que devuelva columnas adicionales de tablas relacionadas. Por último, crearemos una clase de capa lógica de negocios correspondiente y mostraremos el uso de TableAdapter en una página web de ASP.NET.
Paso 1: Crear el TableAdapter mediante una consulta principal simplificada
En este tutorial, agregaremos un objeto TableAdapter y un DataTable fuertemente tipado para la tabla Employees
en el conjunto de datos NorthwindWithSprocs
. La Employees
tabla contiene un ReportsTo
campo que especifica el EmployeeID
del gerente del empleado. Por ejemplo, el empleado Anne Dodsworth tiene un ReportTo
valor de 5, que es el EmployeeID
de Steven Buchanan. Por lo tanto, Anne informa a Steven, su gerente. Junto con los informes sobre el valor de cada empleado ReportsTo
, también podríamos querer recuperar el nombre de su gerente. Esto se puede lograr mediante JOIN
. Pero el uso de un componente JOIN
al crear inicialmente el TableAdapter impide que el asistente de configuración genere automáticamente las capacidades de inserción, actualización y eliminación correspondientes. Por lo tanto, empezaremos creando un TableAdapter cuya consulta principal no contiene ningún JOIN
s. A continuación, en el paso 2, actualizaremos el procedimiento almacenado de consulta principal para recuperar el nombre del administrador a través de .JOIN
Para empezar, abra el conjunto de datos NorthwindWithSprocs
en la carpeta ~/App_Code/DAL
. Haga clic con el botón derecho en el Diseñador, seleccione la opción Agregar en el menú contextual y seleccione el elemento de menú TableAdapter. Se iniciará el Asistente para configuración de TableAdapter. Como se muestra en la figura 5, haga que el asistente cree nuevos procedimientos almacenados y haga clic en Siguiente. Para obtener un repaso sobre cómo crear nuevos procedimientos almacenados desde el asistente de TableAdapter, consulte el tutorial Creación de nuevos procedimientos almacenados para los TableAdapters de DataSet tipado.
Figura 5: Seleccionar la opción Crear nuevos procedimientos almacenados (Haga clic para ver la imagen de tamaño completo)
Use la siguiente SELECT
instrucción para la consulta principal de TableAdapter:
SELECT EmployeeID, LastName, FirstName, Title, HireDate, ReportsTo, Country
FROM Employees
Dado que esta consulta no incluye ningún JOIN
, el Asistente para TableAdapter creará automáticamente procedimientos almacenados con las declaraciones correspondientes INSERT
, UPDATE
, y DELETE
, así como un procedimiento almacenado para ejecutar la consulta principal.
El siguiente paso nos permite asignar un nombre a los procedimientos almacenados de TableAdapter. Use los nombres Employees_Select
, Employees_Insert
, Employees_Update
y Employees_Delete
, como se muestra en la figura 6.
Figura 6: Asignar un nombre a los procedimientos almacenados de TableAdapter (haga clic para ver la imagen de tamaño completo)
El paso final nos pide que asignemos un nombre a los métodos de TableAdapter. Use Fill
y GetEmployees
como nombres de método. Asegúrese también de dejar activada la casilla Crear métodos para enviar actualizaciones directamente a la base de datos (GenerateDBDirectMethods).
Figura 7: Asigne un nombre a los métodos Fill
de TableAdapter y GetEmployees
(haga clic para ver la imagen de tamaño completo)
Después de completar el asistente, tómese un momento para examinar los procedimientos almacenados en la base de datos. Debería ver cuatro nuevos: Employees_Select
, Employees_Insert
, Employees_Update
y Employees_Delete
. A continuación, inspeccione los EmployeesDataTable
y EmployeesTableAdapter
que acaba de crear. DataTable contiene una columna para cada campo devuelto por la consulta principal. Haga clic en TableAdapter y, a continuación, vaya a la ventana Propiedades. Allí verá que las InsertCommand
propiedades , UpdateCommand
y DeleteCommand
están configuradas correctamente para llamar a los procedimientos almacenados correspondientes.
Figura 8: TableAdapter incluye funcionalidades de inserción, actualización y eliminación (haga clic para ver la imagen de tamaño completo)
Con los procedimientos almacenados de inserción, actualización y eliminación creados automáticamente y las InsertCommand
, UpdateCommand
y DeleteCommand
propiedades configuradas correctamente, estamos listos para personalizar el procedimiento almacenado SelectCommand
para devolver información adicional sobre el gerente de cada empleado. En concreto, es necesario actualizar el procedimiento almacenado Employees_Select
para usar un JOIN
y devolver los valores FirstName
y LastName
del administrador. Una vez actualizado el procedimiento almacenado, es necesario actualizar DataTable para que incluya estas columnas adicionales. Abordaremos estas dos tareas en los pasos 2 y 3.
Paso 2: Personalizar el procedimiento almacenado para incluir unJOIN
Empiece por ir al Explorador de servidores, explorar en profundidad la carpeta Procedimientos almacenados de la base de datos Northwind y abrir el Employees_Select
procedimiento almacenado. Si no ve este procedimiento almacenado, haga clic con el botón derecho en la carpeta Procedimientos almacenados y elija Actualizar. Actualice el procedimiento almacenado para que use LEFT JOIN
para devolver el nombre completo del gerente.
SELECT Employees.EmployeeID, Employees.LastName,
Employees.FirstName, Employees.Title,
Employees.HireDate, Employees.ReportsTo,
Employees.Country,
Manager.FirstName as ManagerFirstName,
Manager.LastName as ManagerLastName
FROM Employees
LEFT JOIN Employees AS Manager ON
Employees.ReportsTo = Manager.EmployeeID
Después de actualizar la SELECT
instrucción, guarde los cambios en el menú Archivo y elija Guardar Employees_Select
. Como alternativa, puede hacer clic en el icono Guardar de la barra de herramientas o presionar Ctrl+S. Después de guardar los cambios, haga clic con el botón derecho en el Employees_Select
procedimiento almacenado en el Explorador de servidores y elija Ejecutar. Esto ejecutará el procedimiento almacenado y mostrará sus resultados en la ventana Salida (vea la figura 9).
Figura 9: Los resultados de los procedimientos almacenados se muestran en la ventana de salida (haga clic para ver la imagen de tamaño completo)
Paso 3: Actualizar las columnas de DataTable
En este momento, el Employees_Select
procedimiento almacenado devuelve los valores ManagerFirstName
y ManagerLastName
, pero en EmployeesDataTable
faltan estas columnas. Estas columnas que faltan se pueden agregar a DataTable de una de estas dos maneras:
- Manualmente : haga clic con el botón derecho en DataTable en el Diseñador de conjuntos de datos y, en el menú Agregar, elija Columna. A continuación, puede asignar un nombre a la columna y establecer sus propiedades en consecuencia.
-
Automáticamente : el Asistente para configuración de TableAdapter actualizará las columnas de DataTable para reflejar los campos devueltos por el
SelectCommand
procedimiento almacenado. Al usar instrucciones SQL ad hoc, el asistente también quitará las propiedadesInsertCommand
,UpdateCommand
yDeleteCommand
ya queSelectCommand
ahora contiene unJOIN
. Pero al usar procedimientos almacenados, estas propiedades de comando permanecen intactas.
Hemos explorado la adición manual de columnas a DataTable en tutoriales anteriores, incluyendo Master/Detail Usando una Lista con Viñetas de Registros Maestros con un DataList de Detalles y Carga de Archivos, y examinaremos este proceso de nuevo con más detalle en nuestro siguiente tutorial. En este tutorial, sin embargo, vamos a usar el enfoque automático mediante el Asistente para configuración de TableAdapter.
Para empezar, haga clic con el botón derecho en el EmployeesTableAdapter
y seleccione Configurar en el menú contextual. Esto abre el Asistente para configuración de TableAdapter, que enumera los procedimientos almacenados que se usan para seleccionar, insertar, actualizar y eliminar, junto con sus valores devueltos y parámetros (si los hay). En la figura 10 se muestra este asistente. Aquí podemos ver que el Employees_Select
procedimiento almacenado ahora devuelve los ManagerFirstName
campos y ManagerLastName
.
Figura 10: El asistente muestra la lista de columnas actualizada para el Employees_Select
procedimiento almacenado (haga clic para ver la imagen de tamaño completo)
Para finalizar el asistente, haga clic en Instalar. Al volver al Diseñador de conjuntos de datos, EmployeesDataTable
incluye dos columnas adicionales: ManagerFirstName
y ManagerLastName
.
Figura 11: Contiene EmployeesDataTable
dos columnas nuevas (haga clic para ver la imagen de tamaño completo)
Para ilustrar que el procedimiento almacenado actualizado Employees_Select
está en vigor y que las funcionalidades de inserción, actualización y eliminación de TableAdapter siguen siendo funcionales, vamos a crear una página web que permita a los usuarios ver y eliminar empleados. Sin embargo, antes de crear este tipo de página, primero necesitamos crear una nueva clase en la Capa de Lógica Empresarial para trabajar con empleados del NorthwindWithSprocs
DataSet. En el paso 4, crearemos una EmployeesBLLWithSprocs
clase. En el paso 5, usaremos esta clase desde una página de ASP.NET.
Paso 4: Implementar la capa lógica de negocios
Cree un nuevo archivo de clase en la ~/App_Code/BLL
carpeta denominada EmployeesBLLWithSprocs.cs
. Esta clase imita la semántica de la clase existente EmployeesBLL
, solo esta nueva proporciona menos métodos y usa dataSet NorthwindWithSprocs
(en lugar de Northwind
DataSet). Agregue el siguiente código a la clase EmployeesBLLWithSprocs
.
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using NorthwindWithSprocsTableAdapters;
[System.ComponentModel.DataObject]
public class EmployeesBLLWithSprocs
{
private EmployeesTableAdapter _employeesAdapter = null;
protected EmployeesTableAdapter Adapter
{
get
{
if (_employeesAdapter == null)
_employeesAdapter = new EmployeesTableAdapter();
return _employeesAdapter;
}
}
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Select, true)]
public NorthwindWithSprocs.EmployeesDataTable GetEmployees()
{
return Adapter.GetEmployees();
}
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Delete, true)]
public bool DeleteEmployee(int employeeID)
{
int rowsAffected = Adapter.Delete(employeeID);
// Return true if precisely one row was deleted, otherwise false
return rowsAffected == 1;
}
}
La EmployeesBLLWithSprocs
propiedad de la clase s Adapter
devuelve una instancia de DataSet NorthwindWithSprocs
s EmployeesTableAdapter
. Esto es utilizado por los métodos GetEmployees
de la clase s DeleteEmployee
. El método GetEmployees
llama al método correspondiente EmployeesTableAdapter
, que invoca el procedimiento almacenado GetEmployees
y almacena sus resultados en un Employees_Select
. El DeleteEmployee
método llama de forma similar al EmployeesTableAdapter
método s Delete
, que invoca el Employees_Delete
procedimiento almacenado.
Paso 5: Trabajar con los datos en la capa de presentación
Una vez completada la EmployeesBLLWithSprocs
clase, estamos listos para trabajar con los datos de los empleados a través de una página de ASP.NET. Abra la página JOINs.aspx
en la carpeta AdvancedDAL
y arrastre un control GridView desde el cuadro de herramientas al Diseñador, estableciendo su propiedad ID
en Employees
. A continuación, desde la etiqueta inteligente GridView, enlace la cuadrícula a un nuevo control ObjectDataSource denominado EmployeesDataSource
.
Configure ObjectDataSource para usar la clase EmployeesBLLWithSprocs
y, en las pestañas SELECT y DELETE, asegúrese de que se seleccionen los métodos GetEmployees
y DeleteEmployee
en las listas desplegables. Haga clic en Finalizar para completar la configuración de ObjectDataSource.
Figura 12: Configurar objectDataSource para usar la clase (EmployeesBLLWithSprocs
la imagen de tamaño completo)
Figura 13: Hacer que ObjectDataSource use los GetEmployees
métodos y DeleteEmployee
(haga clic para ver la imagen de tamaño completo)
Visual Studio agregará un BoundField a GridView para cada una de las columnas de EmployeesDataTable
. Quite todos estos BoundFields excepto Title
, LastName
, FirstName
, ManagerFirstName
y ManagerLastName
, y cambie el nombre de las propiedades de los últimos cuatro BoundFields a Apellidos, Nombre, Nombre del Administrador y Apellidos del Administrador, respectivamente.
Para permitir que los usuarios eliminen empleados de esta página, es necesario hacer dos cosas. En primer lugar, indique a GridView que proporcione funcionalidades de eliminación comprobando la opción Habilitar eliminación de su etiqueta inteligente. En segundo lugar, cambie la propiedad del ObjectDataSource del valor del OldValuesParameterFormatString
establecido por el asistente de ObjectDataSource (original_{0}
) a su valor predeterminado ({0}
). Después de realizar estos cambios, el marcado declarativo de GridView y ObjectDataSource deben ser similares a los siguientes:
<asp:GridView ID="Employees" runat="server" AutoGenerateColumns="False"
DataKeyNames="EmployeeID" DataSourceID="EmployeesDataSource">
<Columns>
<asp:CommandField ShowDeleteButton="True" />
<asp:BoundField DataField="Title"
HeaderText="Title"
SortExpression="Title" />
<asp:BoundField DataField="LastName"
HeaderText="Last Name"
SortExpression="LastName" />
<asp:BoundField DataField="FirstName"
HeaderText="First Name"
SortExpression="FirstName" />
<asp:BoundField DataField="ManagerFirstName"
HeaderText="Manager's First Name"
SortExpression="ManagerFirstName" />
<asp:BoundField DataField="ManagerLastName"
HeaderText="Manager's Last Name"
SortExpression="ManagerLastName" />
</Columns>
</asp:GridView>
<asp:ObjectDataSource ID="EmployeesDataSource" runat="server"
DeleteMethod="DeleteEmployee" OldValuesParameterFormatString="{0}"
SelectMethod="GetEmployees" TypeName="EmployeesBLLWithSprocs">
<DeleteParameters>
<asp:Parameter Name="employeeID" Type="Int32" />
</DeleteParameters>
</asp:ObjectDataSource>
Pruebe la página visitando a través de un explorador. Como se muestra en la figura 14, la página enumerará cada empleado y el nombre de su gerente (suponiendo que tenga uno).
Figura 14: en JOIN
el Employees_Select
procedimiento almacenado devuelve el nombre del administrador (haga clic para ver la imagen de tamaño completo)
Al hacer clic en el botón Eliminar, se inicia el flujo de trabajo de eliminación, que culmina en la ejecución del Employees_Delete
procedimiento almacenado. Sin embargo, se produce un error en la instrucción intentada DELETE
en el procedimiento almacenado debido a una violación de la restricción de clave externa (vea la Figura 15). En concreto, cada empleado tiene uno o varios registros en la Orders
tabla, lo que provoca un error en la eliminación.
Figura 15: Eliminar un empleado que tenga los pedidos correspondientes da como resultado una infracción de restricción de clave externa (haga clic para ver la imagen de tamaño completo)
Para permitir que un empleado se elimine, podría:
- Actualizar la restricción de clave externa para que las eliminaciones sean en cascada.
- Elimine manualmente los registros de la
Orders
tabla para los empleados que desea eliminar o - Actualice el
Employees_Delete
procedimiento almacenado para eliminar primero los registros relacionados de laOrders
tabla antes de eliminar elEmployees
registro. Hemos discutido esta técnica en el tutorial Uso de procedimientos almacenados existentes para TableAdapters de DataSet con tipo.
Lo dejo como ejercicio para el lector.
Resumen
Al trabajar con bases de datos relacionales, es habitual que las consultas extraen sus datos de varias tablas relacionadas. Las subconsultas correlacionadas y JOIN
s proporcionan dos técnicas diferentes para acceder a datos de tablas relacionadas en una consulta. En tutoriales anteriores, normalmente se usaban subconsultas correlacionadas porque TableAdapter no puede generar automáticamente INSERT
, UPDATE
y DELETE
para consultas que involucran JOIN
s. Aunque estos valores se pueden proporcionar manualmente, cuando se usen instrucciones SQL ad hoc, se sobrescribirá cualquier personalización cuando se complete el Asistente para configuración de TableAdapter.
Afortunadamente, TableAdapters creados mediante procedimientos almacenados no sufren la misma frágilidad que las creadas mediante instrucciones SQL ad hoc. Por lo tanto, es posible crear un TableAdapter cuya consulta principal use JOIN
al emplear procedimientos almacenados. En este tutorial hemos visto cómo crear un tableAdapter de este tipo. Comenzamos utilizando una consulta JOIN
-less SELECT
para la consulta principal del TableAdapter, de modo que los procedimientos almacenados de inserción, actualización y eliminación correspondientes se crearan automáticamente. Con la configuración inicial de TableAdapter completada, aumentamos el procedimiento almacenado SelectCommand
para utilizar un JOIN
y volvimos a ejecutar el Asistente para Configuración de TableAdapter para actualizar las columnas EmployeesDataTable
.
Al volver a ejecutar el Asistente para configuración de TableAdapter, se actualizaron automáticamente las EmployeesDataTable
columnas para reflejar los campos de datos devueltos por el Employees_Select
procedimiento almacenado. Como alternativa, podríamos haber agregado estas columnas manualmente a DataTable. Exploraremos la adición manual de columnas a DataTable en el siguiente tutorial.
¡Feliz programación!
Acerca del autor
Scott Mitchell, autor de siete libros de ASP/ASP.NET y fundador de 4GuysFromRolla.com, ha estado trabajando con tecnologías web de Microsoft desde 1998. Scott trabaja como consultor independiente, entrenador y escritor. Su último libro es Sams Teach Yourself ASP.NET 2.0 en 24 horas. Se puede contactar con él en mitchell@4GuysFromRolla.com.
Agradecimientos especiales a
Esta serie de tutoriales contó con la revisión de muchos revisores que fueron de gran ayuda. Los revisores principales de este tutorial fueron Hilton Geisenow, David Suru y Teresa Murphy. ¿Le interesa revisar mis próximos artículos de MSDN? Si es así, mándame un mensaje a mitchell@4GuysFromRolla.com.