对于任何数据库,我都有一个简单的查询,它始终在MySQL中运行,但在SQL Server中不运行
select
tagalerts.id,
ts,
assetid,
node.zonename,
battlevel
from tagalerts, node
where
ack=0 and
tagalerts.nodeid=node.id
group by assetid
order by ts desc
错误是:
列tagalerts.id在选择列表中无效,因为它既不在聚合函数中也不在group by子句中。
在tagalerts.id
子句中添加group by
并不是一个简单的情况,因为对于ts
和assetid
等重复出现错误,这意味着所有选择都需要分组或聚合函数中的任何一种……都会导致无意义和不准确的结果。
将select拆分为子查询以正确地进行排序和分组(正如您所期望的,它再次与MySQL配合使用)会使情况变得更糟
SELECT * from
(select
tagalerts.id,
ts,
assetid,
node.zonename,
battlevel
from tagalerts, node
where
ack=0 and
tagalerts.nodeid=node.id
order by ts desc
)T1
group by assetid
除非使用TOP等,否则在视图,内联函数,派生表和表达式中,order by子句无效。
“正确的输出”应为
id ts assetid zonename battlevel
1234 a datetime 1569 Reception 0
3182 another datetime 1572 Reception 0
我正在阅读完全错误的SQL Server规则,或者这是该数据库的主要缺陷。
我该如何在两个系统上都使用它?
答案 0 :(得分:2)
在大多数数据库中,如果不使用聚合函数,就不能仅包含GROUP BY
中没有的列。
MySql是一个例外。但是MS SQL Server并非如此。
因此,您可以仅使用“资产”来保留GROUP BY
。
但是,然后对所有其他列使用适当的聚合函数。
此外,为了天堂的布丁,请使用JOIN语法。
像select * from table1, table2 where table1.id2 = table2.id
这样的SQL正在使用上世纪的语法。
SELECT
MAX(node.id) AS id,
MAX(ta.ts) AS ts,
ta.assetid,
MAX(node.zonename) AS zonename,
MAX(ta.battlevel) AS battlevel
FROM tagalerts AS ta
JOIN node ON node.id = ta.nodeid
WHERE ta.ack = 0
GROUP BY ta.assetid
ORDER BY ta.ts DESC;
在MS SQL Server中使用的另一个技巧是窗口函数ROW_NUMBER。
但这可能不是您所需要的。
示例:
SELECT id, ts, assetid, zonename, battlevel
FROM
(
SELECT
node.id,
ta.ts,
ta.assetid,
node.zonename,
ta.battlevel,
ROW_NUMBER() OVER (PARTITION BY ta.assetid ORDER BY ta.ts DESC) AS rn
FROM tagalerts AS ta
JOIN node ON node.id = ta.nodeid
WHERE ta.ack = 0
) q
WHERE rn = 1
ORDER BY ts DESC;
答案 1 :(得分:1)
我强烈怀疑此查询为 WRONG ,即使在MySql 中也是如此。
我们缺少许多细节(样本数据,而且我们不知道所有列都属于哪个表),但是我知道的是您要按assetid
分组,在哪里看起来一个assetid
值在组中可以有多个ts
(时间戳)值。看起来您还指望order by ts desc
来确保两者都确保您首先在结果中看到最近的时间戳,并且每个assetid
组都首先看到使用该组的最新ts
时间戳。
MySql仅保证前者,不保证后者。 该查询中的任何内容都不保证每个assetid
使用的都是最新的时间戳。您可能会看到错误的时间戳,然后又将这些错误的时间戳用于order by
。这是Sql Server规则在那里停止的问题。 MySql违反了SQL标准,允许您编写错误的查询。
相反,您需要查看每列并将其添加到group by
(最好在所有值都相同的情况下最好) 或将其包装一个MAX()
,MIN()
,AVG()
等聚集函数,因此存在确定性结果,该结果将使用该组中的值。
如果组中某列的所有值都相同,那么将其添加到group by
不会有问题。如果值不同,则要精确选择为结果集选择哪一个。
尽管我在这里,tagalerts, node
连接语法已经过时了20多年。最好在每个表中使用别名,并在别名的每一列前添加前缀。我提到这些是为了解释为什么我在下面的代码示例中更改了它,尽管我只在确信该列属于哪个表的地方添加了前缀。
此查询应在两个数据库上运行:
SELECT ta.assetid, MAX(ta.id) "id", MAX(ta.ts) "ts",
MAX(n.zonename) "zonename", MAX(battlevel) "battlevel"
FROM tagalerts ta
INNER JOIN node n ON ta.nodeid = n.id
WHERE ack = 0
GROUP BY ta.assetid
ORDER BY ts DESC
这里还存在一个问题,即结果可能是从联接的node
表的个不同的记录中选择值。因此,如果battlevel
是{{1}的一部分}表中,您可能会看到将node
与zonename
匹配的结果,该结果从未出现在数据的任何记录中。在Sql Server中,可以通过使用battlevel
仅将一个APPLY
记录与每个node
匹配来轻松解决此问题。 MySql不支持此功能(自2012年以来,所有其他主要数据库中一直存在APPLY或同等功能),但是在这种情况下,您可以使用两个 JOIN进行模拟,其中第一个联接是使用GROUP BY确定值的子查询将唯一地标识所需的tagalert
记录,第二次联接是到node
表以实际产生该记录。不幸的是,我们需要了解有关表的更多信息,才能为您实际编写此代码。