Actualizar TableAdapter para usar JOIN (VB)
por Scott Mitchell
Al trabajar con una base de datos, es habitual solicitar datos que están distribuidos 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 de JOIN antes de examinar cómo crear un TableAdapter que incluya una operación JOIN en su consulta principal.
Introducción
Con las bases de datos relacionales, los datos con los que estamos interesados en trabajar a menudo están distribuidos entre varias tablas. Por ejemplo, al mostrar información del producto, es probable que deseemos enumerar los nombres de categoría y proveedor correspondientes a cada producto. La tabla Products
tiene los valores CategoryID
y SupplierID
, pero los nombres reales de categoría y proveedor se encuentran en las tablas Categories
y Suppliers
, respectivamente.
Para recuperar información de otra tabla relacionada, podemos usar subconsultas correlacionadas u operacionesJOIN
. Una subconsulta correlacionada es una consulta SELECT
anidada que hace referencia a 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 consulta principal de ProductsTableAdapter
para devolver los nombres de categoría y proveedor de cada producto. Una JOIN
es una construcción SQL que combina filas relacionadas de dos tablas diferentes. Hemos utilizado una JOIN
en el tutorial Consulta de datos con el control SqlDataSource para mostrar información de categorías con cada producto.
La razón por la que nos hemos abstenido de usar operaciones JOIN
con TableAdapters se debe a las limitaciones del asistente de TableAdapter para generar automáticamente las instrucciones INSERT
, UPDATE
y DELETE
correspondientes. Más concretamente, si la consulta principal de TableAdapter contiene operaciones JOIN
, TableAdapter no puede crear automáticamente las instrucciones SQL ad hoc ni los procedimientos almacenados para sus propiedades InsertCommand
, UpdateCommand
y DeleteCommand
.
En este tutorial, compararemos y contrastaremos brevemente las subconsultas correlacionadas y las JOIN
antes de explorar cómo crear un TableAdapter que incluya operaciones JOIN
en su consulta principal.
Comparar y contrastar subconsultas correlacionadas y JOIN
Recuerde que el ProductsTableAdapter
creado en el primer tutorial del conjunto de datos Northwind
usa subconsultas correlacionadas para devolver los nombres de categoría y proveedor correspondientes de cada producto. A continuación, se muestra la consulta principal de ProductsTableAdapter
.
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 SELECT
que devuelven un valor único por producto como una columna adicional en la lista de columnas de la instrucción SELECT
externa.
Como alternativa, se puede usar una JOIN
para devolver el nombre de categoría y proveedor de cada producto. La consulta siguiente devuelve la misma salida que la anterior, pero usa JOIN
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
Una JOIN
combina los registros de una tabla con los registros de otra en función de algunos criterios. En la consulta anterior, por ejemplo, LEFT JOIN Categories ON Categories.CategoryID = Products.CategoryID
indica a SQL Server que combine cada registro de producto con el registro de categoría cuyo valor de CategoryID
coincida con el valor de CategoryID
del producto. El resultado combinado nos permite trabajar con los campos de categoría correspondientes para cada producto (por ejemplo, CategoryName
).
Nota:
Las JOIN
se usan normalmente al consultar datos de bases de datos relacionales. Si no está familiarizado con la sintaxis de JOIN
o necesita repasar su uso, recomiendo el tutorial SQL Join en W3 Schools. También merece la pena leer las secciones Aspectos básicos de JOIN
y Aspectos básicos de las subconsultas de los Manuales de SQL online.
Como las JOIN
y las subconsultas correlacionadas se pueden usar para recuperar datos relacionados de otras tablas, no son pocos los desarrolladores que tienen dudas sobre qué enfoque usar. Todos los gurús de SQL con los que he hablado han dicho aproximadamente lo mismo, que realmente no importa en términos de rendimiento, ya que SQL Server generará planes de ejecución aproximadamente idénticos. Su consejo es usar la técnica con la que usted y su equipo se sientan más cómodos. Merece la pena señalar que, después de impartir este consejo, estos expertos expresan inmediatamente su preferencia por JOIN
frente a las subconsultas correlacionadas.
Cuando se compila una capa de acceso a datos con conjuntos de datos con tipo, las herramientas funcionan mejor cuando se usan subconsultas. En concreto, el asistente de TableAdapter no generará automáticamente las instrucciones INSERT
, UPDATE
y DELETE
correspondientes si la consulta principal contiene JOIN
, pero sí las generará automáticamente cuando se usen subconsultas correlacionadas.
Para explorar esta limitación, cree un conjunto de datos con tipo temporal en la carpeta ~/App_Code/DAL
. Durante el Asistente para configuración de TableAdapter, elija usar instrucciones SQL ad hoc y escriba la siguiente consulta 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
(Haga clic para ver la imagen a tamaño completo)
De forma predeterminada, TableAdapter creará automáticamente instrucciones INSERT
, UPDATE
y DELETE
basadas en la consulta principal. Si hace clic en el botón Opciones avanzadas, puede ver que esta característica está habilitada. A pesar de este ajuste, TableAdapter no podrá crear las instrucciones INSERT
, UPDATE
y DELETE
porque la consulta principal contiene una JOIN
.
Figura 2: Escribir una consulta principal que contenga JOIN
Haga clic en Finalizar para completar el asistente. En este punto, el Diseñador del conjunto de datos incluirá un solo TableAdapter con una DataTable con columnas para cada uno de los campos devueltos en la lista de columnas de la consulta SELECT
. Esto incluye CategoryName
y SupplierName
, como se muestra en 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 no tiene 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 propiedades InsertCommand
, UpdateCommand
y DeleteCommand
se establecen en (None).
Figura 4: Las propiedades InsertCommand
, UpdateCommand
y DeleteCommand
están establecidas en (None) (Haga clic para ver la imagen a tamaño completo)
Para solucionar este problema, podemos proporcionar manualmente las instrucciones SQL y los parámetros de las propiedades InsertCommand
, 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 instrucciones INSERT
, UPDATE
y DELETE
se generen automáticamente para nosotros. Después de completar el asistente, podemos actualizar manualmente SelectCommand
de TableAdapter en la ventana Propiedades para que incluya la sintaxis de JOIN
.
Aunque este enfoque funciona, es muy frágil cuando se usan consultas SQL ad hoc, porque cada vez que se vuelve a configurar la consulta principal de TableAdapter con el asistente, se vuelven a crear las instrucciones INSERT
, UPDATE
y DELETE
generadas automáticamente. Esto significa que todas las personalizaciones realizadas después se perderán si hacemos clic con el botón derecho en TableAdapter, elegimos Configurar en el menú contextual y volvemos a completar el asistente.
La fragilidad de las instrucciones INSERT
, UPDATE
y DELETE
generadas automáticamente por TableAdapter se limita afortunadamente a las instrucciones SQL ad hoc. Si TableAdapter usa procedimientos almacenados, puede personalizar los procedimientos almacenados SelectCommand
, InsertCommand
, UpdateCommand
o DeleteCommand
y volver a ejecutar el Asistente para configuración de TableAdapter sin temer que se modifiquen los procedimientos almacenados.
En los siguientes pasos crearemos un TableAdapter que, inicialmente, usa una consulta principal que omite cualquier JOIN
para que se generen automáticamente los procedimientos almacenados de inserción, actualización y eliminación correspondientes. A continuación, actualizaremos SelectCommand
para que use una JOIN
que devuelva columnas adicionales de las tablas relacionadas. Por último, crearemos una clase de capa de lógica empresarial correspondiente y mostraremos el uso de TableAdapter en una página web ASP.NET.
Paso 1: Crear un TableAdapter utilizando una consulta principal simplificada
En este tutorial, añadiremos un TableAdapter y una DataTable fuertemente tipada para la tabla Employees
del conjunto de datos NorthwindWithSprocs
. La tabla Employees
contiene un campo ReportsTo
que especifica el EmployeeID
del director del empleado. Por ejemplo, la empleada Anne Dodsworth tiene un valor de ReportTo
igual a 5, que es el EmployeeID
de Steven Buchanan. Por lo tanto, Anne responde ante Steven, que es su director. Además de obtener el valor ReportsTo
de cada empleado, es posible que también deseemos recuperar el nombre de su jefe. Esto se puede lograr utilizando una JOIN
. No obstante, el uso de una JOIN
al crear inicialmente el TableAdapter impide que el asistente genere automáticamente las funcionalidades de inserción, actualización y eliminación correspondientes. Por lo tanto, empezaremos creando un TableAdapter cuya consulta principal no contenga ninguna JOIN
. A continuación, en el paso 2, actualizaremos el procedimiento almacenado de la consulta principal para recuperar el nombre del director utilizando una 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 Añadir en el menú contextual y elija el elemento de menú TableAdapter. Se iniciará el Asistente para configuración de TableAdapter. Como se muestra en la figura 5, solicite al asistente que cree nuevos procedimientos almacenados y haga clic en Siguiente. Para repasar cómo crear nuevos procedimientos almacenados en el asistente de TableAdapter, consulte el tutorial Creación de nuevos procedimientos almacenados para los TableAdapters del conjunto de datos con tipo.
Figura 5: Seleccionw la opción Crear nuevos procedimientos almacenados (Haga clic para ver la imagen a tamaño completo)
Use la siguiente instrucción SELECT
para la consulta principal de TableAdapter:
SELECT EmployeeID, LastName, FirstName, Title, HireDate, ReportsTo, Country
FROM Employees
Como esta consulta no incluye ninguna JOIN
, el asistente de TableAdapter creará automáticamente procedimientos almacenados con las instrucciones INSERT
, UPDATE
y DELETE
correspondientes, 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: Asigne un nombre a los procedimientos almacenados de TableAdapter (Haga clic para ver la imagen a tamaño completo)
El paso final nos solicita que asignemos un nombre a los métodos de TableAdapter. Utilice 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
y GetEmployees
de TableAdapter (Haga clic para ver la imagen a tamaño completo)
Después de completar el asistente, dedique un momento a examinar los procedimientos almacenados de la base de datos. Debería ver cuatro nuevos: Employees_Select
, Employees_Insert
, Employees_Update
y Employees_Delete
. A continuación, inspeccione las propiedades 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 propiedades InsertCommand
, 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 a tamaño completo)
Con los procedimientos almacenados de inserción, actualización y eliminación creados automáticamente y las propiedades InsertCommand
, UpdateCommand
y DeleteCommand
configuradas correctamente, estamos listos para personalizar el procedimiento almacenado de SelectCommand
para devolver información adicional sobre el director de cada empleado. En concreto, es necesario actualizar el procedimiento almacenado Employees_Select
para usar una JOIN
y devolver los valores FirstName
y LastName
del director. 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 una JOIN
Para empezar, vaya al Explorador de servidores, desplácese a la carpeta Procedimientos almacenados de la base de datos Northwind y abra el procedimiento almacenado Employees_Select
. 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 y el apellido del director:
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 instrucción SELECT
, para guardar los cambios, vaya al 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 procedimiento almacenado Employees_Select
en el Explorador de servidores y elija Ejecutar. Se ejecutará el procedimiento almacenado y se mostrarán sus resultados en la ventana Salida (vea la figura 9).
Figura 9: Los resultados del procedimiento almacenado se muestran en la Ventana de salida (Haga clic para ver la imagen a tamaño completo)
Paso 3: Actualizar las columnas de DataTable
En este punto, el procedimiento almacenado Employees_Select
devuelve los valores ManagerFirstName
y ManagerLastName
, pero en EmployeesDataTable
faltan estas columnas. Estas columnas que faltan se pueden añadir a DataTable de dos maneras:
- Manualmente: haga clic con el botón derecho en DataTable en el Diseñador de conjuntos de datos y, en el menú Añadir, elija Columna. A continuación, puede asignar un nombre a la columna y establecer sus propiedades según corresponda.
- Automáticamente: el Asistente para configuración de TableAdapter actualizará las columnas de DataTable para reflejar los campos devueltos por el procedimiento almacenado
SelectCommand
. Cuando se usan instrucciones SQL ad hoc, el asistente también eliminará las propiedadesInsertCommand
,UpdateCommand
yDeleteCommand
, ya queSelectCommand
ahora contiene unaJOIN
. No obstante, cuando se utilizan procedimientos almacenados, estas propiedades de comando permanecen intactas.
Hemos explorado la adición manual de columnas de DataTable en tutoriales anteriores, incluidos Maestro y detalles mediante una lista con viñetas de registros maestros con un control DataList de detalles y Carga de archivos, y veremos este proceso de nuevo con más detalle en nuestro siguiente tutorial. No obstante, en este tutorial vamos a usar el enfoque automático a través del Asistente para configuración de TableAdapter.
Para empezar, haga clic con el botón derecho en EmployeesTableAdapter
y seleccione Configurar en el menú contextual. Se abrirá 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 sus parámetros (si los hay). La Figura 10 muestra este asistente. Aquí podemos ver que el procedimiento almacenado Employees_Select
ahora devuelve los campos ManagerFirstName
y ManagerLastName
.
Figura 10: El asistente muestra la lista de columnas actualizada para el procedimiento almacenado Employees_Select
(Haga clic para ver la imagen a 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: EmployeesDataTable
contiene dos columnas nuevas (Haga clic para ver la imagen a tamaño completo)
Para ilustrar que el procedimiento almacenado Employees_Select
actualizado está en vigor y que las funcionalidades de inserción, actualización y eliminación de TableAdapter siguen estando operativas, vamos a crear una página web que permita a los usuarios ver y eliminar empleados. Sin embargo, antes de crear esta página, es necesario crear primero una nueva clase en la capa de lógica empresarial para trabajar con empleados del conjunto de datos NorthwindWithSprocs
. En el paso 4, crearemos una clase EmployeesBLLWithSprocs
. En el paso 5, usaremos esta clase desde una página ASP.NET.
Paso 4: Implementar la capa de lógica empresarial
Cree un nuevo archivo de clase en la carpeta ~/App_Code/BLL
llamado EmployeesBLLWithSprocs.vb
. Esta clase imita la semántica de la clase EmployeesBLL
existente, solo que esta nueva proporciona menos métodos y usa el conjunto de datos NorthwindWithSprocs
(en lugar del conjunto de datos Northwind
). Agregue el siguiente código a la clase EmployeesBLLWithSprocs
.
Imports NorthwindWithSprocsTableAdapters
<System.ComponentModel.DataObject()> _
Public Class EmployeesBLLWithSprocs
Private _employeesAdapter As EmployeesTableAdapter = Nothing
Protected ReadOnly Property Adapter() As EmployeesTableAdapter
Get
If _employeesAdapter Is Nothing Then
_employeesAdapter = New EmployeesTableAdapter()
End If
Return _employeesAdapter
End Get
End Property
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Select, True)> _
Public Function GetEmployees() As NorthwindWithSprocs.EmployeesDataTable
Return Adapter.GetEmployees()
End Function
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Delete, True)> _
Public Function DeleteEmployee(ByVal employeeID As Integer) As Boolean
Dim rowsAffected = Adapter.Delete(employeeID)
'Return true if precisely one row was deleted, otherwise false
Return rowsAffected = 1
End Function
End Class
La propiedad Adapter
de la clase EmployeesBLLWithSprocs
devuelve una instancia de EmployeesTableAdapter
del conjunto de datos NorthwindWithSprocs
. Esta se utiliza en los métodos GetEmployees
y DeleteEmployee
de la clase. El método GetEmployees
llama al método GetEmployees
correspondiente de EmployeesTableAdapter
, que invoca el procedimiento almacenado Employees_Select
y rellena sus resultados en una EmployeeDataTable
. El método DeleteEmployee
llama de forma similar al método Delete
de EmployeesTableAdapter
, que invoca el procedimiento almacenado Employees_Delete
.
Paso 5: Trabajar con los datos en la capa de presentación
Una vez completada la clase EmployeesBLLWithSprocs
, estamos listos para trabajar con los datos de los empleados a través de una página ASP.NET. Abra la página JOINs.aspx
en la carpeta AdvancedDAL
, arrastre un control GridView desde el cuadro de herramientas al Diseñador y establezca su propiedad ID
en Employees
. A continuación, en la etiqueta inteligente de GridView, enlace la cuadrícula a un nuevo control ObjectDataSource denominado EmployeesDataSource
.
Configure ObjectDataSource para que utilice la clase EmployeesBLLWithSprocs
y, en las pestañas SELECT y DELETE, asegúrese de que los métodos GetEmployees
y DeleteEmployee
estén seleccionados en las listas desplegables. Haga clic en Finalizar para completar la configuración de ObjectDataSource.
Figura 12: Configure ObjectDataSource para usar la clase EmployeesBLLWithSprocs
(Haga clic para ver la imagen a tamaño completo)
Figura 13: Solicitar a ObjectDataSource que use los métodos GetEmployees
y DeleteEmployee
(Haga clic para ver la imagen a tamaño completo)
Visual Studio añadirá un BoundField a la GridView para cada una de las columnas de EmployeesDataTable
. Elimine todos estos BoundFields excepto Title
, LastName
FirstName
, ManagerFirstName
y ManagerLastName
, y cambie el nombre de las propiedades HeaderText
de los últimos cuatro BoundFields a Apellido, Nombre, Nombre del director y Apellido del director, respectivamente.
Para que los usuarios puedan eliminar empleados en esta página, es necesario realizar dos acciones. En primer lugar, para indicar a GridView que proporcione funcionalidades de eliminación, marque la opción Habilitar eliminación en su etiqueta inteligente. En segundo lugar, cambie la propiedad OldValuesParameterFormatString
de ObjectDataSource del valor 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 debe ser similar al siguiente:
<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 visitándola a través de un explorador. Como se muestra en la figura 14, la página mostrará cada empleado y el nombre de su director (suponiendo que tenga uno).
Figura 14: JOIN
en el procedimiento almacenado Employees_Select
devuelve el nombre del director (Haga clic para ver la imagen a 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 procedimiento almacenado Employees_Delete
. Sin embargo, se produce un error en la instrucción DELETE
intentada en el procedimiento almacenado debido a una infracción de restricción de clave externa (vea la figura 15). En concreto, cada empleado tiene uno o varios registros en la tabla Orders
, lo que provoca un error en la eliminación.
Figura 15: Eliminar un empleado que tenga registros correspondientes da como resultado una infracción de restricción de clave externa (Haga clic para ver la imagen a tamaño completo)
Para permitir la eliminación de un empleado, puede:
- Actualizar la restricción de clave externa a eliminaciones en cascada,
- Eliminar manualmente los registros de la tabla
Orders
para los empleados que desee eliminar; o - Actualizar el procedimiento almacenado
Employees_Delete
para eliminar primero los registros relacionados de la tablaOrders
antes de eliminar el registroEmployees
. Hemos analizado esta técnica en el tutorial Uso de procedimientos almacenados existentes para Conjunto de datos con tipo de TableAdapters.
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 las JOIN
proporcionan dos técnicas diferentes para acceder a los datos de tablas relacionadas en una consulta. En tutoriales anteriores, normalmente hemos utilizado subconsultas correlacionadas porque TableAdapter no puede generar automáticamente las instrucciones INSERT
, UPDATE
y DELETE
para las consultas que implican JOIN
. Aunque estos valores se pueden proporcionar manualmente, cuando se usan instrucciones SQL ad hoc, las personalizaciones se sobrescribirán cuando finalice el Asistente para configuración de TableAdapter.
Afortunadamente, los TableAdapters creados utilizando procedimientos almacenados no tienen la misma fragilidad que los creados con instrucciones SQL ad hoc. Por lo tanto, es factible crear un TableAdapter cuya consulta principal utilice una JOIN
cuando se usan procedimientos almacenados. En este tutorial, hemos visto cómo crear un TableAdapter de este tipo. Hemos empezado utilizando una consulta SELECT
sin JOIN
para la consulta principal de TableAdapter, para 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, hemos aumentado el procedimiento almacenado SelectCommand
para usar una JOIN
y hemos vuelto a ejecutar el Asistente para configuración de TableAdapter para actualizar las columnas de EmployeesDataTable
.
Al volver a ejecutar el Asistente para configuración de TableAdapter, se han actualizado automáticamente las columnas de EmployeesDataTable
para reflejar los campos de datos devueltos por el procedimiento almacenado Employees_Select
. De manera alternativa, podríamos haber añadido 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 in 24 Hours. Puede ponerse en contacto con él a través de mitchell@4GuysFromRolla.com. o de su blog, que se puede encontrar en http://ScottOnWriting.NET.
Agradecimientos especiales a
Esta serie de tutoriales fue revisada por muchos revisores 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 fuera así, escríbame a mitchell@4GuysFromRolla.com.