SQL Server 2017 中每个级别的递归总和
Posted
技术标签:
【中文标题】SQL Server 2017 中每个级别的递归总和【英文标题】:Recursive Sum of each level in SQL Server 2017 【发布时间】:2022-01-23 22:47:18 【问题描述】:我需要找出树的每个节点的递归和。请在下面找到图片。
这里蓝色的数字(假设没有销售)是 Parent Quantity + Sum of its children 的总和,红色的数字是节点本身的销售。所以如果我们以 Node C = 44 为例,那么它的计算是这样的。
节点 C 有一个孩子 F 并且 F 又有两个孩子 G 和 H。 G & H 没有孩子。但他们分别取得了 5 和 6 的销售额。此外,他们的父母 F 自己也完成了 11 次销售,所以 F 的总销售额将是 22 [他自己的 11 次,他的孩子的 11 次]。同样在同一行中,F 的父级 C 也获得了自己的 22 个销售额。所以现在 C 的总销售额将是 44(他自己的 22 和 他的孩子和孙子的 22强>)。
这种层次结构和计算将继续进行。层次结构的深度和广度也没有限制。一个节点可以有 N 个子节点。
我的数据库有 3 个表格,如下图所示:
StoreMaster:此表包含所有商店的主列表。
StoreMaps:这是一个维护父子层次结构的映射表。这里MasterStoreId -> ParentId
和SlaveStoreId -> ChildId
。 masterstoreid 和 slavestoreid 都引用了 StoreMaster
表的 storeid。此外,此映射表支持任何层次结构中的 N 级子级。完全没有限制。
StoreSales:StoreMaster 的事务表。此表包含特定商店的销售额。
我想要实现的是,我需要一个递归查询,这将有助于找到每个商店制作的 TotalAmount
和 Quantity of Sales
,如图 1 [上图]所示。
我尝试使用 CTE,但没有获得所需的输出。这是我尝试过的
;WITH ChildStores AS
(
SELECT SlaveStoreId
FROM StoreMaps
WHERE SlaveStoreId = 35
UNION ALL
SELECT t.SlaveStoreId
FROM StoreMaps t
INNER JOIN ChildStores r ON t.MasterStoreId = r.SlaveStoreId
)
INSERT INTO @StoreTable
SELECT *
FROM ChildStores
SELECT DISTINCT ss.StoreId, ss.StoreCode, ss.StoreName
FROM StoreSales ss
WHERE ss.StoreId IN (SELECT * FROM @StoreTable)
SELECT ss.StoreName, COUNT(ss.SaleId), SUM(ss.AmountPaid)
FROM StoreSales ss
WHERE ss.StoreId IN (SELECT * FROM @StoreTable)
AND ss.SaleStatus = 'Paid'
AND ss.PlanCode <> '12345'
GROUP BY ss.StoreName
第二次查询:
WITH cteAggregateCost AS
(
SELECT
i.SlaveStoreId AS rootid, i.SlaveStoreId, i.MasterStoreId
FROM
StoreMaps i
UNION ALL
SELECT
rootid, i.SlaveStoreId, i.MasterStoreId
FROM
StoreMaps i
JOIN
cteAggregateCost c ON i.MasterStoreId = c.SlaveStoreId
)
SELECT
t.StoreName, SUM(t.AmountPaid) AS AggregateCost
FROM
StoreMaps a
LEFT JOIN
cteAggregateCost i ON a.SlaveStoreId = i.rootid
LEFT JOIN
StoreSales t ON i.SlaveStoreId = t.StoreId
WHERE
t.SaleStatus = 'Paid'
AND t.PlanCode <> 12345
GROUP BY
t.StoreName
架构
CREATE TABLE StoreMaps
(
[StoreMapId] [int] IDENTITY(1,1) NOT NULL,
[MasterStoreId] [int] NULL,
[MasterStoreCode] [varchar](10) NULL,
[SlaveStoreId] [int] NULL,
[SlaveStoreCode] [varchar](10) NULL,
[IsActive] [bit] NOT NULL,
CONSTRAINT [PK_StoreMaps]
PRIMARY KEY CLUSTERED ([StoreMapId] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
CREATE TABLE StoreMaster
(
[StoreId] [int] IDENTITY(1,1) NOT NULL,
[StoreName] [varchar](150) NULL,
[StoreCode] [varchar](10) NULL,
[IsActive] [bit] NOT NULL,
CONSTRAINT [PK_StoreMaster]
PRIMARY KEY CLUSTERED ([StoreId] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
CREATE TABLE StoreSales
(
[SaleId] [int] IDENTITY(1,1) NOT NULL,
[StoreId] [int] NULL,
[StoreCode] [varchar](10) NULL,
[StoreName] [varchar](150) NULL,
[SaleStatus] [varchar](20) NULL,
[AmountPaid] [int] NULL,
[IsActive] [bit] NOT NULL,
CONSTRAINT [PK_StoreSales]
PRIMARY KEY CLUSTERED ([SaleId] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
完整查询
DECLARE @StoreCode VARCHAR(10) = NULL,
@StartDate DATE = NULL, @EndDate DATE = NULL
SET NOCOUNT ON;
DECLARE @Description VARCHAR(50),
@NetSales DECIMAL(16, 2),
@Quantity TINYINT,
@PayoutPercent TINYINT, @PayableAmount DECIMAL(16, 2),
@StoreName VARCHAR(150), @PartnerId TINYINT,
@Rowcount INT, @BillingMonth VARCHAR(10)
DECLARE @StoreTable AS TABLE (StoreId INT);
--INSERT INTO SpDebug (ObjectName, ObjectParam)
--VALUES (OBJECT_NAME(@@PROCID), @JsonParams)
IF ISNULL(TRIM(@StoreCode), '') = ''
BEGIN
SET @StoreCode = 'GW'
END
IF ISNULL(@StartDate, '') = ''
BEGIN
SET @StartDate = DATEADD(DD, - 7, GETDATE());
END
IF ISNULL(@EndDate, '') = ''
BEGIN
--Select * from @StoreTable
SET @EndDate = DATEADD(dd, - 1, GETDATE());
END
BEGIN TRY
BEGIN TRANSACTION;
WITH ChildStores
AS (
SELECT SlaveStoreId
FROM StoreMaps
WHERE SlaveStoreCode = @StoreCode
UNION ALL
SELECT t.SlaveStoreId
FROM StoreMaps t
INNER JOIN ChildStores r
ON t.MasterStoreId = r.SlaveStoreId
)
INSERT INTO @StoreTable
SELECT *
FROM ChildStores
SELECT @Rowcount = COUNT(*)
FROM @StoreTable;
WHILE (@Rowcount > 0)
BEGIN
SELECT @Rowcount = @Rowcount - 1;
SELECT @PartnerId = StoreId
FROM @StoreTable
ORDER BY StoreId DESC OFFSET @Rowcount ROWS
FETCH NEXT 1 ROWS ONLY;
IF EXISTS (
SELECT 1
FROM StoreMaster
WHERE StoreId = @PartnerId
AND IsActive = 1
AND ExcludeFromPayout = 0
)
BEGIN
SELECT @PayoutPercent = PayoutPercent, @StoreName = StoreName, @StoreCode = StoreCode
FROM StoreMaster
WHERE StoreId = @PartnerId
SET @Description = CONCAT ('Payout for ', FORMAT(@StartDate, 'dd-MMM-yyyy'), ' & ', FORMAT(@EndDate, 'dd-MMM-yyyy'));
SELECT @Quantity = COUNT(SaleId), @NetSales = SUM(AmountPaid / 1.18)
FROM StoreSales
WHERE StoreId = @PartnerId
AND (
CAST(ModifiedOn AS DATE) BETWEEN CAST(@StartDate AS DATE)
AND CAST(@EndDate AS DATE)
)
AND SaleStatus = 'Paid'
AND PlanCode <> '12345'
IF NOT EXISTS (
SELECT *
FROM PartnerInvoices
WHERE (
(
@StartDate BETWEEN PayoutStartDate
AND PayoutEndDate
)
OR (
@EndDate BETWEEN PayoutStartDate
AND PayoutEndDate
)
)
AND PartnerId = @PartnerId
)
BEGIN
SET @Quantity = ISNULL(@Quantity, 0)
SET @NetSales = ISNULL(@NetSales, 0)
SET @PayableAmount = ISNULL((@NetSales * @PayoutPercent * 0.01), 0)
SET @BillingMonth = FORMAT(GETDATE(), 'MMyyyy')
INSERT INTO PartnerInvoices (PartnerId, StoreName, PayoutStartDate, PayoutEndDate, BillingMonth, TxnRefNumber, [Description], NetSales, Quantity, PayoutPercent, PayableAmount)
VALUES (@PartnerId, @StoreName, @StartDate, @EndDate, @BillingMonth, '', @Description, @NetSales, @Quantity, @PayoutPercent, @PayableAmount)
END
--ELSE
--BEGIN
-- PRINT 'Invoice already generated for this duration.';
--END
END
END
COMMIT TRANSACTION
SELECT 1;
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
INSERT INTO AppErrors (ErrorMessage, ObjectName, [Parameters])
VALUES (ERROR_MESSAGE(), OBJECT_NAME(@@procid), @PartnerId);
THROW
END CATCH
请帮我解决这个查询,因为这是为了提取我们的渠道合作伙伴的支出,我不能花太多时间进行研发。这是一个严重的错误。
【问题讨论】:
【参考方案1】:您正在寻找类似的东西吗?
WITH CTE AS (
SELECT NULL AS MasterID, @MasterStoreID AS SlaveID
UNION ALL
SELECT M.MasterStoreId, M.SlaveStoreId AS SlaveID
FROM CTE C
INNER JOIN StoreMaps M ON M.MasterStoreId = C.SlaveID
)
SELECT @MasterStoreID AS StoreID, SUM(ISNULL(S.AmountPaid, 0)) AS AmountPaidTotal
FROM CTE C
LEFT JOIN StoreSales S ON S.StoreId = C.SlaveID;
@MasterStoreID
是您想要总销售额的商店的 ID。
编辑:
此查询将为StoreMaster
表中的每个商店返回AmountPaid
sum,不带@MasterStoreID
参数:
WITH CTE AS (
SELECT StoreID AS RootID, NULL AS MasterID, StoreID AS SlaveID
FROM StoreMaster
UNION ALL
SELECT C.RootID, M.MasterStoreid, M.SlaveStoreId AS SlaveID
FROM CTE C
INNER JOIN StoreMaps M ON M.MasterStoreid = C.SlaveID
)
SELECT C.RootID AS StoreID, SUM(ISNULL(S.AmountPaid, 0)) AS AmountPaidTotal
FROM CTE C
LEFT JOIN StoreSales S ON S.StoreId = C.SlaveID
GROUP BY C.RootID;
【讨论】:
谢谢亚当,稍微调整一下第一个查询就达到了我的预期。再次感谢。以上是关于SQL Server 2017 中每个级别的递归总和的主要内容,如果未能解决你的问题,请参考以下文章