如何创建允许您传递参数的函数

时间:2019-07-04 12:50:03

标签: sql-server

我有一个查询,其中包含日期和国家/地区名称,我想知道如何将其创建到函数中,以便日期率(即2000-01-01至2001-12-31之间)和国家/地区名称例如“德国”可以作为变量传递。

我创建了一个查询,但没有创建函数。

SELECT * 
FROM Customers c
Join Orders o
On c.CustomerID = o.CustomerID
WHERE o.OrderDate BETWEEN '1990-01-01' AND '2017-01-01'
AND c.Country LIKE 'Germany'

是否可以为日期范围和国家/地区设置参数,以便可以在函数中传递这些参数?

2 个答案:

答案 0 :(得分:3)

您可以创建一个inline table valued function,例如

CREATE FUNCTION dbo.GetCustomerOrders(@Country VARCHAR(255), @DateFrom DATE, @DateTo DATE)
RETURNS TABLE
AS
RETURN
(   SELECT  c.CustomerID,
            o.OrderDate,
            c.Country,
            <Other Fields>
    FROM    dbo.Customers AS c
            INNER JOIN dbo.Orders AS o
                ON o.CustomerID = c.CustomerID
    WHERE   o.OrderDate >= @DateFrom
    AND     o.OrderDate <= @DateTo
    AND     c.Country = @Country
);

然后将其用作:

SELECT  <columns>
FROM    dbo.GetCustomerOrders('Germany', '19000101', '20170101') AS co;

或者,如果您需要使用另一个表作为参数源,则可以使用:

SELECT  <columns>
FROM    dbo.SomeOtherTable AS t
        CROSS APPLY dbo.GetCustomerOrders(t.Country, t.StartDate, t.EndDate) AS co;

后一种用法很好地说明了为什么要封装简单选择的原因,表值函数比存储过程更好的主意。它虽然可重用,但用途更多。随着功能的查询定义扩展到外部查询中,它们的性能也往往会更好,因此您更有可能根据用例获得最佳执行计划。

我将BETWEEN更改为>= AND <=-尽管从语义上讲这是相同的,但我发现两者之间可能会引起问题,尤其是在查询日期时间列时。进一步阅读:What do BETWEEN and the devil have in common?。如果OrderDate是日期时间列,您可能会发现以下更合适的内容:

WHERE   o.OrderDate >= @DateFrom
AND     o.OrderDate < DATEADD(DAY, 1, @DateTo)

我还明确列出了您的列(可以的话),因为您永远不要在生产代码中使用SELECT *。进一步阅读:Bad habits to kick : using SELECT * / omitting the column list。我还包括dbo.模式前缀,因为它是generally a good idea to do so(除非您有意将其保留,以便所引用的表根据登录名而有所不同,我怀疑您并非如此)

最后,由于您没有进行任何通配符或模式匹配,因此我将LIKE更改为=

如果您想要存储过程,则语法为

CREATE PROCEDURE dbo.GetCustomerOrders (@Country VARCHAR(255), @DateFrom DATE, @DateTo DATE)
AS
BEGIN
    SELECT  c.CustomerID,
            o.OrderDate,
            c.Country,
            <Other Fields>
    FROM    dbo.Customers AS c
            INNER JOIN dbo.Orders AS o
                ON o.CustomerID = c.CustomerID
    WHERE   o.OrderDate >= @DateFrom
    AND     o.OrderDate <= @DateTo
    AND     c.Country = @Country;
END

这将使用:

EXECUTE dbo.GetCustomerOrders('Germany', '19000101', '20170101');

但是如上所述,对于一个简单的选择,我不认为这提供了超过TVF的任何优势,只有劣势。

最后一点,我与SQL Blog或Aaron Bertrand没有关系,我并不是故意插入他的博客的负载,只是它们都非常相关。

答案 1 :(得分:0)

这将创建一个内联表值函数:

CREATE FUNCTION dbo.CustomerOrders (
    @FromDate DATE,
    @ToDate DATE,
    @CountryLike VARCHAR(50))
RETURNS TABLE AS 
RETURN
    SELECT
        *
    FROM
        Customers c
        inner Join Orders o On c.CustomerID = o.CustomerID
    WHERE 
        o.OrderDate BETWEEN @FromDate AND @ToDate AND 
        c.Country LIKE '%' + @CountryLike + '%'

请避免使用*,而应替换为计划使用的实际列。还要注意,我在函数内部直接添加了两个通配符,因此您不必在国家名称上写%(可以根据需要进行更改)。

您可以像以下方式使用它:

SELECT
    C.*
FROM
    dbo.CustomerOrders('2019-05-01', '2019-06-15', 'German') AS C