使用 SUM 和分组依据的 SQL 联接

SQL Join with SUM and Group By

提问人:ExecChef 提问时间:4/14/2022 最后编辑:Thom AExecChef 更新时间:4/14/2022 访问量:72

问:

我有一个 MVC5 C# 应用程序。我想添加一项功能,管理员可以查看每个停车场有多少停车位,以及当前占用的停车位与可用停车位的数量。

比方说,我有两张桌子(Lots 和 Staff)。

“地段”表是现有停车场的名称,以及每个地段的车位数量:

Name | Count
A    |  200
B    |  450
C    |  375

“员工”表包含每个人的 ID,以及每个批次的 int 列。这些列为 1(员工被分配到此批次)或 0(员工未分配到此批次)

StaffID    |    LotA    |    Lot B    |    Lot C
7264       |      0     |       1     |      0
2266       |      0     |       0     |      1
3344       |      1     |       0     |      0
4444       |      0     |       1     |      0

在上述场景中,所需的输出将是 . . . .

 Lot |   Total |   Used   |  Vacant
  A  |     200 |    1     |   199
  B  |     450 |    2     |   448
  C  |     375 |    1     |   374   

过去,我只做过简单的连接,我正在努力弄清楚最好将 COUNT 与 Where 列 = 1 一起使用,还是使用 Sum。但我也会被这群人噎住,迷路了。

我尝试了以下类似的变体(没有成功)

SELECT Lots.Name,
       Lots.Count,
       SUM(Staff.LotA) As A_Occupied,
       SUM(Staff.LotB) as B_Occupied,
       SUM(Staff.LotC) as C_Occupied
FROM Lots
     CROSS JOIN Staff
GROUP BY Lots.Name

从理论上讲,我预计这将产生一个地段列表,每个地段中有多少个空间,以及每个地段中占用了多少个空间,并按地段名称分组。

这会在“分组依据”中生成错误,因为我选择的列未包含在“分组依据”中,但这只会让我更加困惑如何根据需要进行分组依据。

总的来说,我有一个坏习惯,就是把事情弄得过于复杂,所以我提前道歉。我不是一个“受过传统训练的程序员”。我尽了最大努力组织问题并使其易于理解

SQL 服务器

评论


答:

1赞 Stu 4/14/2022 #1

首先,如果对设计进行适当的规范化,您的设计会更好 - 您的表格应该有一列代表每个员工使用的批次(如果您有 100 个批次,您会有 100 列吗?这样就不需要将数据从列透视到所需的行。Staff

在当前架构中,可以使用交叉应用将列透视为行,然后可以将其与每个批次名称相关联:

select l.[name] as Lot, q.Qty as Used, l.count - q.Qty as Vacant
from lots l
cross apply (
  select Sum(LotA) A, Sum(LotB) B, Sum(LotC) C
  from Staff s 
)s
cross apply (
  select Qty from (
    select * from (values ('A', s.A),('B', s.B),('C', s.C))v(Lot, Qty)
  )x
  where x.Lot = l.[name]
)q;

观看演示小提琴

评论

0赞 ExecChef 4/15/2022
我理解你关于“规范化表”的观点。请理解,这是一个 25 年前的申请,刚刚交给我 (FML)。而且由于它不是一个“关键”应用程序,因此目前尚未获得重写授权。也就是说 - 你的决议是完美的。对不起,我花了一天时间才完成它。我想确保我也理解它,而不仅仅是使用它。非常感谢您在这里的专业知识。我永远不会自己解决这个问题。
0赞 Stu 4/15/2022
是的,我知道 - 指出设计问题总是最好的,但我完全理解并且确实期望在大多数情况下这是不容易改变的,尤其是对于遗留系统。很高兴你有一个解决方案!
0赞 Florin 4/14/2022 #2

是一个外行的解决方案。希望对你有所帮助。

        with parking  as (
            select 'A' as lot ,sum(case when lota >0 then 1 else 0 end ) as lots from staff
            union all
            select 'B' as lotb , sum(case when lotb >0 then 1 else 0 end ) as lots from staff
            union all
            select 'C' as lotc, sum(case when lotc >0 then 1 else 0 end ) as lots from staff
            )
            select a.name, a.count, b.lots, a.count-b.lots as places
            from lots a  join parking b on a.name=b.lot