Ejecuté algunas pruebas con un bit de lógica de ejecución prolongada, con el mismo bit de código (una declaración SELECT larga) ejecutándose tanto en una función con valores de tabla como en un procedimiento almacenado, y un EXEC / SELECT directo, y cada uno se realizó de manera idéntica.
En mi opinión, siempre use una función con valores de tabla en lugar de un procedimiento almacenado para devolver un conjunto de resultados, ya que hace que la lógica sea mucho más fácil y legible en las consultas que posteriormente se unen a ellas, y le permite reutilizar la misma lógica. Para evitar un impacto excesivo en el rendimiento, a menudo uso parámetros "opcionales" (es decir, puede pasarles NULL) para permitir que la función devuelva el conjunto de resultados para que sea más rápido, por ejemplo:
CREATE FUNCTION dbo.getSitePermissions(@RegionID int, @optPersonID int, optSiteID int)
AS
RETURN
SELECT DISTINCT SiteID, PersonID
FROM dbo.SiteViewPermissions
WHERE (@optPersonID IS NULL OR @optPersonID = PersonID)
AND (@optSiteID IS NULL OR @optSiteID = SiteID)
AND @RegionID = RegionID
De esta manera, puede usar esta función para muchas situaciones diferentes y no sufrir un gran impacto en el rendimiento. Creo que esto es más eficiente que filtrar después:
SELECT * FROM dbo.getSitePermissions(@RegionID) WHERE SiteID = 1
He utilizado esta técnica en varias funciones, a veces con una larga lista de parámetros "opcionales" de este tipo.