【问题标题】:SQL Server Count records from parent category and all subcategoriesSQL Server 计数来自父类别和所有子类别的记录
【发布时间】:2017-05-05 11:30:42
【问题描述】:

目前我有一个存储过程,我在其中创建一个表并查询该表以获得我想要的结果,结果是一个无限分层的子/父表,允许我在基于 ASP Classic 的网页上显示数据。

这个过程是:

SET NOCOUNT ON;

DECLARE @Categories TABLE(
    CatID INT NOT NULL,
    CatName VARCHAR(200) NOT NULL,
    ParentID INT
)

INSERT INTO @Categories 
SELECT CatID, CatName, ParentID = NULL FROM Categories
WHERE CatName NOT IN (
    SELECT CatName FROM Categories c 
    INNER JOIN CategoriesRel r ON c.CatID = r.ChildID)
UNION
SELECT CatID, CatName, cr.ParentID FROM Categories 
INNER JOIN CategoriesRel cr ON cr.ChildID = Categories.CatID
ORDER BY CatID;

WITH r AS (
    SELECT CatID, CatName, ParentID, depth=0 ,Sort=CAST(CatName As VARCHAR(MAX))
    FROM @Categories WHERE ParentID IS NULL
    UNION ALL
    SELECT c.CatID, c.CatName, c.ParentID, Depth=r.Depth+1 ,Sort=r.Sort+CAST(c.CatName AS VARCHAR(200))
    FROM r INNER JOIN @Categories c ON r.CatID=c.ParentID WHERE r.Depth<32767
)

SELECT CatID, CatName=replicate('-',r.Depth*3)+r.CatName,
(SELECT COUNT(BsnID) FROM Businesses WHERE Businesses.CatID = r.CatID) AS CatCount
FROM r ORDER BY Sort OPTION(maxrecursion 32767);

这个问题是底部的计数查询。

(SELECT COUNT(BsnID) FROM Businesses WHERE Businesses.CatID = r.CatID) AS CatCount

如果我使用这段特定的代码,我只会获得针对特定类别 ID 返回的行数。例如这是当前结果:

CatID | CatName              | CatCount
______|______________________|_________
1016  | Antiques             | 1
1021  | Automotive           | 1
1024  | ---Repair            | 1
1026  | ------Engine Repair  | 1
1035  | ---Tyres             | 1
1002  | Building             | 0

我需要这样的结果:

CatID | CatName              | CatCount
______|______________________|_________
1016  | Antiques             | 1
1021  | Automotive           | 4
1024  | ---Repair            | 2
1026  | ------Engine Repair  | 1
1035  | ---Tyres             | 1
1002  | Building             | 0

任何帮助将不胜感激!谢谢

编辑:包括一些用于测试目的的 SQL

CREATE TABLE Categories(CatID int NOT NULL,CatName nvarchar(100) NOT NULL,PRIMARY KEY (CatID));

CREATE TABLE CategoriesRel(CatLinkID int NOT NULL,ParentID int NOT NULL,ChildID int NOT NULL,PRIMARY KEY (CatLinkID),FOREIGN KEY (ParentID) REFERENCES Categories(CatID),FOREIGN KEY (ChildID) REFERENCES Categories(CatID);

CREATE TABLE Businesses(BsnID int NOT NULL,BsnName nvarchar(100) NOT NULL,CatID int NOT NULL,PRIMARY KEY (BsnID),FOREIGN KEY (CatID) REFERENCES Categories(CatID);

INSERT INTO Categories VALUES ('1','Antique'),('2','Automotive'),('3','Building'),('4','Tyres'),('5','Repair'),('6','Engine Repairs');
INSERT INTO CategoriesRel VALUES ('1', '2','4'),('1','2','5'),('1','5','6');
INSERT INTO Businesses VALUES ('1','Test1','2'),('2','Test2','4'),('3','Test3','5'),('4','Test4','6');

【问题讨论】:

  • 你能做一个T-SQL过程吗?你的用例是什么?
  • 用例比较简单,用户与网页交互显示所有类别。所述网页执行程序以显示数据库中的完整类别列表...至于 T-SQL,我会,但我绝不是 SQL 专家,我不知道实现我的最佳方法需要的结果!
  • Antiques | 1Building | 0 => 错别字?
  • 它们是没有子类别的类别,因此它们在这些类别中分别有 1 个或 0 个商家
  • 你能给我们创建表+几行测试吗?

标签: sql sql-server sql-server-2016


【解决方案1】:

取出CTE的WHERE ParentID IS NULL部分,添加RootId字段。这将让您找到每个父母级别的孩子人数。

;WITH r AS (
    SELECT CatID, CatName, ParentID, CatID RootId, depth=0 ,Sort=CAST(CatName As VARCHAR(MAX))
    FROM @Categories 
    --WHERE ParentID IS NULL
    UNION ALL
    SELECT c.CatID, c.CatName, c.ParentID, RootId, Depth=r.Depth+1 ,Sort=r.Sort+CAST(c.CatName AS VARCHAR(200))
    FROM r 
    INNER JOIN @Categories c ON r.CatID=c.ParentID 
    WHERE r.Depth<32767
)

这为层次结构的每个级别提供了一行。父类别出现多次,但具有不同的 RootId。这将让我们在层次结构的每个级别上进行计数:

+-------+---------------+----------+--------+-------+-------------------------------+
| CatID |    CatName    | ParentID | RootId | depth |             Sort              |
+-------+---------------+----------+--------+-------+-------------------------------+
|  1002 | Building      | NULL     |   1002 |     0 | Building                      |
|  1016 | Antiques      | NULL     |   1016 |     0 | Antiques                      |
|  1021 | Automotive    | NULL     |   1021 |     0 | Automotive                    |
|  1024 | Repair        | 1021     |   1024 |     0 | Repair                        |
|  1026 | Engine Repair | 1024     |   1026 |     0 | Engine Repair                 |
|  1035 | Tyres         | 1021     |   1035 |     0 | Tyres                         |
|  1026 | Engine Repair | 1024     |   1024 |     1 | RepairEngine Repair           |
|  1024 | Repair        | 1021     |   1021 |     1 | AutomotiveRepair              |
|  1035 | Tyres         | 1021     |   1021 |     1 | AutomotiveTyres               |
|  1026 | Engine Repair | 1024     |   1021 |     2 | AutomotiveRepairEngine Repair |
+-------+---------------+----------+--------+-------+-------------------------------+

如果您按 RootId 分组并获得 COUNT(),它会为您提供您正在寻找的号码:

select RootId, count(b.CatId) catCount
from r 
left outer join Businesses b on r.CatID = b.CatId
group by rootid

+--------+----------+
| RootId | CatCount |
+--------+----------+
|   1002 |        0 |
|   1016 |        1 |
|   1021 |        4 |
|   1024 |        2 |
|   1026 |        1 |
|   1035 |        1 |
+--------+----------+

查询的其余部分只是获取排序和缩进的 CatName。您想为每个类别获取最深的孩子:

select r.CatId, CatName, max(depth) MaxDepth
from r
group by r.catId, CatName

最后的查询是:

SELECT y.CatID,
replicate('-',y.MaxDepth*3)+y.CatName CatName,
x.CatCount
FROM (select RootId, count(b.CatId) catCount
      from r 
      left outer join Businesses b on r.CatID = b.CatId
      group by rootid) x
join (select r.CatId, CatName, max(depth) MaxDepth
      from r
      group by r.catId, CatName) y on y.CatID = x.RootId
order by (select Sort from r where r.CatID = x.RootId and r.depth = y.MaxDepth)
OPTION(maxrecursion 32767);

【讨论】:

  • 太棒了,完美运行,你能详细解释一下它是如何工作的吗?谢谢你
  • 干得好,我也在尝试with 解决方案,但没有任何成功; +1
  • @RhysRuffell 我在答案中添加了一些细节。
  • @Blag 谢谢。前几天我回答了一个类似的问题,这有助于解决这个问题。不过,这个比较棘手。
猜你喜欢
  • 1970-01-01
  • 2013-01-24
  • 1970-01-01
  • 2011-07-30
  • 1970-01-01
  • 1970-01-01
  • 2018-04-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多