T-SQL:从循环返回多个查询的函数

时间:2017-05-22 01:06:41

标签: sql-server function tsql while-loop

我正在尝试创建一个函数,该函数接收一个句点的开始日期和日期结束作为参数,并生成一个列表,其中列出了该期间每个月销售的前5个产品。

例如:

CREATE FUNCTION top5Sales(@CurrentDate DATETIME, @EndDate DATETIME)
RETURNS TABLE
RETURN
(
    WHILE(@CurrentDate < @EndDate)
        BEGIN
            SELECT TOP 5
                AdventureWorks.Sales.SalesOrderDetail.ProductID,
                AdventureWorks.Production.Product.Name,
                MONTH(AdventureWorks.Sales.SalesOrderHeader.OrderDate) as 'Month',
                YEAR(AdventureWorks.Sales.SalesOrderHeader.OrderDate) as 'Year',
                SUM(AdventureWorks.Sales.SalesOrderDetail.OrderQty) as 'Total Quantity Sold',
                AVG(AdventureWorks.Sales.SalesOrderDetail.UnitPrice) as 'Average Unit Price',
                SUM(AdventureWorks.Sales.SalesOrderDetail.UnitPriceDiscount) as 'Total Discount',
                SUM(AdventureWorks.Sales.SalesOrderDetail.LineTotal) as 'Total Value Sold'
            FROM
                AdventureWorks.Sales.SalesOrderDetail
            INNER JOIN
                AdventureWorks.Sales.SalesOrderHeader
            ON
                AdventureWorks.Sales.SalesOrderHeader.SalesOrderID = AdventureWorks.Sales.SalesOrderDetail.SalesOrderID
            INNER JOIN
                AdventureWorks.Production.Product
            ON
                AdventureWorks.Production.Product.ProductID = AdventureWorks.Sales.SalesOrderDetail.ProductID
            WHERE
                MONTH(AdventureWorks.Sales.SalesOrderHeader.OrderDate) = month(@CurrentDate) 
                and
                YEAR(AdventureWorks.Sales.SalesOrderHeader.OrderDate) = year(@CurrentDate)
            GROUP BY
                    AdventureWorks.Production.Product.Name,
                    AdventureWorks.Sales.SalesOrderDetail.ProductID,
                    MONTH(AdventureWorks.Sales.SalesOrderHeader.OrderDate),
                    YEAR(AdventureWorks.Sales.SalesOrderHeader.OrderDate)
            ORDER BY
                [Total Quantity Sold] DESC
            SET
                @CurrentDate = DATEADD(MONTH, 1, @CurrentDate)
        END
)

实际上,上面的代码是错误的。它仅用于举例说明问题,并提供一些可能的解决方案。

您不必遵循这一思路。如果还有另一个更有趣的解决方案,请随意公开它。

2 个答案:

答案 0 :(得分:0)

您可以使用row_number和前5个关系,并使用单个查询返回此结果,如下所示:您能检查一下吗?

CREATE FUNCTION top5Sales(@CurrentDate DATETIME, @EndDate DATETIME)
RETURNS TABLE
RETURN
(        
    SELECT Top 5 with ties * from ( 
         SELECT 
                AdventureWorks.Sales.SalesOrderDetail.ProductID,
                AdventureWorks.Production.Product.Name,
                MONTH(AdventureWorks.Sales.SalesOrderHeader.OrderDate) as 'Month',
                YEAR(AdventureWorks.Sales.SalesOrderHeader.OrderDate) as 'Year',
                SUM(AdventureWorks.Sales.SalesOrderDetail.OrderQty) as 'Total Quantity Sold',
                AVG(AdventureWorks.Sales.SalesOrderDetail.UnitPrice) as 'Average Unit Price',
                SUM(AdventureWorks.Sales.SalesOrderDetail.UnitPriceDiscount) as 'Total Discount',
                SUM(AdventureWorks.Sales.SalesOrderDetail.LineTotal) as 'Total Value Sold'
            FROM
                AdventureWorks.Sales.SalesOrderDetail
            INNER JOIN
                AdventureWorks.Sales.SalesOrderHeader
            ON
                AdventureWorks.Sales.SalesOrderHeader.SalesOrderID = AdventureWorks.Sales.SalesOrderDetail.SalesOrderID
            INNER JOIN
                AdventureWorks.Production.Product
            ON
                AdventureWorks.Production.Product.ProductID = AdventureWorks.Sales.SalesOrderDetail.ProductID
            WHERE
                AdventureWorks.Sales.SalesOrderHeader.OrderDate > @CurrentDate
                AND AdventureWorks.Sales.SalesOrderHeader.OrderDate <= @EndDate
            GROUP BY
                    AdventureWorks.Production.Product.Name,
                    AdventureWorks.Sales.SalesOrderDetail.ProductID,
                    MONTH(AdventureWorks.Sales.SalesOrderHeader.OrderDate),
                    YEAR(AdventureWorks.Sales.SalesOrderHeader.OrderDate)
        --  ORDER BY
        --      [Total Quantity Sold] DESC
        ) a 
        Order by Row_Number() over(Partition by [ProductId], [Name], [Year], [Month] order by [Total Quantity Sold] Desc
 )

答案 1 :(得分:-1)

我查看了AdventureWorks数据库,但我没有看到日期表。有关基本概述,您可以查看此视频 - https://www.brentozar.com/training/t-sql-level/3-number-date-tables-10m/

以下逻辑不会出现在内联表函数中,但它是一种选择您正在寻找的数据的快速而简单的方法。

DECLARE @sql NVARCHAR(Max), 
        @CurrentDate DATETIME = '2005-06-01 00:00:00.000', 
        @EndDate DATETIME = '2008-08-31 00:00:00.000';

SELECT @sql = COALESCE( @sql + CHAR(13) + 'UNION' + CHAR(13), '') + 
              ' SELECT TOP 5 ' + CONVERT(NVARCHAR(8), [Year]) + ' AS [Year], ' 
                               + CONVERT(NVARCHAR(8), [Month]) + ' AS [Month],
                               sod.ProductId, 
                               SUM(sod.OrderQty) AS Sales
                FROM Sales.SalesOrderDetail sod
                JOIN Sales.SalesOrderHeader soh
                ON sod.SalesOrderId = soh.SalesOrderId
                    AND YEAR(soh.OrderDate) = ' + CONVERT(NVARCHAR(8), [Year]) + '
                    AND MONTH(soh.OrderDate) = ' + CONVERT(NVARCHAR(8), [Month]) + '
                GROUP BY sod.ProductId'
FROM dbo.[Date]
WHERE [Date] BETWEEN @CurrentDate AND @EndDate

EXEC sp_executesql @sql

如果您执行此T-SQL,您会发现它相对于获取相同数据的其他方法而言非常快且可扩展。