SQL Server 2017 中每个级别的递归总和
Recursive Sum of each level in SQL Server 2017
我需要找出树的每个节点的递归和。请找到下面的图片。
这里蓝色的数字(假设没有销售)是 Parent 数量的总和 + 其 children 的总和,红色的数字是节点本身的销售。所以如果我们以 Node C = 44 为例,那么它的计算是这样的。
节点 C 有一个 child F 并且 F 又有两个children G 和 H。 G & H 没有 children。但他们分别取得了 5 和 6 的销售额。另外他们ParentF也靠自己11的销量,所以F的总销量是22[ 11 个是他自己的,11 个来自他的 children]。同样在同一行C,F的parent,也得到了22 sales他自己的。所以现在 C 的销售总数将是 44(他自己的 22 和 22 来自他的 child 和 grand children).
并且这种层次结构和计算将继续下去。层次结构的深度和广度也没有限制。一个节点可以有N个children.
我的数据库有 3 tables,如截图所示:
StoreMaster:此 table 包含所有商店的主列表。
StoreMaps:这是一个映射 table,它维护 parent child 层次结构。这里 MasterStoreId -> ParentId
和 SlaveStoreId -> ChildId
。 masterstoreid 和 slavestoreid 都引用 StoreMaster
table 的 storeid。此外,此地图 table 支持任何层次结构中的 N 级 children。完全没有限制。
StoreSales:StoreMaster 的交易 table。此 table 包含特定商店的销售额。
我想要实现的是,我需要一个递归查询来帮助找到我 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
请帮我解决这个问题,因为这是为了提取我们的渠道合作伙伴支出,我不能花太多时间进行研发。这是一个严重的错误。
您正在寻找这样的东西吗?
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。
编辑:
此查询将 return AmountPaid
对 StoreMaster
table 中的每个商店求和,不带 @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;
我需要找出树的每个节点的递归和。请找到下面的图片。
这里蓝色的数字(假设没有销售)是 Parent 数量的总和 + 其 children 的总和,红色的数字是节点本身的销售。所以如果我们以 Node C = 44 为例,那么它的计算是这样的。
节点 C 有一个 child F 并且 F 又有两个children G 和 H。 G & H 没有 children。但他们分别取得了 5 和 6 的销售额。另外他们ParentF也靠自己11的销量,所以F的总销量是22[ 11 个是他自己的,11 个来自他的 children]。同样在同一行C,F的parent,也得到了22 sales他自己的。所以现在 C 的销售总数将是 44(他自己的 22 和 22 来自他的 child 和 grand children).
并且这种层次结构和计算将继续下去。层次结构的深度和广度也没有限制。一个节点可以有N个children.
我的数据库有 3 tables,如截图所示:
StoreMaster:此 table 包含所有商店的主列表。
StoreMaps:这是一个映射 table,它维护 parent child 层次结构。这里
MasterStoreId -> ParentId
和SlaveStoreId -> ChildId
。 masterstoreid 和 slavestoreid 都引用StoreMaster
table 的 storeid。此外,此地图 table 支持任何层次结构中的 N 级 children。完全没有限制。StoreSales:StoreMaster 的交易 table。此 table 包含特定商店的销售额。
我想要实现的是,我需要一个递归查询来帮助找到我 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
请帮我解决这个问题,因为这是为了提取我们的渠道合作伙伴支出,我不能花太多时间进行研发。这是一个严重的错误。
您正在寻找这样的东西吗?
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。
编辑:
此查询将 return AmountPaid
对 StoreMaster
table 中的每个商店求和,不带 @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;