¿Debo usar una función, Ver T-SQL o procedimiento almacenado?
-
28-09-2019 - |
Pregunta
Tengo una pregunta acerca de la reutilización de datos de la tabla, sino un punto de vista no funcionará en este escenario ya que tengo un parámetro que debe transmitirse. Básicamente esta parte del sistema requiere un travellerid
para ser enviados al procedimiento y una lista de arreglistas devuelto para que el viajero específica. Hay alrededor de 7 reglas de negocio que se utilizan para determinar qué arreglistas pueden ser devueltos y son mutuamente excluyentes, por lo que con el fin de acomodar estas reglas opcionales he utilizado una serie de las uniones dentro de una consulta derivada. Esto está funcionando bien, y el rendimiento parece bien a través de una base de datos bastante grande, sin embargo tengo que volver a utilizar estas reglas (sindicatos) en aproximadamente 4 otras partes del sistema.
Al principio me trató de crear una vista con estos sindicatos, pero que no funcionó debido a la lógica que difieren en cada Unión y diferentes requisitos de los parámetros, así que estaba pensando que tal función podría resolver este problema? Si he creado una función que tuvo @travellerid
como un parámetro y devuelve una lista de arrangerid
basado en las reglas de negocio, esto sería una solución ideal / rápido? Actualmente estoy usando UNION ALL y una clara en la consulta externa, ya que demostró ser mucho más rápido que el uso de la unión de la singularidad de los datos.
Procedimiento actual con las reglas de negocio a continuación (SQL Server 2008):
CREATE PROCEDURE [dbo].[getArrangersForTraveller]
@travellerid int
AS
DECLARE @costcentreid int
DECLARE @departmentid int
-- Shorthand the traveller costcentre and department for use in queries below
SET @costcentreid = (SELECT costcentreid FROM traveller WHERE id = @travellerid)
SET @departmentid = (SELECT departmentid FROM traveller WHERE id = @travellerid)
SELECT DISTINCT t.id, t.firstname, t.lastname, ti.name AS title, dv.preferred
FROM traveller t
INNER JOIN title ti ON t.titleid = ti.id
INNER JOIN
(
-- Get Preferred Arrangers linked to Department Groups
SELECT dg.arrangerid as id
FROM departmentGroup dg
INNER JOIN department_departmentGroup ddg
ON (dg.id = ddg.departmentGroupId AND ddg.departmentid = @departmentid)
UNION ALL
-- Get Preferred Arrangers linked to Cost Centre Groups
SELECT cg.arrangerid as id
FROM costCentreGroup cg
INNER JOIN costcentre_costCentreGroup ccg
ON (cg.id = ccg.costCentreGroupId AND ccg.costcentreid = @costcentreid)
UNION ALL
-- If Cost Centre Group has a linked department and this department matches
-- the travel arrangers department then return these travel arrangers as well
SELECT t3.id
FROM costCentreGroup cg1
INNER JOIN costcentre_costCentreGroup ccg1
ON (cg1.id = ccg1.costCentreGroupId AND ccg1.costcentreid = @costcentreid)
INNER JOIN traveller t3
ON t3.departmentid = cg1.departmentid
WHERE t3.accesslevelid > 1
UNION ALL
-- Get Direct linked travel arrangers
SELECT t1.travelarrangerid as id
FROM travelarranger_traveller t1
WHERE t1.travellerid = @travellerid
UNION ALL
-- Get Cost Centre linked arrangers
SELECT tc.travelarrangerid as id
FROM travelArranger_costcentre tc
WHERE tc.costcentreid = @costcentreid
UNION ALL
-- Get Department linked arrangers
SELECT td.travelarrangerid
FROM travelArranger_department td
WHERE td.departmentid = @departmentid
UNION ALL
-- Get Company flagged arrangers
SELECT t2.id
FROM traveller t2
INNER JOIN company c ON t2.companyid = c.id
WHERE t2.accesslevelid > 1
AND ((c.allowTravelArrangerDepartmentAccess = 1 AND t2.departmentid = @departmentid)
OR (c.allowTravelArrangerCostCentreAccess = 1 AND t2.costcentreid = @costcentreid))
) as dv ON dv.id = t.id
WHERE t.accessLevelid > 1 -- arranger or manager
AND t.isenabled = 1
ORDER BY dv.preferred DESC, t.lastname, t.firstname;
Solución
Al principio me trató de crear una vista con estos sindicatos, pero que no funcionó debido a la lógica que difieren en cada Unión y diferentes requisitos de los parámetros, así que estaba pensando tal vez una función podría resolver este problema? Si he creado una función que tuvo como @travellerid parámetro y devuelve una lista de arrangerid de basado en las reglas de negocio, esto sería una solución ideal / rápido?
usted está pensando procedimiento de programación / OO, pero SQL se establece en función.
Una función funcionaría, pero se aseguraría de que un índice no podía utilizarse cuando se utiliza la función de los criterios de decisión / etc. Una vista no materializada es sólo ligeramente mejor; en SQL Server existe la opción de utilizar una vista indizada (También conocido como materializó vista) pero se ven limitados notoriamente. Va en contra de los conceptos de programación modular, pero funciona mejor cuanto menos SQL intenta modularizar y utilizar sólo lo que realmente necesita.
Me re-escribió la consulta, pero se dio cuenta de que la columna dv.preferred se hace referencia en la consulta externa, pero no está presente en el interior. Siendo que dv
es un conglomerado de varias tablas y la lógica, el valor id
ser devuelto no es de ningún valor real fuera de la consulta interna, ya que había necesidad de saber qué tabla el valor de procedencia. Dicho esto, aquí está:
SELECT t.id, t.firstname, t.lastname, ti.name AS title /*, dv.preferred */
FROM TRAVELLER t
JOIN title ti ON t.titleid = ti.id
WHERE (EXISTS(SELECT NULL -- Get Preferred Arrangers linked to Department Groups
FROM departmentGroup dg
JOIN department_departmentGroup ddg ON ddg.departmentGroupId = dg.id
AND ddg.departmentid = @departmentid
WHERE dg.arrangerid = t.id)
OR EXISTS(SELECT NULL -- Get Preferred Arrangers linked to Cost Centre Groups
FROM costCentreGroup cg
JOIN costcentre_costCentreGroup ccg ON ccg.costCentreGroupId = cg.id
AND ccg.costcentreid = @costcentreid
WHERE cg.arrangerid = t.id)
OR EXISTS(SELECT NULL -- If Cost Centre Group has a linked department and this department matches the travel arrangers department then return these travel arrangers as well
FROM costCentreGroup cg1
JOIN costcentre_costCentreGroup ccg1 ON ccg1.costCentreGroupId = cg1.id
AND ccg1.costcentreid = @costcentreid
JOIN traveller t3 ON t3.departmentid = cg1.departmentid
AND t3.accesslevelid > 1
WHERE t3.id = t.id)
OR EXISTS(SELECT NULL -- Get Direct linked travel arrangers
FROM travelarranger_traveller t1
WHERE t1.travellerid = @travellerid
AND t1.travelarrangerid = t.id)
OR EXISTS(SELECT NULL -- Get Cost Centre linked arrangers
FROM travelArranger_costcentre tc
WHERE tc.costcentreid = @costcentreid
AND tc.travelarrangerid = t.id)
OR EXISTS(SELECT NULL -- Get Department linked arrangers
FROM travelArranger_department td
WHERE td.departmentid = @departmentid
AND td.travelarrangerid = t.id)
OR EXISTS(SELECT NULL -- Get Company flagged arrangers
FROM traveller t2
JOIN company c ON t2.companyid = c.id
AND t2.accesslevelid > 1
WHERE ( (c.allowTravelArrangerDepartmentAccess = 1 AND t2.departmentid = @departmentid)
OR (c.allowTravelArrangerCostCentreAccess = 1 AND t2.costcentreid = @costcentreid))
AND t2.id = t.id))
AND t.accessLevelid > 1 -- arranger or manager
AND t.isenabled = 1
ORDER BY /*dv.preferred DESC,*/ t.lastname, t.firstname;
El uso de una subconsulta (EN, EXISTE) aliviará el problema duplicados que viene con el uso se une si hay más de un registro para niños fijado a la matriz.