在SQL Server中选择的正确格式

时间:2018-08-30 14:01:58

标签: mysql sql-server

对于任何数据库,我都有一个简单的查询,它始终在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并不是一个简单的情况,因为对于tsassetid等重复出现错误,这意味着所有选择都需要分组或聚合函数中的任何一种……都会导致无意义和不准确的结果。

将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规则,或者这是该数据库的主要缺陷。

我该如何在两个系统上都使用它?

2 个答案:

答案 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}的一部分}表中,您可能会看到将nodezonename匹配的结果,该结果从未出现在数据的任何记录中。在Sql Server中,可以通过使用battlevel仅将一个APPLY记录与每个node匹配来轻松解决此问题。 MySql不支持此功能(自2012年以来,所有其他主要数据库中一直存在APPLY或同等功能),但是在这种情况下,您可以使用两个 JOIN进行模拟,其中第一个联接是使用GROUP BY确定值的子查询将唯一地标识所需的tagalert记录,第二次联接是到node表以实际产生该记录。不幸的是,我们需要了解有关表的更多信息,才能为您实际编写此代码。