带有聚合函数的棘手(MS)SQL查询

时间:2016-10-19 12:02:09

标签: sql sql-server

我有这三个表:

table_things:      [id]
table_location:    [id]
                   [location]
                   [quantity]
table_reservation: [id]
                   [quantity]
                   [location]
                   [list_id]

示例数据:

table_things:
id
1
2
3

table_location
id     location     quantity
1      100          10
1      101          4
2      100          1

table_reservation
id     quantity     location     list_id
1      2            100          500
1      1            100          0
2      1            100          0

它们通过[id]在所有三个表中相同,并且[location]在table_loation和table_reservation中是相同的。

table_location中的

[quantity]显示某个地方([location])有多少([数量])事物([id])。

table_reservation中的

[quantity]显示在某个地方([location])保留了多少([数量])事物([id])。

table_reservation中有0行或多行对应table_location.id = table_reservation_id,所以我可能需要使用外连接。

我想创建一个回答问题的查询:在这个特定的地方有多少东西([id])(WHERE table_location = 123),保留了多少这些东西(table_reservation。[quantity])和保留的数量有多少在table_reservation.list_id上,其中table_reservation.list_id> 0

我无法将聚合函数直接应用于答案仅包含table_location中具有给定WHERE子句的行数,同时我得到正确数量的table_reservation.quantity。

如果我这样做,我会在答案中得到正确的行数:

SELECT table_things.[id],
    table_location.[quantity],
    SUM(table_reservation.[quantity]
FROM table_location
INNER JOIN table_things ON table_location.[id] = table_things.[id]
RIGHT OUTER JOIN table_reservation ON table_things.location = table_reservation.location
WHERE table_location.location = 100
GROUP BY table_things.[id], table_location[quantity]

但该查询的问题是我(当然)得到一个不正确的SUM值(table_reservation。[quantity]),因为它总结了table_reservation中的所有相应行,并在每个行上发布相同的值结果。

第二部分是尝试获得table_reservation数量的正确值。[quantity]其list_id>我在SELECT列表中尝试了类似的东西:

(SELECT SUM(CASE WHEN table_reservation.list_id > 0 THEN table_reservation.[quantity] ELSE 0 END)) AS test

但这甚至都没有解析......我只是表现出来表达我的想法。

可能是一个简单的SQL问题,但是自从我做这些复杂的查询以来已经太久了。

3 个答案:

答案 0 :(得分:1)

前两个问题:

  

在这个特定的地方有多少东西([id])(WHERE table_location = 123),保留了多少这些东西(table_reservation。[quantity])

我认为您只需要LEFT OUTER JOIN代替RIGHT,并为table_reservation

添加其他联接谓词
SELECT  l.id,
        l.quantity,
        Reserved = SUM(ISNULL(r.quantity, 0))
FROM    table_location AS l
        INNER JOIN table_things AS t
            ON t.id = l.ID
        LEFT JOIN table_reservation r
            ON r.id = t.id
            AND r.location = l.location
WHERE   l.location = 100
GROUP BY l.id, l.quantity;

N.B我添加了ISNULL,这样当没有任何保留时,你会得到0而不是NULL的结果。您根本不需要引用table_things,但我猜这是一个简化的示例,您可能需要其他字段,因此将其保留。我还使用了别名来进行查询(在我看来)更容易阅读。

第3个问题:

  

以及有多少保留在table_reservation.list_id上的table_reservation.list_id> 0

然后您可以在CASE内使用条件聚合(SUM表达式):

SELECT  l.id,
        l.quantity,
        Reserved = SUM(r.quantity),
        ReservedWithListOver0 = SUM(CASE WHEN r.list_id > 0 THEN r.[quantity] ELSE 0 END)
FROM    table_location AS l
        INNER JOIN table_things AS t
            ON t.id = l.ID
        LEFT JOIN table_reservation r
            ON r.id = t.id
            AND r.location = l.location
WHERE   l.location = 100
GROUP BY l.id, l.quantity;

作为一些附注,除非您是出于正确的原因(因此根据执行查询的人查询不同的表),否则它是good idea to always use the schema prefix,即{{1}而不仅仅是dbo.table_reservation。使用对象类型(即table_reservation而不仅仅是dbo.table_things)为对象名称添加前缀也相当陈旧。它有些受欢迎,但是this page给出了一个很好的例子,说明为什么它可能不是最好的主意。

答案 1 :(得分:0)

您可以使用如下查询:

SELECT tt.[id],
       tl.[quantity],
       tr.[total_quantity],
       tr.[partial_quantity]              
FROM table_location AS tl
INNER JOIN table_things AS tt ON tl.[id] = tt.[id]
LEFT JOIN (
   SELECT id, location,
          SUM(quantity) AS total_quantity,
          SUM(CASE WHEN list_id > 0 THEN quantity ELSE 0 END) AS partial_quantity
   FROM table_reservation
   GROUP BY id, location
) AS tr ON tl.id = tr.id AND tl.location = tr.location
WHERE tl.location = 100

这里的诀窍是对表LEFT JOIN已聚合版本执行table_reservation,以便每id, location获得一行。派生表使用条件聚合来计算包含partial_quantity的数量的字段list_id > 0

<强>输出:

id  quantity  total_quantity  partial_quantity
-----------------------------------------------
1   10        3               2
2   1         1               0

答案 2 :(得分:0)

这是一个典型的情况,一个问题坐了几个小时无处可去,然后当你发布到stackoverflow时,你突然想出了答案。这是获取我想要的内容的查询:

SELECT table_things.[id],
    table_location.[quantity],
    SUM(table_reservation.[quantity],
    (SELECT SUM(CASE WHEN table_reservation.list_id > 0 THEN ISNULL(table_reservation.[quantity], 0) ELSE 0 END)) AS test
FROM table_location
INNER JOIN table_things ON table_location.[id] = table_things.[id]
RIGHT OUTER JOIN table_reservation ON table_things.location = table_reservation.location AND table_things.[id] = table_reservation.[id]
WHERE table_location.location = 100
GROUP BY table_things.[id], table_location[quantity]

编辑:在阅读下面的GarethD回复后,我做了他建议的更改(对我的真实代码,而不是上面的查询),这使得(真实)查询更正。