[英]SQL Server looping through inside stored procedure
我有一個與產品相關的 C# windows 應用程序。 在我的應用程序中,我們獲取產品添加到庫存的日期。 根據產品的AddDate
,我們得到產品的年齡(以月為單位)。
假設產品的使用期限為 25 個月。
int Age = 25;
for(int i = Age; i >=0; i --)
{
var result = GetProductData(DateTime.Now.AddMonth(0-i));
}
GetProductData()
方法調用存儲過程,因此如果產品的使用期限為 25 個月,則存儲過程將被調用 25 次。
在存儲過程中,我們從DateTime
中提取月份和年份部分,並將這些位存儲到 2 個單獨的變量中。 這是目前的情況
CREATE PROCEDURE usp_GetProductData
@AppId INT,
@Date DATETIME
AS
BEGIN
DECLARE @Month INT
DECLARE @Year INT
DECLARE @ProductInstall INT
SELECT @Month = SELECT DATEPART(m, @Date)
SELECT @Year = SELECT YEAR(@Date)
SELECT @ProductInstall = (SUM(P.[AutoInstalls]) + SUM(P.[ITInstalls]))
FROM dbo.[ProductInstalls] P
INNER JOIN [User] U ON U.[UserId] = P.[UserId]
WHERE LicenseRequired = 1
AND DATEPART(m, P.[InstallDate]) = @month
AND DATEPART(year, P.[InstallDate]) = @Year
SELECT
AVG(A.[TotalRequests] - A.[TotalInstalls]) * 100 AS [ProductAverage],
@ProductInstall, @Month/@Year
FROM
dbo.[ApplicationInstalls]
/*There are few more joins and some business logic after this */
WHERE
DATEPART(m, A.[InstallDate]) = @Month
AND DATEPART(year, A.[InstallDate]) = @Year
END
現在,我不想像產品/應用程序的年齡一樣多次調用存儲過程,而是想在一個請求中執行它,因為我已經知道應用程序/產品添加到庫存中的日期
DECLARE @ProductAddDate
DECLARE @ProductAge
SELECT @ProductAddDate = [DateAdded] FROM dbo.[Application] WHERE [AppId] = @AppId
SELECT @ProductAge = DATEDIFF(DAY, @ProductAddDate, GETDATE())/30
現在隨着我擁有的產品年齡,我想每個月循環通過以下邏輯。
SELECT
@ProductInstall = (SUM(P.[AutoInstalls]) + SUM(P.[ITInstalls]))
FROM
dbo.[ProductInstalls] P
INNER JOIN
[User] U ON U.[UserId] = P.[UserId]
WHERE
LicenseRequired = 1
AND DATEPART(m, P.[InstallDate]) = @month
AND DATEPART(year, P.[InstallDate]) = @Year
SELECT
AVG(A.[TotalRequests] - A.[TotalInstalls]) * 100 AS [ProductAverage], @ProductInstall,
@Month/@Year
FROM
dbo.[ApplicationInstalls]
/*There are few more joins and some business logic after this */
WHERE
DATEPART(m, A.[InstallDate]) = @Month
AND DATEPART(year, A.[InstallDate]) = @Year
不確定您的核心邏輯是否真的代表“應用程序”數據或產品數據,但我會將 proc 名稱保留為“getProductData”。 這是它的作用:
這是代碼:
create procedure getProductData
@AppId int
as
declare @ProductAddDate date = (
select dateAdded
from [application]
where appId = @AppId
);
with
monthYears as (
select mo = datepart(m, @productAddDate),
yr = datepart(year, @productAddDate),
i = 0
union all
select mo = datepart(m, dateAdd(m, i+1, @productAddDate)),
yr = datepart(year, dateAdd(m, i+1, @productAddDate)),
i = i+1
from monthYears
where datepart(m, dateAdd(m, i+1, @productAddDate)) <= datepart(m, getdate())
and datepart(year, dateAdd(m, i+1, @productAddDate)) <= datepart(year, getdate())
),
productInstalls as (
select mo = datepart(m, p.installDate),
yr = datepart(year, p.installDate),
ProductInstall = sum(p.autoinstalls) + sum(p.itinstalls)
from productInstalls p
join [user] u on u.userId = p.userId
where licenseRequired = 1
group by datepart(m, p.installDate),
datepart(year, p.installDate)
),
applicationInstalls as (
select mo = datepart(m, a.installDate),
yr = datepart(year, a.installDate),
ProductAverage = avg(a.totalRequests - a.totalInstalls) * 100
from applicationInstalls a
group by datepart(m, p.installDate),
datepart(year, p.InstallDate)
)
select my.yr,
my.mo,
ProductAverage = isnull(a.ProductAverage, 0),
ProductInstall = isnull(p.ProductInstall, 0)
from monthYears my
left join applicationInstalls a on my.yr = a.yr and my.mo = a.mo
left join productInstalls p on my.yr = p.yr and my.mo = p.mo;
當你在 C# 中得到這個時,它將作為一個集合。 最有可能的是 output 作為 DataTable 或者您的業務層可能是 output 它作為 IEnumerable 或某種類型的列表。 因此,如有必要,您的循環將發生在 C# 代碼中,而不是 SQL 代碼中。
就像是:
int appId = 0; // or whatever
foreach(var productDatum in GetProductData(appId)) {
// do something with productDatum
}
我本身沒有“答案”,但我確實有一個可能值得深入研究的想法。 在 SQL 服務器中,有 Window 函數的概念。 幾年前,Itsak Ben-Gan 寫了一本書,但是你可以通過谷歌搜索找到它——這里有一個網站。
這里的缺點是你必須開始以不同的方式思考才能以這種方式編程。 在適用的情況下,專業人士沒有遞歸。 window 在單次掃描中向下流過表,在它向下時收集聚合。 幾年前,我用它來確定大型應用程序中的調度沖突,將返回結果的時間從 10 - 20 秒減少到約 0.5 秒。 您可能不需要這種性能提升,但如果您能弄清楚如何應用到您的問題,您就可以避免大量的表(或索引)掃描。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.